1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * libdvbsub - DVB subtitle decoding
4 * Copyright (C) Mart Raudsepp 2009 <mart.raudsepp@artecdesign.ee>
6 * Heavily uses code algorithms ported from ffmpeg's libavcodec/dvbsubdec.c,
7 * especially the segment parsers. The original license applies to this
8 * ported code and the whole code in this file as well.
10 * Original copyright information follows:
13 * DVB subtitle decoding for ffmpeg
14 * Copyright (c) 2005 Ian Caulfield
16 * This file is part of FFmpeg.
18 * FFmpeg is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU Lesser General Public
20 * License as published by the Free Software Foundation; either
21 * version 2.1 of the License, or (at your option) any later version.
23 * FFmpeg is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * Lesser General Public License for more details.
28 * You should have received a copy of the GNU Lesser General Public
29 * License along with FFmpeg; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
34 #include <string.h> /* memset */
35 #include <gst/gstutils.h> /* GST_READ_UINT16_BE */
36 #include <gst/base/gstbitreader.h> /* GstBitReader */
37 #include "ffmpeg-colorspace.h" /* YUV_TO_RGB1_CCIR */ /* FIXME: Just give YUV data to gstreamer then? */
39 /* FIXME: Convert to GST_LOG and clean up */
40 void (*g_log_callback) (GLogLevelFlags log_level, const gchar * format,
41 va_list args, gpointer user_data) = NULL;
42 gpointer g_log_callback_user_data = NULL;
46 #define dvb_log(log_type, log_level, format...) real_dvb_log(log_type, log_level, ## format)
49 /* dvb_log types // DVB_LOG environment variable string */
50 DVB_LOG_GENERAL, /* GENERAL */
51 DVB_LOG_PAGE, /* PAGE */
52 DVB_LOG_REGION, /* REGION */
53 DVB_LOG_CLUT, /* CLUT */
54 DVB_LOG_OBJECT, /* OBJECT */
55 DVB_LOG_PIXEL, /* PIXEL */
56 DVB_LOG_RUNLEN, /* RUNLEN */
57 DVB_LOG_DISPLAY, /* DISPLAY */
58 DVB_LOG_STREAM, /* STREAM - issues in the encoded stream (TV service provider encoder problem) */
59 DVB_LOG_PACKET, /* PACKET - messages during raw demuxer data packet handling */
60 DVB_LOG_LAST /* sentinel use only */
64 real_dvb_log (const gint log_type, GLogLevelFlags log_level,
65 const gchar * format, ...)
69 va_start (va, format);
72 g_log_callback (log_level, format, va, g_log_callback_user_data);
82 #define dvb_log(log_type, log_level, format...)
85 /* FIXME: Are we waiting for an acquisition point before trying to do things? */
86 /* FIXME: In the end convert some of the guint8/16 (especially stack variables) back to gint for access efficiency */
90 * @short_description: a DVB subtitle parsing class
91 * @stability: Unstable
93 * The #DvbSub represents an object used for parsing a DVB subpicture,
94 * and signalling the API user for new bitmaps to show on screen.
97 #define MAX_NEG_CROP 1024
98 static guint8 ff_cropTbl[256 + 2 * MAX_NEG_CROP] = { 0, };
100 #define cm (ff_cropTbl + MAX_NEG_CROP)
102 /* FIXME: This is really ARGB... We might need this configurable for performant
103 * FIXME: use in GStreamer as well if that likes RGBA more (Qt prefers ARGB) */
104 #define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
106 typedef struct DVBSubCLUT
108 int id; /* default_clut uses -1 for this, so guint8 isn't fine without adaptations first */
112 guint32 clut256[256];
114 struct DVBSubCLUT *next;
117 static DVBSubCLUT default_clut;
119 typedef struct DVBSubObjectDisplay
121 /* FIXME: Use more correct sizes */
131 /* FIXME: Should we use GSList? The relating interaction and pointer assigment is quite complex and perhaps unsuited for a plain GSList anyway */
132 struct DVBSubObjectDisplay *region_list_next;
133 struct DVBSubObjectDisplay *object_list_next;
134 } DVBSubObjectDisplay;
136 typedef struct DVBSubObject
138 /* FIXME: Use more correct sizes */
139 int id; /* FIXME: Use guint8 after checking it's fine in all code using it */
143 /* FIXME: Should we use GSList? */
144 DVBSubObjectDisplay *display_list;
145 struct DVBSubObject *next;
148 typedef struct DVBSubRegionDisplay
149 { /* FIXME: Figure out if this structure is only used temporarily in page_segment parser, or also more */
155 struct DVBSubRegionDisplay *next;
156 } DVBSubRegionDisplay;
158 typedef struct DVBSubRegion
163 guint8 depth; /* If we want to make this a guint8, then need to ensure it isn't wrap around with reserved values in region handling code */
168 /* FIXME: Validate these fields existence and exact types */
172 DVBSubObjectDisplay *display_list;
174 struct DVBSubRegion *next;
177 typedef struct _DvbSubPrivate DvbSubPrivate;
178 struct _DvbSubPrivate
181 DvbSubCallbacks callbacks;
184 guint8 page_time_out;
185 DVBSubRegion *region_list;
186 DVBSubCLUT *clut_list;
187 DVBSubObject *object_list;
189 int display_list_size;
190 DVBSubRegionDisplay *display_list;
194 #define DVB_SUB_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), DVB_TYPE_SUB, DvbSubPrivate))
196 G_DEFINE_TYPE (DvbSub, dvb_sub, G_TYPE_OBJECT);
202 } DvbSubPixelDataSubBlockFieldType;
204 /* FIXME: It might make sense to pass DvbSubPrivate for all the get_* functions, instead of public DvbSub */
205 static DVBSubObject *
206 get_object (DvbSub * dvb_sub, guint16 object_id)
208 const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
209 DVBSubObject *ptr = priv->object_list;
211 while (ptr && ptr->id != object_id) {
219 get_clut (DvbSub * dvb_sub, gint clut_id)
221 const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
222 DVBSubCLUT *ptr = priv->clut_list;
224 while (ptr && ptr->id != clut_id) {
231 // FIXME: Just pass private_data pointer directly here and in other get_* helper functions?
232 static DVBSubRegion *
233 get_region (DvbSub * dvb_sub, guint8 region_id)
235 const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
236 DVBSubRegion *ptr = priv->region_list;
238 while (ptr && ptr->id != region_id) {
246 delete_region_display_list (DvbSub * dvb_sub, DVBSubRegion * region)
248 const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
249 DVBSubObject *object, *obj2;
250 DVBSubObject **obj2_ptr;
251 DVBSubObjectDisplay *display, *obj_disp, **obj_disp_ptr;
253 while (region->display_list) {
254 display = region->display_list;
256 object = get_object (dvb_sub, display->object_id);
259 obj_disp_ptr = &object->display_list;
260 obj_disp = *obj_disp_ptr;
262 while (obj_disp && obj_disp != display) {
263 obj_disp_ptr = &obj_disp->object_list_next;
264 obj_disp = *obj_disp_ptr;
268 *obj_disp_ptr = obj_disp->object_list_next;
270 if (!object->display_list) {
271 obj2_ptr = (DVBSubObject **) & priv->object_list; /* FIXME: Evil casting */
274 while (obj2 != object) {
276 obj2_ptr = &obj2->next;
280 *obj2_ptr = obj2->next;
282 g_slice_free (DVBSubObject, obj2);
287 region->display_list = display->region_list_next;
289 g_slice_free (DVBSubObjectDisplay, display);
294 delete_state (DvbSub * dvb_sub)
296 DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
297 DVBSubRegion *region;
299 while (priv->region_list) {
300 region = priv->region_list;
302 priv->region_list = region->next;
304 delete_region_display_list (dvb_sub, region);
306 g_free (region->pbuf);
308 g_slice_free (DVBSubRegion, region);
311 g_slice_free_chain (DVBSubCLUT, priv->clut_list, next);
312 priv->clut_list = NULL;
314 /* Should already be null */
315 if (priv->object_list)
316 g_warning ("Memory deallocation error!");
320 dvb_sub_init (DvbSub * self)
324 self->private_data = priv = DVB_SUB_GET_PRIVATE (self);
326 /* TODO: Add initialization code here */
327 /* FIXME: Do we have a reason to initiate the members to zero, or are we guaranteed that anyway? */
328 priv->region_list = NULL;
329 priv->object_list = NULL;
330 priv->page_time_out = 0; /* FIXME: Maybe 255 instead? */
331 priv->pes_buffer = g_string_new (NULL);
335 dvb_sub_finalize (GObject * object)
337 DvbSub *self = DVB_SUB (object);
338 DvbSubPrivate *priv = (DvbSubPrivate *) self->private_data;
339 /* TODO: Add deinitalization code here */
340 /* FIXME: Clear up region_list contents */
341 delete_state (self); /* close_pid should have called this, but lets be sure */
342 g_string_free (priv->pes_buffer, TRUE);
344 G_OBJECT_CLASS (dvb_sub_parent_class)->finalize (object);
347 /* init static data necessary for ffmpeg-colorspace conversion */
349 dsputil_static_init (void)
353 for (i = 0; i < 256; i++)
354 ff_cropTbl[i + MAX_NEG_CROP] = i;
355 for (i = 0; i < MAX_NEG_CROP; i++) {
357 ff_cropTbl[i + MAX_NEG_CROP + 256] = 255;
362 dvb_sub_class_init (DvbSubClass * klass)
364 int i, r, g, b, a = 0;
365 GObjectClass *object_class = (GObjectClass *) klass;
367 object_class->finalize = dvb_sub_finalize;
369 g_type_class_add_private (klass, sizeof (DvbSubPrivate));
371 dsputil_static_init (); /* Initializes ff_cropTbl table, used in YUV_TO_RGB conversion */
373 /* Initialize the static default_clut structure, from which other clut
374 * structures are initialized from (to start off with default CLUTs
375 * as defined in the specification). */
376 default_clut.id = -1;
378 default_clut.clut4[0] = RGBA (0, 0, 0, 0);
379 default_clut.clut4[1] = RGBA (255, 255, 255, 255);
380 default_clut.clut4[2] = RGBA (0, 0, 0, 255);
381 default_clut.clut4[3] = RGBA (127, 127, 127, 255);
383 default_clut.clut16[0] = RGBA (0, 0, 0, 0);
384 for (i = 1; i < 16; i++) {
386 r = (i & 1) ? 255 : 0;
387 g = (i & 2) ? 255 : 0;
388 b = (i & 4) ? 255 : 0;
390 r = (i & 1) ? 127 : 0;
391 g = (i & 2) ? 127 : 0;
392 b = (i & 4) ? 127 : 0;
394 default_clut.clut16[i] = RGBA (r, g, b, 255);
397 default_clut.clut256[0] = RGBA (0, 0, 0, 0);
398 for (i = 1; i < 256; i++) {
400 r = (i & 1) ? 255 : 0;
401 g = (i & 2) ? 255 : 0;
402 b = (i & 4) ? 255 : 0;
407 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
408 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
409 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
413 r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
414 g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
415 b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
419 r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
420 g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
421 b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
425 r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
426 g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
427 b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
432 default_clut.clut256[i] = RGBA (r, g, b, a);
437 _dvb_sub_parse_page_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
439 { /* FIXME: Use guint for buf_size here and in many other places? */
440 DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
441 DVBSubRegionDisplay *display;
442 DVBSubRegionDisplay *tmp_display_list, **tmp_ptr;
444 const guint8 *buf_end = buf + buf_size;
449 static int counter = 0;
450 static const gchar *page_state_str[] = {
461 priv->page_time_out = *buf++;
462 page_state = ((*buf++) >> 2) & 3;
466 dvb_log (DVB_LOG_PAGE, G_LOG_LEVEL_DEBUG,
467 "%d: page_id = %u, length = %d, page_time_out = %u seconds, page_state = %s",
468 counter, page_id, buf_size, priv->page_time_out,
469 page_state_str[page_state]);
472 if (page_state == 2) { /* Mode change */
473 delete_state (dvb_sub);
476 tmp_display_list = priv->display_list;
477 priv->display_list = NULL;
478 priv->display_list_size = 0;
480 while (buf + 5 < buf_end) {
484 display = tmp_display_list;
485 tmp_ptr = &tmp_display_list;
487 while (display && display->region_id != region_id) {
488 tmp_ptr = &display->next;
489 display = display->next;
493 display = g_slice_new0 (DVBSubRegionDisplay);
495 display->region_id = region_id;
497 display->x_pos = GST_READ_UINT16_BE (buf);
499 display->y_pos = GST_READ_UINT16_BE (buf);
502 *tmp_ptr = display->next;
504 display->next = priv->display_list;
505 priv->display_list = display;
506 priv->display_list_size++;
508 dvb_log (DVB_LOG_PAGE, G_LOG_LEVEL_DEBUG,
509 "%d: REGION information: ID = %u, address = %ux%u",
510 counter, region_id, display->x_pos, display->y_pos);
513 while (tmp_display_list) {
514 display = tmp_display_list;
516 tmp_display_list = display->next;
518 g_slice_free (DVBSubRegionDisplay, display);
523 _dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
526 DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
528 const guint8 *buf_end = buf + buf_size;
531 DVBSubRegion *region;
532 DVBSubObject *object;
533 DVBSubObjectDisplay *object_display;
541 region = get_region (dvb_sub, region_id);
543 if (!region) { /* Create a new region */
544 region = g_slice_new0 (DVBSubRegion);
545 region->id = region_id;
546 region->next = priv->region_list;
547 priv->region_list = region;
550 fill = ((*buf++) >> 3) & 1;
552 region->width = GST_READ_UINT16_BE (buf);
554 region->height = GST_READ_UINT16_BE (buf);
557 if (region->width * region->height != region->buf_size) { /* FIXME: Read closer from spec what happens when dimensions change */
559 g_free (region->pbuf);
561 region->buf_size = region->width * region->height;
563 region->pbuf = g_malloc (region->buf_size); /* TODO: We can probably use GSlice here if careful about freeing while buf_size still records the correct size */
565 fill = 1; /* FIXME: Validate from spec that fill is forced on (in the following codes context) when dimensions change */
568 region->depth = 1 << (((*buf++) >> 2) & 7);
569 if (region->depth < 2 || region->depth > 8) {
570 g_warning ("region depth %d is invalid\n", region->depth);
571 region->depth = 4; /* FIXME: Check from spec this is the default? */
574 region->clut = *buf++;
576 if (region->depth == 8)
577 region->bgcolor = *buf++;
581 if (region->depth == 4)
582 region->bgcolor = (((*buf++) >> 4) & 15);
584 region->bgcolor = (((*buf++) >> 2) & 3);
587 dvb_log (DVB_LOG_REGION, G_LOG_LEVEL_DEBUG,
588 "id = %u, (%ux%u)@%u-bit",
589 region_id, region->width, region->height, region->depth);
592 memset (region->pbuf, region->bgcolor, region->buf_size);
593 dvb_log (DVB_LOG_REGION, G_LOG_LEVEL_DEBUG,
594 "Filling region (%u) with bgcolor = %u", region->id, region->bgcolor);
597 delete_region_display_list (dvb_sub, region); /* Delete the region display list for current region - FIXME: why? */
599 while (buf + 6 <= buf_end) {
600 object_id = GST_READ_UINT16_BE (buf);
603 object = get_object (dvb_sub, object_id);
606 object = g_slice_new0 (DVBSubObject);
608 object->id = object_id;
610 object->next = priv->object_list;
611 priv->object_list = object;
614 object->type = (*buf) >> 6;
616 object_display = g_slice_new0 (DVBSubObjectDisplay);
618 object_display->object_id = object_id;
619 object_display->region_id = region_id;
621 object_display->x_pos = GST_READ_UINT16_BE (buf) & 0xfff;
623 object_display->y_pos = GST_READ_UINT16_BE (buf) & 0xfff;
626 if ((object->type == 1 || object->type == 2) && buf + 2 <= buf_end) {
627 object_display->fgcolor = *buf++;
628 object_display->bgcolor = *buf++;
631 object_display->region_list_next = region->display_list;
632 region->display_list = object_display;
634 object_display->object_list_next = object->display_list;
635 object->display_list = object_display;
637 dvb_log (DVB_LOG_REGION, G_LOG_LEVEL_DEBUG,
638 "REGION DATA: object_id = %u, region_id = %u, pos = %ux%u, obj_type = %u",
639 object->id, region->id, object_display->x_pos, object_display->y_pos,
641 if (object->type == 1 || object->type == 2)
642 dvb_log (DVB_LOG_REGION, G_LOG_LEVEL_DEBUG,
643 "REGION DATA: fgcolor = %u, bgcolor = %u\n", object_display->fgcolor,
644 object_display->bgcolor);
649 _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
652 DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
654 const guint8 *buf_end = buf + buf_size;
657 int entry_id, depth, full_range;
658 int y, cr, cb, alpha;
659 int r, g, b, r_add, g_add, b_add;
661 #ifdef DEBUG_PACKET_CONTENTS
662 g_print ("DVB clut packet:\n");
663 gst_util_dump_mem (buf, buf_size);
669 clut = get_clut (dvb_sub, clut_id);
672 clut = g_slice_new (DVBSubCLUT); /* FIXME-MEMORY-LEAK: This seems to leak per valgrind */
674 memcpy (clut, &default_clut, sizeof (DVBSubCLUT));
678 clut->next = priv->clut_list;
679 priv->clut_list = clut;
682 while (buf + 4 < buf_end) {
685 depth = (*buf) & 0xe0;
688 g_warning ("Invalid clut depth 0x%x!", *buf);
692 full_range = (*buf++) & 1;
701 cr = (((buf[0] & 3) << 2) | ((buf[1] >> 6) & 3)) << 4;
702 cb = (buf[1] << 2) & 0xf0;
703 alpha = (buf[1] << 6) & 0xc0;
711 YUV_TO_RGB1_CCIR (cb, cr);
712 YUV_TO_RGB2_CCIR (r, g, b, y);
714 dvb_log (DVB_LOG_CLUT, G_LOG_LEVEL_DEBUG,
715 "CLUT DEFINITION: clut %d := (%d,%d,%d,%d)", entry_id, r, g, b, alpha);
718 clut->clut4[entry_id] = RGBA (r, g, b, 255 - alpha);
720 clut->clut16[entry_id] = RGBA (r, g, b, 255 - alpha);
722 clut->clut256[entry_id] = RGBA (r, g, b, 255 - alpha);
726 // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same
727 // FFMPEG-FIXME: refactoring as done here
729 _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len,
730 const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
732 GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
733 /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
735 gboolean stop_parsing = FALSE;
737 guint32 pixels_read = 0;
739 static gboolean warning_shown = FALSE;
740 if (!warning_shown) {
742 ("Parsing 2bit color DVB sub-picture. This is not tested at all. If you see this message, "
743 "please provide the developers with sample media with these subtitles, if possible.");
744 warning_shown = TRUE;
746 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
747 "(n=2): Inside %s with dbuf_len = %d", __PRETTY_FUNCTION__, dbuf_len);
749 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
750 guint run_length = 0, clut_index = 0;
751 gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
753 if (bits) { /* 2-bit_pixel-code */
756 } else { /* 2-bit_zero */
757 gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
758 if (bits == 1) { /* switch_1 == '1' */
759 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3);
761 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
762 } else { /* switch_1 == '0' */
763 gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
764 if (bits == 1) { /* switch_2 == '1' */
765 run_length = 1; /* 1x pseudo-colour '00' */
766 } else { /* switch_2 == '0' */
767 gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
768 switch (bits) { /* switch_3 */
769 case 0x0: /* end of 2-bit/pixel_code_string */
772 case 0x1: /* two pixels shall be set to pseudo colour (entry) '00' */
775 case 0x2: /* the following 6 bits contain run length coded pixel data */
776 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4);
778 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
780 case 0x3: /* the following 10 bits contain run length coded pixel data */
781 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8);
783 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
790 /* If run_length is zero, continue. Only case happening is when
791 * stop_parsing is TRUE too, so next cycle shouldn't run */
795 /* Trim the run_length to not go beyond the line end and consume
796 * it from remaining length of dest line */
797 run_length = MIN (run_length, dbuf_len);
798 dbuf_len -= run_length;
800 /* Make clut_index refer to the index into the desired bit depths
801 * CLUT definition table */
803 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
805 /* Now we can simply memset run_length count of destination bytes
806 * to clut_index, but only if not non_modifying */
807 dvb_log (DVB_LOG_RUNLEN, G_LOG_LEVEL_DEBUG,
808 "Setting %u pixels to color 0x%x in destination buffer; dbuf_len left is %d pixels",
809 run_length, clut_index, dbuf_len);
810 if (!(non_mod == 1 && bits == 1))
811 memset (destbuf, clut_index, run_length);
813 destbuf += run_length;
814 pixels_read += run_length;
817 // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
818 //gst_bit_reader_skip_to_byte (&gb);
819 *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
821 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
822 "Returning from 2bit_string parser with %u pixels read", pixels_read);
823 // FIXME: Shouldn't need this variable if tracking things in the loop better
827 // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same
828 // FFMPEG-FIXME: refactoring as done here, explained in commit 895296c3
830 _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len,
831 const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
833 GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
834 /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
835 gboolean stop_parsing = FALSE;
837 guint32 pixels_read = 0;
839 dvb_log (DVB_LOG_RUNLEN, G_LOG_LEVEL_DEBUG,
840 "Entering 4bit_string parser at srcbuf position %p with buf_size = %d; destination buffer size is %d @ %p",
841 *srcbuf, buf_size, dbuf_len, destbuf);
843 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
844 guint run_length = 0, clut_index = 0;
845 gst_bit_reader_get_bits_uint32 (&gb, &bits, 4);
851 gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
852 if (bits == 0) { /* switch_1 == '0' */
853 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3);
859 } else { /* switch_1 == '1' */
860 gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
861 if (bits == 0) { /* switch_2 == '0' */
862 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 2);
864 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
865 } else { /* switch_2 == '1' */
866 gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
868 case 0x0: /* switch_3 == '00' */
869 run_length = 1; /* 1 pixel of pseudo-color 0 */
871 case 0x1: /* switch_3 == '01' */
872 run_length = 2; /* 2 pixels of pseudo-color 0 */
874 case 0x2: /* switch_3 == '10' */
875 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4);
877 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
879 case 0x3: /* switch_3 == '11' */
880 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8);
882 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
889 /* If run_length is zero, continue. Only case happening is when
890 * stop_parsing is TRUE too, so next cycle shouldn't run */
894 /* Trim the run_length to not go beyond the line end and consume
895 * it from remaining length of dest line */
896 run_length = MIN (run_length, dbuf_len);
897 dbuf_len -= run_length;
899 /* Make clut_index refer to the index into the desired bit depths
900 * CLUT definition table */
902 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
904 /* Now we can simply memset run_length count of destination bytes
905 * to clut_index, but only if not non_modifying */
906 dvb_log (DVB_LOG_RUNLEN, G_LOG_LEVEL_DEBUG,
907 "Setting %u pixels to color 0x%x in destination buffer; dbuf_len left is %d pixels",
908 run_length, clut_index, dbuf_len);
909 if (!(non_mod == 1 && bits == 1))
910 memset (destbuf, clut_index, run_length);
912 destbuf += run_length;
913 pixels_read += run_length;
916 // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
917 //gst_bit_reader_skip_to_byte (&gb);
918 *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
920 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
921 "Returning from 4bit_string parser with %u pixels read", pixels_read);
922 // FIXME: Shouldn't need this variable if tracking things in the loop better
927 _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len,
928 const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
930 GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
931 /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
933 gboolean stop_parsing = FALSE;
935 guint32 pixels_read = 0;
937 static gboolean warning_shown = FALSE;
938 if (!warning_shown) {
940 ("Parsing 8bit color DVB sub-picture. This is not tested at all. If you see this message, "
941 "please provide the developers with sample media with these subtitles, if possible.");
942 warning_shown = TRUE;
944 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
945 "(n=8): Inside %s with dbuf_len = %d", __PRETTY_FUNCTION__, dbuf_len);
947 /* FFMPEG-FIXME: ffmpeg uses a manual byte walking algorithm, which might be more performant,
948 * FFMPEG-FIXME: but it does almost absolutely no buffer length checking, so could walk over
949 * FFMPEG-FIXME: memory boundaries. While we don't check gst_bit_reader_get_bits_uint32
950 * FFMPEG-FIXME: return values either and therefore might get some pixels corrupted, we at
951 * FFMPEG-FIXME: lest have no chance of reading memory we don't own and visual corruption
952 * FFMPEG-FIXME: is guaranteed anyway when not all bytes are present */
953 /* Rephrased - it's better to work with bytes with default value '0' instead of reading from memory we don't own. */
954 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
955 guint run_length = 0, clut_index = 0;
956 gst_bit_reader_get_bits_uint32 (&gb, &bits, 8);
958 if (bits) { /* 8-bit_pixel-code */
961 } else { /* 8-bit_zero */
962 gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
963 if (bits == 0) { /* switch_1 == '0' */
964 /* run_length_1-127 for pseudo-colour _entry) '0x00' */
965 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7);
966 if (run_length == 0) { /* end_of_string_signal */
969 } else { /* switch_1 == '1' */
970 /* run_length_3-127 */
971 gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7);
972 gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 8);
974 /* Emit a debugging message about stream not following specification */
975 if (run_length < 3) {
976 dvb_log (DVB_LOG_STREAM, G_LOG_LEVEL_WARNING,
977 "8-bit/pixel_code_string::run_length_3-127 value was %u, but the spec requires it must be >=3",
984 /* If run_length is zero, continue. Only case happening is when
985 * stop_parsing is TRUE too, so next cycle shouldn't run */
989 /* Trim the run_length to not go beyond the line end and consume
990 * it from remaining length of dest line */
991 run_length = MIN (run_length, dbuf_len);
992 dbuf_len -= run_length;
994 /* Make clut_index refer to the index into the desired bit depths
995 * CLUT definition table */
997 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
999 /* Now we can simply memset run_length count of destination bytes
1000 * to clut_index, but only if not non_modifying */
1001 dvb_log (DVB_LOG_RUNLEN, G_LOG_LEVEL_DEBUG,
1002 "Setting %u pixels to color 0x%x in destination buffer; dbuf_len left is %d pixels",
1003 run_length, clut_index, dbuf_len);
1004 if (!(non_mod == 1 && bits == 1))
1005 memset (destbuf, clut_index, run_length);
1007 destbuf += run_length;
1008 pixels_read += run_length;
1011 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1012 "Returning from 8bit_string parser with %u pixels read", pixels_read);
1013 // FIXME: Shouldn't need this variable if tracking things in the loop better
1018 _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub,
1019 DVBSubObjectDisplay * display, const guint8 * buf, gint buf_size,
1020 DvbSubPixelDataSubBlockFieldType top_bottom, guint8 non_mod)
1022 DVBSubRegion *region = get_region (dvb_sub, display->region_id);
1023 const guint8 *buf_end = buf + buf_size;
1027 gboolean dest_buf_filled = FALSE;
1029 guint8 map2to4[] = { 0x0, 0x7, 0x8, 0xf };
1030 guint8 map2to8[] = { 0x00, 0x77, 0x88, 0xff };
1031 guint8 map4to8[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
1032 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
1036 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1037 "(parse_block): DVB pixel block size %d, %s field:",
1038 buf_size, top_bottom ? "bottom" : "top");
1040 #ifdef DEBUG_PACKET_CONTENTS
1041 gst_util_dump_mem (buf, buf_size);
1044 if (region == NULL) {
1045 g_print ("Region is NULL, returning\n");
1049 pbuf = region->pbuf;
1051 x_pos = display->x_pos;
1052 y_pos = display->y_pos;
1054 if ((y_pos & 1) != top_bottom)
1057 while (buf < buf_end) {
1058 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1059 "Iteration start, %u bytes missing from end; buf = %p, buf_end = %p; "
1060 "Region is number %u, with a dimension of %dx%d; We are at position %dx%d",
1061 buf_end - buf, buf, buf_end,
1062 region->id, region->width, region->height, x_pos, y_pos);
1063 // FFMPEG-FIXME: ffmpeg doesn't check for equality and so can overflow destination buffer later on with bad input data
1064 // FFMPEG-FIXME: However that makes it warn on end_of_object_line and map tables as well, so we add the dest_buf_filled tracking
1065 // FIXME: Removed x_pos checking here, because we don't want to turn dest_buf_filled to TRUE permanently in that case
1066 // FIXME: We assume that region->width - x_pos as dbuf_len to read_nbit_string will take care of that case nicely;
1067 // FIXME: That is, that read_nbit_string never scribbles anything if dbuf_len passed to it is zero due to this.
1068 if (y_pos >= region->height) {
1069 dest_buf_filled = TRUE;
1074 if (dest_buf_filled) {
1075 g_warning ("Invalid object location for data_type 0x%x!\n", *(buf - 1)); /* FIXME: Be more verbose */
1076 g_print ("Remaining data after invalid object location:\n");
1077 gst_util_dump_mem (buf, buf_end - buf);
1081 if (region->depth == 8)
1082 map_table = map2to8;
1083 else if (region->depth == 4)
1084 map_table = map2to4;
1088 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1089 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1091 _dvb_sub_read_2bit_string (pbuf + (y_pos * region->width) + x_pos,
1092 region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
1095 if (dest_buf_filled) {
1096 g_warning ("Invalid object location for data_type 0x%x!\n", *(buf - 1)); /* FIXME: Be more verbose */
1097 g_print ("Remaining data after invalid object location:\n");
1098 gst_util_dump_mem (buf, buf_end - buf);
1099 return; // FIXME: Perhaps tell read_nbit_string that dbuf_len is zero and let it walk the bytes regardless? (Same FIXME for 2bit and 8bit)
1102 if (region->depth < 4) {
1103 g_warning ("4-bit pixel string in %d-bit region!\n", region->depth);
1107 if (region->depth == 8)
1108 map_table = map4to8;
1112 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1113 "READ_nBIT_STRING (4): String data into position %dx%d; buf before is %p\n",
1115 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1116 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1118 _dvb_sub_read_4bit_string (pbuf + (y_pos * region->width) + x_pos,
1119 region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
1120 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1121 "READ_nBIT_STRING (4) finished: buf pointer now %p", buf);
1124 if (dest_buf_filled) {
1125 g_warning ("Invalid object location for data_type 0x%x!\n", *(buf - 1)); /* FIXME: Be more verbose */
1126 g_print ("Remaining data after invalid object location:\n");
1127 gst_util_dump_mem (buf, buf_end - buf);
1131 if (region->depth < 8) {
1132 g_warning ("8-bit pixel string in %d-bit region!\n", region->depth);
1135 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1136 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1138 _dvb_sub_read_8bit_string (pbuf + (y_pos * region->width) + x_pos,
1139 region->width - x_pos, &buf, buf_end - buf, non_mod, NULL);
1143 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1144 "(parse_block): handling map2to4 table data");
1145 /* FIXME: I don't see any guards about buffer size here - buf++ happens with the switch, but
1146 * FIXME: buffer is walked without length checks? Same deal in other map table cases */
1147 map2to4[0] = (*buf) >> 4;
1148 map2to4[1] = (*buf++) & 0xf;
1149 map2to4[2] = (*buf) >> 4;
1150 map2to4[3] = (*buf++) & 0xf;
1153 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1154 "(parse_block): handling map2to8 table data");
1155 for (i = 0; i < 4; i++)
1156 map2to8[i] = *buf++;
1159 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1160 "(parse_block): handling map4to8 table data");
1161 for (i = 0; i < 16; i++)
1162 map4to8[i] = *buf++;
1166 dvb_log (DVB_LOG_PIXEL, G_LOG_LEVEL_DEBUG,
1167 "(parse_block): end of object line code encountered");
1168 x_pos = display->x_pos;
1172 /* FIXME: Do we consume word align stuffing byte that could follow top/bottom data? */
1173 g_warning ("Unknown/unsupported pixel block 0x%x", *(buf - 1));
1179 _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
1182 const guint8 *buf_end = buf + buf_size;
1184 DVBSubObject *object;
1186 guint8 coding_method, non_modifying_color;
1188 object_id = GST_READ_UINT16_BE (buf);
1191 object = get_object (dvb_sub, object_id);
1193 dvb_log (DVB_LOG_OBJECT, G_LOG_LEVEL_DEBUG,
1194 "parse_object_segment: A new object segment has occurred for object_id = %u",
1199 ("Nothing known about object with ID %u yet inside parse_object_segment, bailing out",
1204 coding_method = ((*buf) >> 2) & 3;
1205 non_modifying_color = ((*buf++) >> 1) & 1;
1207 if (coding_method == 0) {
1208 const guint8 *block;
1209 DVBSubObjectDisplay *display;
1210 guint16 top_field_len, bottom_field_len;
1212 top_field_len = GST_READ_UINT16_BE (buf);
1214 bottom_field_len = GST_READ_UINT16_BE (buf);
1217 if (buf + top_field_len + bottom_field_len > buf_end) {
1218 g_warning ("%s: Field data size too large\n", __PRETTY_FUNCTION__);
1222 /* FIXME: Potential optimization opportunity here - parse the object pixmap only once, and copy it to all the
1223 * FIXME: regions that need it. One object being in multiple regions is a rare occurrence in real life, however */
1224 for (display = object->display_list; display;
1225 display = display->object_list_next) {
1228 dvb_log (DVB_LOG_OBJECT, G_LOG_LEVEL_DEBUG,
1229 "Parsing top and bottom part of object id %d; top_field_len = %u, bottom_field_len = %u",
1230 display->object_id, top_field_len, bottom_field_len);
1231 _dvb_sub_parse_pixel_data_block (dvb_sub, display, block, top_field_len,
1232 TOP_FIELD, non_modifying_color);
1234 if (bottom_field_len > 0)
1235 block = buf + top_field_len;
1237 bottom_field_len = top_field_len;
1239 _dvb_sub_parse_pixel_data_block (dvb_sub, display, block,
1240 bottom_field_len, BOTTOM_FIELD, non_modifying_color);
1243 } else if (coding_method == 1) {
1244 g_warning ("'a string of characters' coding method not supported (yet?)!");
1246 g_warning ("%s: Unknown object coding 0x%x\n", __PRETTY_FUNCTION__,
1252 _dvb_sub_parse_end_of_display_set (DvbSub * dvb_sub, guint16 page_id,
1253 guint8 * buf, gint buf_size, guint64 pts)
1255 DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
1257 DVBSubtitles *sub = g_slice_new0 (DVBSubtitles);
1259 DVBSubRegion *region;
1260 DVBSubRegionDisplay *display;
1261 DVBSubtitleRect *rect;
1263 guint32 *clut_table;
1266 static unsigned counter = 0; /* DEBUG use only */
1268 dvb_log (DVB_LOG_DISPLAY, G_LOG_LEVEL_DEBUG,
1269 "END OF DISPLAY SET: page_id = %u, length = %d\n", page_id, buf_size);
1272 #if 0 /* FIXME: PTS stuff not figured out yet */
1273 sub->start_display_time = 0;
1274 sub->end_display_time = priv->page_time_out * 1000;
1275 sub->format = 0; /* 0 = graphics */
1278 sub->num_rects = priv->display_list_size;
1280 if (sub->num_rects > 0) {
1281 // FIXME-MEMORY-LEAK: This structure is not freed up yet
1282 sub->rects = g_malloc0 (sizeof (*sub->rects) * sub->num_rects); /* GSlice? */
1283 for (i = 0; i < sub->num_rects; i++)
1284 sub->rects[i] = g_malloc0 (sizeof (*sub->rects[i])); /* GSlice? */
1289 for (display = priv->display_list; display; display = display->next) {
1290 region = get_region (dvb_sub, display->region_id);
1291 rect = sub->rects[i];
1296 rect->x = display->x_pos;
1297 rect->y = display->y_pos;
1298 rect->w = region->width;
1299 rect->h = region->height;
1300 #if 0 /* FIXME: Don't think we need to save the number of colors in the palette when we are saving as RGBA? */
1301 rect->nb_colors = 16;
1303 #if 0 /* FIXME: Needed to be specified once we support strings of characters based subtitles */
1304 rect->type = SUBTITLE_BITMAP;
1306 rect->pict.rowstride = region->width;
1307 rect->pict.palette_bits_count = region->depth;
1309 clut = get_clut (dvb_sub, region->clut);
1312 clut = &default_clut;
1314 switch (region->depth) {
1316 clut_table = clut->clut4;
1319 clut_table = clut->clut256;
1323 clut_table = clut->clut16;
1327 /* FIXME: Tweak this to be saved in a format most suitable for Qt and GStreamer instead.
1328 * Currently kept in AVPicture for quick save_display_set testing */
1329 rect->pict.palette = g_malloc ((1 << region->depth) * sizeof (guint32)); /* FIXME: Can we use GSlice here? */
1330 memcpy (rect->pict.palette, clut_table,
1331 (1 << region->depth) * sizeof (guint32));
1333 g_print ("rect->pict.data.palette content:\n");
1334 gst_util_dump_mem (rect->pict.palette,
1335 (1 << region->depth) * sizeof (guint32));
1338 rect->pict.data = g_malloc (region->buf_size); /* FIXME: Can we use GSlice here? */
1339 memcpy (rect->pict.data, region->pbuf, region->buf_size);
1342 dvb_log (DVB_LOG_DISPLAY, G_LOG_LEVEL_DEBUG,
1343 "An object rect created: number %u, iteration %u, pos: %d:%d, size: %dx%d",
1344 counter, i, rect->x, rect->y, rect->w, rect->h);
1346 g_print ("rect->pict.data content:\n");
1347 gst_util_dump_mem (rect->pict.data, region->buf_size);
1354 sub->page_time_out = priv->page_time_out;
1357 if (priv->callbacks.new_data) {
1358 priv->callbacks.new_data (dvb_sub, sub, priv->user_data);
1360 /* No-one responsible to clean up memory, so do it ourselves */
1361 /* FIXME: Just don't bother with all this palette image creation in the first place then... */
1362 dvb_subtitles_free (sub);
1365 return 1; /* FIXME: The caller of this function is probably supposed to do something with the return value */
1369 dvb_subtitles_free (DVBSubtitles * sub)
1372 DVBSubtitleRect *rect;
1374 /* Now free up all the temporary memory we allocated */
1375 for (i = 0; i < sub->num_rects; ++i) {
1376 rect = sub->rects[i];
1378 g_free (rect->pict.palette);
1379 g_free (rect->pict.data);
1382 g_free (sub->rects);
1383 g_slice_free (DVBSubtitles, sub);
1389 * Creates a new #DvbSub.
1391 * Return value: a newly created #DvbSub
1396 DvbSub *dvbsub = g_object_new (DVB_TYPE_SUB, NULL);
1401 #define DVB_SUB_SEGMENT_PAGE_COMPOSITION 0x10
1402 #define DVB_SUB_SEGMENT_REGION_COMPOSITION 0x11
1403 #define DVB_SUB_SEGMENT_CLUT_DEFINITION 0x12
1404 #define DVB_SUB_SEGMENT_OBJECT_DATA 0x13
1405 #define DVB_SUB_SEGMENT_END_OF_DISPLAY_SET 0x80
1406 #define DVB_SUB_SEGMENT_STUFFING 0xFF
1408 #define DVB_SUB_SYNC_BYTE 0x0f
1410 * dvb_sub_feed_with_pts:
1411 * @dvb_sub: a #DvbSub
1412 * @pts: The PTS of the data
1413 * @data: The data to feed to the parser
1414 * @len: Length of the data
1416 * Feeds the DvbSub parser with new binary data to parse,
1417 * with an associated PTS value. E.g, data left after PES
1418 * packet header has been already parsed, which contains
1419 * the PTS information).
1421 * Return value: -1 if data was unhandled (e.g, not a subtitle packet),
1422 * -2 if data parsing was unsuccesful (e.g, length was invalid),
1423 * 0 or positive if data was handled. If positive, then amount of data consumed on success. FIXME: List the positive return values.
1426 dvb_sub_feed_with_pts (DvbSub * dvb_sub, guint64 pts, guint8 * data, gint len)
1428 unsigned int pos = 0;
1429 guint8 segment_type;
1430 guint16 segment_len;
1433 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1434 "Inside dvb_sub_feed_with_pts with pts=%" G_GUINT64_FORMAT
1435 " and length %d", pts, len);
1437 g_return_val_if_fail (data != NULL, -1);
1439 if (len <= 3) { /* len(0x20 0x00 end_of_PES_data_field_marker) */
1440 g_warning ("Data length too short");
1444 if (data[pos++] != 0x20) {
1446 ("Tried to handle a PES packet private data that isn't a subtitle packet (does not start with 0x20)");
1450 if (data[pos++] != 0x00) {
1452 ("'Subtitle stream in this PES packet' was not 0x00, so this is in theory not a DVB subtitle stream (but some other subtitle standard?); bailing out");
1456 while (data[pos++] == DVB_SUB_SYNC_BYTE) {
1457 if ((len - pos) < (2 * 2 + 1)) {
1459 ("Data after SYNC BYTE too short, less than needed to even get to segment_length");
1462 segment_type = data[pos++];
1463 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1464 "=== Segment type is 0x%x", segment_type);
1465 page_id = (data[pos] << 8) | data[pos + 1];
1466 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG, "page_id is 0x%x", page_id);
1468 segment_len = (data[pos] << 8) | data[pos + 1];
1469 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1470 "segment_length is %d (0x%x 0x%x)", segment_len, data[pos],
1473 if ((len - pos) < segment_len) {
1475 ("segment_length was told to be %u, but we only have %d bytes left",
1476 segment_len, len - pos);
1479 // TODO: Parse the segment per type (this is probably a leftover TODO that is now done?)
1480 /* FIXME: Handle differing PTS values - all segments of a given display set must be with the same PTS,
1481 * FIXME: but we let it slip and just take it for granted in end_of_display_set */
1482 switch (segment_type) {
1483 case DVB_SUB_SEGMENT_PAGE_COMPOSITION:
1484 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1485 "Page composition segment at buffer pos %u\n", pos);
1486 _dvb_sub_parse_page_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1488 case DVB_SUB_SEGMENT_REGION_COMPOSITION:
1489 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1490 "Region composition segment at buffer pos %u\n", pos);
1491 _dvb_sub_parse_region_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1493 case DVB_SUB_SEGMENT_CLUT_DEFINITION:
1494 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1495 "CLUT definition segment at buffer pos %u\n", pos);
1496 _dvb_sub_parse_clut_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1498 case DVB_SUB_SEGMENT_OBJECT_DATA:
1499 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1500 "Object data segment at buffer pos %u\n", pos);
1501 _dvb_sub_parse_object_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1503 case DVB_SUB_SEGMENT_END_OF_DISPLAY_SET:
1504 dvb_log (DVB_LOG_PACKET, G_LOG_LEVEL_DEBUG,
1505 "End of display set at buffer pos %u\n", pos);
1506 _dvb_sub_parse_end_of_display_set (dvb_sub, page_id, data + pos, segment_len, pts); /* FIXME: Not sure about args */
1509 g_warning ("Unhandled segment type 0x%x", segment_type);
1516 g_warning ("Data ended without a PES data end marker");
1521 g_warning ("Processed %d bytes out of %d\n", pos, len);
1526 * dvb_sub_set_callbacks:
1527 * @dvb_sub: a #DvbSub
1528 * @callbacks: the callbacks to install
1529 * @user_data: a user_data argument for the callback
1531 * Set callback which will be executed when new subpictures are available.
1534 dvb_sub_set_callbacks (DvbSub * dvb_sub, DvbSubCallbacks * callbacks,
1537 DvbSubPrivate *priv;
1539 g_return_if_fail (dvb_sub != NULL);
1540 g_return_if_fail (DVB_IS_SUB (dvb_sub));
1541 g_return_if_fail (callbacks != NULL);
1543 priv = (DvbSubPrivate *) dvb_sub->private_data;
1545 priv->callbacks = *callbacks;
1546 priv->user_data = user_data;
1550 dvb_sub_set_global_log_cb (void (*log_cb) (GLogLevelFlags log_level,
1551 const gchar * format, va_list args, gpointer user_data),
1555 g_log_callback = log_cb;
1556 g_log_callback_user_data = user_data;