dvbsuboverlay: clean-up: dvb sub parser helper doesn't need to be a GObject
[platform/upstream/gstreamer.git] / gst / dvbsuboverlay / dvb-sub.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * libdvbsub - DVB subtitle decoding
4  * Copyright (C) Mart Raudsepp 2009 <mart.raudsepp@artecdesign.ee>
5  * 
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.
9  *
10  * Original copyright information follows:
11  */
12 /*
13  * DVB subtitle decoding for ffmpeg
14  * Copyright (c) 2005 Ian Caulfield
15  *
16  * This file is part of FFmpeg.
17  *
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.
22  *
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.
27  *
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
31  */
32
33 #include <string.h>             /* memset */
34 #include <gst/gstutils.h>       /* GST_READ_UINT16_BE */
35 #include <gst/base/gstbitreader.h>      /* GstBitReader */
36 #include "ffmpeg-colorspace.h" /* YUV_TO_RGB1_CCIR */   /* FIXME: Just give YUV data to gstreamer then? */
37
38 #include "dvb-sub.h"
39
40 GST_DEBUG_CATEGORY_STATIC (dvbsub_debug);
41 #define GST_CAT_DEFAULT dvbsub_debug
42
43 static void dvb_sub_init (void);
44
45 /* FIXME: Are we waiting for an acquisition point before trying to do things? */
46 /* FIXME: In the end convert some of the guint8/16 (especially stack variables) back to gint for access efficiency */
47
48 /**
49  * SECTION:dvb-sub
50  * @short_description: a DVB subtitle parsing class
51  * @stability: Unstable
52  *
53  * The #DvbSub represents an object used for parsing a DVB subpicture,
54  * and signalling the API user for new bitmaps to show on screen.
55  */
56
57 #define MAX_NEG_CROP 1024
58 static guint8 ff_cropTbl[256 + 2 * MAX_NEG_CROP] = { 0, };
59
60 #define cm (ff_cropTbl + MAX_NEG_CROP)
61
62 /* FIXME: This is really ARGB... We might need this configurable for performant
63  * FIXME: use in GStreamer as well if that likes RGBA more (Qt prefers ARGB) */
64 #define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
65
66 typedef struct DVBSubCLUT
67 {
68   int id;                       /* default_clut uses -1 for this, so guint8 isn't fine without adaptations first */
69
70   guint32 clut4[4];
71   guint32 clut16[16];
72   guint32 clut256[256];
73
74   struct DVBSubCLUT *next;
75 } DVBSubCLUT;
76
77 static DVBSubCLUT default_clut;
78
79 typedef struct DVBSubObjectDisplay
80 {
81   /* FIXME: Use more correct sizes */
82   int object_id;
83   int region_id;
84
85   int x_pos;
86   int y_pos;
87
88   int fgcolor;
89   int bgcolor;
90
91   /* FIXME: Should we use GSList? The relating interaction and pointer assigment is quite complex and perhaps unsuited for a plain GSList anyway */
92   struct DVBSubObjectDisplay *region_list_next;
93   struct DVBSubObjectDisplay *object_list_next;
94 } DVBSubObjectDisplay;
95
96 typedef struct DVBSubObject
97 {
98   /* FIXME: Use more correct sizes */
99   int id;                       /* FIXME: Use guint8 after checking it's fine in all code using it */
100
101   int type;
102
103   /* FIXME: Should we use GSList? */
104   DVBSubObjectDisplay *display_list;
105   struct DVBSubObject *next;
106 } DVBSubObject;
107
108 typedef struct DVBSubRegionDisplay
109 {                               /* FIXME: Figure out if this structure is only used temporarily in page_segment parser, or also more */
110   int region_id;
111
112   int x_pos;
113   int y_pos;
114
115   struct DVBSubRegionDisplay *next;
116 } DVBSubRegionDisplay;
117
118 typedef struct DVBSubRegion
119 {
120   guint8 id;
121   guint16 width;
122   guint16 height;
123   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 */
124
125   guint8 clut;
126   guint8 bgcolor;
127
128   /* FIXME: Validate these fields existence and exact types */
129   guint8 *pbuf;
130   int buf_size;
131
132   DVBSubObjectDisplay *display_list;
133
134   struct DVBSubRegion *next;
135 } DVBSubRegion;
136
137 typedef struct _DvbSubPrivate DvbSubPrivate;
138 struct _DvbSubPrivate
139 {
140   int fd;
141   DvbSubCallbacks callbacks;
142   gpointer user_data;
143
144   guint8 page_time_out;
145   DVBSubRegion *region_list;
146   DVBSubCLUT *clut_list;
147   DVBSubObject *object_list;
148   /* FIXME... */
149   int display_list_size;
150   DVBSubRegionDisplay *display_list;
151   GString *pes_buffer;
152   DVBSubtitleWindow display_def;
153 };
154 struct _DvbSub
155 {
156   DvbSubPrivate priv;           // FIXME (tpm)
157
158   DvbSubPrivate *private_data;
159 };
160
161 typedef enum
162 {
163   TOP_FIELD = 0,
164   BOTTOM_FIELD = 1
165 } DvbSubPixelDataSubBlockFieldType;
166
167 /* FIXME: It might make sense to pass DvbSubPrivate for all the get_* functions, instead of public DvbSub */
168 static DVBSubObject *
169 get_object (DvbSub * dvb_sub, guint16 object_id)
170 {
171   const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
172   DVBSubObject *ptr = priv->object_list;
173
174   while (ptr && ptr->id != object_id) {
175     ptr = ptr->next;
176   }
177
178   return ptr;
179 }
180
181 static DVBSubCLUT *
182 get_clut (DvbSub * dvb_sub, gint clut_id)
183 {
184   const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
185   DVBSubCLUT *ptr = priv->clut_list;
186
187   while (ptr && ptr->id != clut_id) {
188     ptr = ptr->next;
189   }
190
191   return ptr;
192 }
193
194 // FIXME: Just pass private_data pointer directly here and in other get_* helper functions?
195 static DVBSubRegion *
196 get_region (DvbSub * dvb_sub, guint8 region_id)
197 {
198   const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
199   DVBSubRegion *ptr = priv->region_list;
200
201   while (ptr && ptr->id != region_id) {
202     ptr = ptr->next;
203   }
204
205   return ptr;
206 }
207
208 static void
209 delete_region_display_list (DvbSub * dvb_sub, DVBSubRegion * region)
210 {
211   const DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
212   DVBSubObject *object, *obj2;
213   DVBSubObject **obj2_ptr;
214   DVBSubObjectDisplay *display, *obj_disp, **obj_disp_ptr;
215
216   while (region->display_list) {
217     display = region->display_list;
218
219     object = get_object (dvb_sub, display->object_id);
220
221     if (object) {
222       obj_disp_ptr = &object->display_list;
223       obj_disp = *obj_disp_ptr;
224
225       while (obj_disp && obj_disp != display) {
226         obj_disp_ptr = &obj_disp->object_list_next;
227         obj_disp = *obj_disp_ptr;
228       }
229
230       if (obj_disp) {
231         *obj_disp_ptr = obj_disp->object_list_next;
232
233         if (!object->display_list) {
234           obj2_ptr = (DVBSubObject **) & priv->object_list;     /* FIXME: Evil casting */
235           obj2 = *obj2_ptr;
236
237           while (obj2 != object) {
238             g_assert (obj2);
239             obj2_ptr = &obj2->next;
240             obj2 = *obj2_ptr;
241           }
242
243           *obj2_ptr = obj2->next;
244
245           g_slice_free (DVBSubObject, obj2);
246         }
247       }
248     }
249
250     region->display_list = display->region_list_next;
251
252     g_slice_free (DVBSubObjectDisplay, display);
253   }
254 }
255
256 static void
257 delete_state (DvbSub * dvb_sub)
258 {
259   DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
260   DVBSubRegion *region;
261
262   while (priv->region_list) {
263     region = priv->region_list;
264
265     priv->region_list = region->next;
266
267     delete_region_display_list (dvb_sub, region);
268     if (region->pbuf)
269       g_free (region->pbuf);
270
271     g_slice_free (DVBSubRegion, region);
272   }
273
274   g_slice_free_chain (DVBSubCLUT, priv->clut_list, next);
275   priv->clut_list = NULL;
276
277   /* Should already be null */
278   if (priv->object_list)
279     g_warning ("Memory deallocation error!");
280 }
281
282 /* init static data necessary for ffmpeg-colorspace conversion */
283 static void
284 dsputil_static_init (void)
285 {
286   int i;
287
288   for (i = 0; i < 256; i++)
289     ff_cropTbl[i + MAX_NEG_CROP] = i;
290   for (i = 0; i < MAX_NEG_CROP; i++) {
291     ff_cropTbl[i] = 0;
292     ff_cropTbl[i + MAX_NEG_CROP + 256] = 255;
293   }
294 }
295
296 static void
297 dvb_sub_init (void)
298 {
299   int i, r, g, b, a = 0;
300
301   GST_DEBUG_CATEGORY_INIT (dvbsub_debug, "dvbsub", 0, "dvbsuboverlay parser");
302
303   dsputil_static_init ();       /* Initializes ff_cropTbl table, used in YUV_TO_RGB conversion */
304
305   /* Initialize the static default_clut structure, from which other clut
306    * structures are initialized from (to start off with default CLUTs
307    * as defined in the specification). */
308   default_clut.id = -1;
309
310   default_clut.clut4[0] = RGBA (0, 0, 0, 0);
311   default_clut.clut4[1] = RGBA (255, 255, 255, 255);
312   default_clut.clut4[2] = RGBA (0, 0, 0, 255);
313   default_clut.clut4[3] = RGBA (127, 127, 127, 255);
314
315   default_clut.clut16[0] = RGBA (0, 0, 0, 0);
316   for (i = 1; i < 16; i++) {
317     if (i < 8) {
318       r = (i & 1) ? 255 : 0;
319       g = (i & 2) ? 255 : 0;
320       b = (i & 4) ? 255 : 0;
321     } else {
322       r = (i & 1) ? 127 : 0;
323       g = (i & 2) ? 127 : 0;
324       b = (i & 4) ? 127 : 0;
325     }
326     default_clut.clut16[i] = RGBA (r, g, b, 255);
327   }
328
329   default_clut.clut256[0] = RGBA (0, 0, 0, 0);
330   for (i = 1; i < 256; i++) {
331     if (i < 8) {
332       r = (i & 1) ? 255 : 0;
333       g = (i & 2) ? 255 : 0;
334       b = (i & 4) ? 255 : 0;
335       a = 63;
336     } else {
337       switch (i & 0x88) {
338         case 0x00:
339           r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
340           g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
341           b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
342           a = 255;
343           break;
344         case 0x08:
345           r = ((i & 1) ? 85 : 0) + ((i & 0x10) ? 170 : 0);
346           g = ((i & 2) ? 85 : 0) + ((i & 0x20) ? 170 : 0);
347           b = ((i & 4) ? 85 : 0) + ((i & 0x40) ? 170 : 0);
348           a = 127;
349           break;
350         case 0x80:
351           r = 127 + ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
352           g = 127 + ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
353           b = 127 + ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
354           a = 255;
355           break;
356         case 0x88:
357           r = ((i & 1) ? 43 : 0) + ((i & 0x10) ? 85 : 0);
358           g = ((i & 2) ? 43 : 0) + ((i & 0x20) ? 85 : 0);
359           b = ((i & 4) ? 43 : 0) + ((i & 0x40) ? 85 : 0);
360           a = 255;
361           break;
362       }
363     }
364     default_clut.clut256[i] = RGBA (r, g, b, a);
365   }
366 }
367
368 static void
369 _dvb_sub_parse_page_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
370     gint buf_size)
371 {                               /* FIXME: Use guint for buf_size here and in many other places? */
372   DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
373   DVBSubRegionDisplay *display;
374   DVBSubRegionDisplay *tmp_display_list, **tmp_ptr;
375
376   const guint8 *buf_end = buf + buf_size;
377   guint8 region_id;
378   guint8 page_state;
379
380 #ifndef GST_DISABLE_GST_DEBUG
381   static int counter = 0;       /* FIXME: static counter? I think not.. */
382 #endif
383
384   if (buf_size < 1)
385     return;
386
387   priv->page_time_out = *buf++;
388   page_state = ((*buf++) >> 2) & 3;
389
390 #ifndef GST_DISABLE_GST_DEBUG
391   {
392     static const gchar *page_state_str[4] = {
393       "Normal case", "ACQUISITION POINT", "Mode Change", "RESERVED"
394     };
395
396     ++counter;
397     GST_DEBUG ("PAGE: %d: page_id = %u, length = %d, page_time_out = %u secs, "
398         "page_state = %s", counter, page_id, buf_size, priv->page_time_out,
399         page_state_str[page_state]);
400   }
401 #endif
402
403   if (page_state == 2) {        /* Mode change */
404     delete_state (dvb_sub);
405   }
406
407   tmp_display_list = priv->display_list;
408   priv->display_list = NULL;
409   priv->display_list_size = 0;
410
411   while (buf + 5 < buf_end) {
412     region_id = *buf++;
413     buf += 1;
414
415     display = tmp_display_list;
416     tmp_ptr = &tmp_display_list;
417
418     while (display && display->region_id != region_id) {
419       tmp_ptr = &display->next;
420       display = display->next;
421     }
422
423     if (!display)
424       display = g_slice_new0 (DVBSubRegionDisplay);
425
426     display->region_id = region_id;
427
428     display->x_pos = GST_READ_UINT16_BE (buf);
429     buf += 2;
430     display->y_pos = GST_READ_UINT16_BE (buf);
431     buf += 2;
432
433     *tmp_ptr = display->next;
434
435     display->next = priv->display_list;
436     priv->display_list = display;
437     priv->display_list_size++;
438
439     GST_LOG ("PAGE %d: REGION information: ID = %u, address = %ux%u",
440         counter, region_id, display->x_pos, display->y_pos);
441   }
442
443   while (tmp_display_list) {
444     display = tmp_display_list;
445
446     tmp_display_list = display->next;
447
448     g_slice_free (DVBSubRegionDisplay, display);
449   }
450 }
451
452 static void
453 _dvb_sub_parse_region_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
454     gint buf_size)
455 {
456   DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
457
458   const guint8 *buf_end = buf + buf_size;
459   guint8 region_id;
460   guint16 object_id;
461   DVBSubRegion *region;
462   DVBSubObject *object;
463   DVBSubObjectDisplay *object_display;
464   gboolean fill;
465
466   if (buf_size < 10)
467     return;
468
469   region_id = *buf++;
470
471   region = get_region (dvb_sub, region_id);
472
473   if (!region) {                /* Create a new region */
474     region = g_slice_new0 (DVBSubRegion);
475     region->id = region_id;
476     region->next = priv->region_list;
477     priv->region_list = region;
478   }
479
480   fill = ((*buf++) >> 3) & 1;
481
482   region->width = GST_READ_UINT16_BE (buf);
483   buf += 2;
484   region->height = GST_READ_UINT16_BE (buf);
485   buf += 2;
486
487   if (region->width * region->height != region->buf_size) {     /* FIXME: Read closer from spec what happens when dimensions change */
488     if (region->pbuf)
489       g_free (region->pbuf);
490
491     region->buf_size = region->width * region->height;
492
493     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 */
494
495     fill = 1;                   /* FIXME: Validate from spec that fill is forced on (in the following codes context) when dimensions change */
496   }
497
498   region->depth = 1 << (((*buf++) >> 2) & 7);
499   if (region->depth < 2 || region->depth > 8) {
500     GST_WARNING ("region depth %d is invalid", region->depth);
501     region->depth = 4;          /* FIXME: Check from spec this is the default? */
502   }
503
504   region->clut = *buf++;
505
506   if (region->depth == 8)
507     region->bgcolor = *buf++;
508   else {
509     buf += 1;
510
511     if (region->depth == 4)
512       region->bgcolor = (((*buf++) >> 4) & 15);
513     else
514       region->bgcolor = (((*buf++) >> 2) & 3);
515   }
516
517   GST_DEBUG ("REGION: id = %u, (%ux%u)@%u-bit", region_id, region->width,
518       region->height, region->depth);
519
520   if (fill) {
521     memset (region->pbuf, region->bgcolor, region->buf_size);
522     GST_DEBUG ("REGION: filling region (%u) with bgcolor = %u", region->id,
523         region->bgcolor);
524   }
525
526   delete_region_display_list (dvb_sub, region); /* Delete the region display list for current region - FIXME: why? */
527
528   while (buf + 6 <= buf_end) {
529     object_id = GST_READ_UINT16_BE (buf);
530     buf += 2;
531
532     object = get_object (dvb_sub, object_id);
533
534     if (!object) {
535       object = g_slice_new0 (DVBSubObject);
536
537       object->id = object_id;
538
539       object->next = priv->object_list;
540       priv->object_list = object;
541     }
542
543     object->type = (*buf) >> 6;
544
545     object_display = g_slice_new0 (DVBSubObjectDisplay);
546
547     object_display->object_id = object_id;
548     object_display->region_id = region_id;
549
550     object_display->x_pos = GST_READ_UINT16_BE (buf) & 0xfff;
551     buf += 2;
552     object_display->y_pos = GST_READ_UINT16_BE (buf) & 0xfff;
553     buf += 2;
554
555     if ((object->type == 1 || object->type == 2) && buf + 2 <= buf_end) {
556       object_display->fgcolor = *buf++;
557       object_display->bgcolor = *buf++;
558     }
559
560     object_display->region_list_next = region->display_list;
561     region->display_list = object_display;
562
563     object_display->object_list_next = object->display_list;
564     object->display_list = object_display;
565
566     GST_DEBUG ("REGION DATA: object_id = %u, region_id = %u, pos = %ux%u, "
567         "obj_type = %u", object->id, region->id, object_display->x_pos,
568         object_display->y_pos, object->type);
569
570     if (object->type == 1 || object->type == 2) {
571       GST_DEBUG ("REGION DATA: fgcolor = %u, bgcolor = %u",
572           object_display->fgcolor, object_display->bgcolor);
573     }
574   }
575 }
576
577 static void
578 _dvb_sub_parse_clut_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
579     gint buf_size)
580 {
581   DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
582
583   const guint8 *buf_end = buf + buf_size;
584   guint8 clut_id;
585   DVBSubCLUT *clut;
586   int entry_id, depth, full_range;
587   int y, cr, cb, alpha;
588   int r, g, b, r_add, g_add, b_add;
589
590   GST_MEMDUMP ("DVB clut packet", buf, buf_size);
591
592   clut_id = *buf++;
593   buf += 1;
594
595   clut = get_clut (dvb_sub, clut_id);
596
597   if (!clut) {
598     clut = g_slice_new (DVBSubCLUT);    /* FIXME-MEMORY-LEAK: This seems to leak per valgrind */
599
600     memcpy (clut, &default_clut, sizeof (DVBSubCLUT));
601
602     clut->id = clut_id;
603
604     clut->next = priv->clut_list;
605     priv->clut_list = clut;
606   }
607
608   while (buf + 4 < buf_end) {
609     entry_id = *buf++;
610
611     depth = (*buf) & 0xe0;
612
613     if (depth == 0) {
614       GST_WARNING ("Invalid clut depth 0x%x!", *buf);
615       return;
616     }
617
618     full_range = (*buf++) & 1;
619
620     if (full_range) {
621       y = *buf++;
622       cr = *buf++;
623       cb = *buf++;
624       alpha = *buf++;
625     } else {
626       y = buf[0] & 0xfc;
627       cr = (((buf[0] & 3) << 2) | ((buf[1] >> 6) & 3)) << 4;
628       cb = (buf[1] << 2) & 0xf0;
629       alpha = (buf[1] << 6) & 0xc0;
630
631       buf += 2;
632     }
633
634     if (y == 0)
635       alpha = 0xff;
636
637     YUV_TO_RGB1_CCIR (cb, cr);
638     YUV_TO_RGB2_CCIR (r, g, b, y);
639
640     GST_DEBUG ("CLUT DEFINITION: clut %d := (%d,%d,%d,%d)", entry_id, r, g, b,
641         alpha);
642
643     if (depth & 0x80)
644       clut->clut4[entry_id] = RGBA (r, g, b, 255 - alpha);
645     if (depth & 0x40)
646       clut->clut16[entry_id] = RGBA (r, g, b, 255 - alpha);
647     if (depth & 0x20)
648       clut->clut256[entry_id] = RGBA (r, g, b, 255 - alpha);
649   }
650 }
651
652 // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same
653 // FFMPEG-FIXME: refactoring as done here
654 static int
655 _dvb_sub_read_2bit_string (guint8 * destbuf, gint dbuf_len,
656     const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
657 {
658   GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
659   /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
660
661   gboolean stop_parsing = FALSE;
662   guint32 bits = 0;
663   guint32 pixels_read = 0;
664
665   static gboolean warning_shown = FALSE;
666   if (!warning_shown) {
667     g_warning ("Parsing 2bit color DVB sub-picture. This is not tested at all. "
668         "If you see this message, please provide the developers with sample "
669         "media with these subtitles, if possible.");
670     warning_shown = TRUE;
671   }
672
673   GST_TRACE ("dbuf_len = %d", dbuf_len);
674
675   while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
676     guint run_length = 0, clut_index = 0;
677     gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
678
679     if (bits) {                 /* 2-bit_pixel-code */
680       run_length = 1;
681       clut_index = bits;
682     } else {                    /* 2-bit_zero */
683       gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
684       if (bits == 1) {          /* switch_1 == '1' */
685         gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3);
686         run_length += 3;
687         gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
688       } else {                  /* switch_1 == '0' */
689         gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
690         if (bits == 1) {        /* switch_2 == '1' */
691           run_length = 1;       /* 1x pseudo-colour '00' */
692         } else {                /* switch_2 == '0' */
693           gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
694           switch (bits) {       /* switch_3 */
695             case 0x0:          /* end of 2-bit/pixel_code_string */
696               stop_parsing = TRUE;
697               break;
698             case 0x1:          /* two pixels shall be set to pseudo colour (entry) '00' */
699               run_length = 2;
700               break;
701             case 0x2:          /* the following 6 bits contain run length coded pixel data */
702               gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4);
703               run_length += 12;
704               gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
705               break;
706             case 0x3:          /* the following 10 bits contain run length coded pixel data */
707               gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8);
708               run_length += 29;
709               gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 2);
710               break;
711           }
712         }
713       }
714     }
715
716     /* If run_length is zero, continue. Only case happening is when
717      * stop_parsing is TRUE too, so next cycle shouldn't run */
718     if (run_length == 0)
719       continue;
720
721     /* Trim the run_length to not go beyond the line end and consume
722      * it from remaining length of dest line */
723     run_length = MIN (run_length, dbuf_len);
724     dbuf_len -= run_length;
725
726     /* Make clut_index refer to the index into the desired bit depths
727      * CLUT definition table */
728     if (map_table)
729       clut_index = map_table[clut_index];       /* now clut_index signifies the index into map_table dest */
730
731     /* Now we can simply memset run_length count of destination bytes
732      * to clut_index, but only if not non_modifying */
733     GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer, "
734         "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
735
736     if (!(non_mod == 1 && bits == 1))
737       memset (destbuf, clut_index, run_length);
738
739     destbuf += run_length;
740     pixels_read += run_length;
741   }
742
743   // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
744   //gst_bit_reader_skip_to_byte (&gb);
745   *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
746
747   GST_TRACE ("PIXEL: returning, read %u pixels", pixels_read);
748   // FIXME: Shouldn't need this variable if tracking things in the loop better
749   return pixels_read;
750 }
751
752 // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same
753 // FFMPEG-FIXME: refactoring as done here, explained in commit 895296c3
754 static int
755 _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len,
756     const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
757 {
758   GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
759   /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
760   gboolean stop_parsing = FALSE;
761   guint32 bits = 0;
762   guint32 pixels_read = 0;
763
764   GST_TRACE ("RUNLEN: srcbuf position %p, buf_size = %d; destination buffer "
765       "size is %d @ %p", *srcbuf, buf_size, dbuf_len, destbuf);
766
767   while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
768     guint run_length = 0, clut_index = 0;
769     gst_bit_reader_get_bits_uint32 (&gb, &bits, 4);
770
771     if (bits) {
772       run_length = 1;
773       clut_index = bits;
774     } else {
775       gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
776       if (bits == 0) {          /* switch_1 == '0' */
777         gst_bit_reader_get_bits_uint32 (&gb, &run_length, 3);
778         if (!run_length) {
779           stop_parsing = TRUE;
780         } else {
781           run_length += 2;
782         }
783       } else {                  /* switch_1 == '1' */
784         gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
785         if (bits == 0) {        /* switch_2 == '0' */
786           gst_bit_reader_get_bits_uint32 (&gb, &run_length, 2);
787           run_length += 4;
788           gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
789         } else {                /* switch_2 == '1' */
790           gst_bit_reader_get_bits_uint32 (&gb, &bits, 2);
791           switch (bits) {
792             case 0x0:          /* switch_3 == '00' */
793               run_length = 1;   /* 1 pixel of pseudo-color 0 */
794               break;
795             case 0x1:          /* switch_3 == '01' */
796               run_length = 2;   /* 2 pixels of pseudo-color 0 */
797               break;
798             case 0x2:          /* switch_3 == '10' */
799               gst_bit_reader_get_bits_uint32 (&gb, &run_length, 4);
800               run_length += 9;
801               gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
802               break;
803             case 0x3:          /* switch_3 == '11' */
804               gst_bit_reader_get_bits_uint32 (&gb, &run_length, 8);
805               run_length += 25;
806               gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 4);
807               break;
808           }
809         }
810       }
811     }
812
813     /* If run_length is zero, continue. Only case happening is when
814      * stop_parsing is TRUE too, so next cycle shouldn't run */
815     if (run_length == 0)
816       continue;
817
818     /* Trim the run_length to not go beyond the line end and consume
819      * it from remaining length of dest line */
820     run_length = MIN (run_length, dbuf_len);
821     dbuf_len -= run_length;
822
823     /* Make clut_index refer to the index into the desired bit depths
824      * CLUT definition table */
825     if (map_table)
826       clut_index = map_table[clut_index];       /* now clut_index signifies the index into map_table dest */
827
828     /* Now we can simply memset run_length count of destination bytes
829      * to clut_index, but only if not non_modifying */
830     GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer; "
831         "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
832
833     if (!(non_mod == 1 && bits == 1))
834       memset (destbuf, clut_index, run_length);
835
836     destbuf += run_length;
837     pixels_read += run_length;
838   }
839
840   // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
841   //gst_bit_reader_skip_to_byte (&gb);
842   *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
843
844   GST_LOG ("Returning with %u pixels read", pixels_read);
845
846   // FIXME: Shouldn't need this variable if tracking things in the loop better
847   return pixels_read;
848 }
849
850 static int
851 _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len,
852     const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
853 {
854   GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
855   /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
856
857   gboolean stop_parsing = FALSE;
858   guint32 bits = 0;
859   guint32 pixels_read = 0;
860
861   static gboolean warning_shown = FALSE;
862   if (!warning_shown) {
863     g_warning
864         ("Parsing 8bit color DVB sub-picture. This is not tested at all. If you see this message, "
865         "please provide the developers with sample media with these subtitles, if possible.");
866     warning_shown = TRUE;
867   }
868
869   GST_LOG ("dbuf_len = %d", dbuf_len);
870
871   /* FFMPEG-FIXME: ffmpeg uses a manual byte walking algorithm, which might be more performant,
872    * FFMPEG-FIXME: but it does almost absolutely no buffer length checking, so could walk over
873    * FFMPEG-FIXME: memory boundaries. While we don't check gst_bit_reader_get_bits_uint32
874    * FFMPEG-FIXME: return values either and therefore might get some pixels corrupted, we at
875    * FFMPEG-FIXME: lest have no chance of reading memory we don't own and visual corruption
876    * FFMPEG-FIXME: is guaranteed anyway when not all bytes are present */
877   /* Rephrased - it's better to work with bytes with default value '0' instead of reading from memory we don't own. */
878   while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 0)) {
879     guint run_length = 0, clut_index = 0;
880     gst_bit_reader_get_bits_uint32 (&gb, &bits, 8);
881
882     if (bits) {                 /* 8-bit_pixel-code */
883       run_length = 1;
884       clut_index = bits;
885     } else {                    /* 8-bit_zero */
886       gst_bit_reader_get_bits_uint32 (&gb, &bits, 1);
887       if (bits == 0) {          /* switch_1 == '0' */
888         /* run_length_1-127 for pseudo-colour _entry) '0x00' */
889         gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7);
890         if (run_length == 0) {  /* end_of_string_signal */
891           stop_parsing = TRUE;
892         }
893       } else {                  /* switch_1 == '1' */
894         /* run_length_3-127 */
895         gst_bit_reader_get_bits_uint32 (&gb, &run_length, 7);
896         gst_bit_reader_get_bits_uint32 (&gb, &clut_index, 8);
897
898         if (run_length < 3) {
899           GST_WARNING ("runlength value was %u, but the spec requires it "
900               "must be >=3", run_length);
901         }
902       }
903     }
904
905     /* If run_length is zero, continue. Only case happening is when
906      * stop_parsing is TRUE too, so next cycle shouldn't run */
907     if (run_length == 0)
908       continue;
909
910     /* Trim the run_length to not go beyond the line end and consume
911      * it from remaining length of dest line */
912     run_length = MIN (run_length, dbuf_len);
913     dbuf_len -= run_length;
914
915     /* Make clut_index refer to the index into the desired bit depths
916      * CLUT definition table */
917     if (map_table)
918       clut_index = map_table[clut_index];       /* now clut_index signifies the index into map_table dest */
919
920     /* Now we can simply memset run_length count of destination bytes
921      * to clut_index, but only if not non_modifying */
922     GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer; "
923         "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
924
925     if (!(non_mod == 1 && bits == 1))
926       memset (destbuf, clut_index, run_length);
927
928     destbuf += run_length;
929     pixels_read += run_length;
930   }
931
932   GST_LOG ("Returning with %u pixels read", pixels_read);
933
934   // FIXME: Shouldn't need this variable if tracking things in the loop better
935   return pixels_read;
936 }
937
938 static void
939 _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub,
940     DVBSubObjectDisplay * display, const guint8 * buf, gint buf_size,
941     DvbSubPixelDataSubBlockFieldType top_bottom, guint8 non_mod)
942 {
943   DVBSubRegion *region = get_region (dvb_sub, display->region_id);
944   const guint8 *buf_end = buf + buf_size;
945   guint8 *pbuf;
946   int x_pos, y_pos;
947   int i;
948   gboolean dest_buf_filled = FALSE;
949
950   guint8 map2to4[] = { 0x0, 0x7, 0x8, 0xf };
951   guint8 map2to8[] = { 0x00, 0x77, 0x88, 0xff };
952   guint8 map4to8[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
953     0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
954   };
955   guint8 *map_table;
956
957   GST_LOG ("DVB pixel block size %d, %s field:", buf_size,
958       top_bottom ? "bottom" : "top");
959
960   GST_MEMDUMP ("packet", buf, buf_size);
961
962   if (region == NULL) {
963     GST_LOG ("Region is NULL, returning");
964     return;
965   }
966
967   pbuf = region->pbuf;
968
969   x_pos = display->x_pos;
970   y_pos = display->y_pos;
971
972   if ((y_pos & 1) != top_bottom)
973     y_pos++;
974
975   while (buf < buf_end) {
976     GST_LOG ("Iteration start, %u bytes missing from end; buf = %p, "
977         "buf_end = %p; Region is number %u, with a dimension of %dx%d; "
978         "We are at position %dx%d", (guint) (buf_end - buf), buf, buf_end,
979         region->id, region->width, region->height, x_pos, y_pos);
980
981     // FFMPEG-FIXME: ffmpeg doesn't check for equality and so can overflow destination buffer later on with bad input data
982     // 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
983     // FIXME: Removed x_pos checking here, because we don't want to turn dest_buf_filled to TRUE permanently in that case
984     // FIXME: We assume that region->width - x_pos as dbuf_len to read_nbit_string will take care of that case nicely;
985     // FIXME: That is, that read_nbit_string never scribbles anything if dbuf_len passed to it is zero due to this.
986     if (y_pos >= region->height) {
987       dest_buf_filled = TRUE;
988     }
989
990     switch (*buf++) {
991       case 0x10:
992         if (dest_buf_filled) {
993           /* FIXME: Be more verbose */
994           GST_WARNING ("Invalid object location for data_type 0x%x!",
995               *(buf - 1));
996           GST_MEMDUMP ("Remaining data after invalid object location:", buf,
997               (guint) (buf_end - buf));
998           return;
999         }
1000
1001         if (region->depth == 8)
1002           map_table = map2to8;
1003         else if (region->depth == 4)
1004           map_table = map2to4;
1005         else
1006           map_table = NULL;
1007
1008         // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1009         // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1010         x_pos +=
1011             _dvb_sub_read_2bit_string (pbuf + (y_pos * region->width) + x_pos,
1012             region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
1013         break;
1014       case 0x11:
1015         if (dest_buf_filled) {
1016           /* FIXME: Be more verbose */
1017           GST_WARNING ("Invalid object location for data_type 0x%x!",
1018               *(buf - 1));
1019           GST_MEMDUMP ("Remaining data after invalid object location:", buf,
1020               buf_end - buf);
1021           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)
1022         }
1023
1024         if (region->depth < 4) {
1025           GST_WARNING ("4-bit pixel string in %d-bit region!", region->depth);
1026           return;
1027         }
1028
1029         if (region->depth == 8)
1030           map_table = map4to8;
1031         else
1032           map_table = NULL;
1033
1034         GST_LOG ("READ_4BIT_STRING: String data into position %dx%d; "
1035             "buf before is %p", x_pos, y_pos, buf);
1036         // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1037         // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1038         x_pos +=
1039             _dvb_sub_read_4bit_string (pbuf + (y_pos * region->width) + x_pos,
1040             region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
1041         GST_DEBUG ("READ_4BIT_STRING finished: buf pointer now %p", buf);
1042         break;
1043       case 0x12:
1044         if (dest_buf_filled) {
1045           /* FIXME: Be more verbose */
1046           GST_WARNING ("Invalid object location for data_type 0x%x!",
1047               *(buf - 1));
1048           GST_MEMDUMP ("Remaining data after invalid object location:",
1049               buf, (guint) (buf_end - buf));
1050           return;
1051         }
1052
1053         if (region->depth < 8) {
1054           GST_WARNING ("8-bit pixel string in %d-bit region!", region->depth);
1055           return;
1056         }
1057         // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1058         // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1059         x_pos +=
1060             _dvb_sub_read_8bit_string (pbuf + (y_pos * region->width) + x_pos,
1061             region->width - x_pos, &buf, buf_end - buf, non_mod, NULL);
1062         break;
1063
1064       case 0x20:
1065         GST_DEBUG ("handling map2to4 table data");
1066         /* FIXME: I don't see any guards about buffer size here - buf++ happens with the switch, but
1067          * FIXME: buffer is walked without length checks? Same deal in other map table cases */
1068         map2to4[0] = (*buf) >> 4;
1069         map2to4[1] = (*buf++) & 0xf;
1070         map2to4[2] = (*buf) >> 4;
1071         map2to4[3] = (*buf++) & 0xf;
1072         break;
1073       case 0x21:
1074         GST_DEBUG ("handling map2to8 table data");
1075         for (i = 0; i < 4; i++)
1076           map2to8[i] = *buf++;
1077         break;
1078       case 0x22:
1079         GST_DEBUG ("handling map4to8 table data");
1080         for (i = 0; i < 16; i++)
1081           map4to8[i] = *buf++;
1082         break;
1083       case 0xf0:
1084         GST_DEBUG ("end of object line code encountered");
1085         x_pos = display->x_pos;
1086         y_pos += 2;
1087         break;
1088       default:
1089         /* FIXME: Do we consume word align stuffing byte that could follow top/bottom data? */
1090         GST_WARNING ("Unknown/unsupported pixel block 0x%x", *(buf - 1));
1091     }
1092   }
1093 }
1094
1095 static void
1096 _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
1097     gint buf_size)
1098 {
1099   const guint8 *buf_end = buf + buf_size;
1100   guint object_id;
1101   DVBSubObject *object;
1102
1103   guint8 coding_method, non_modifying_color;
1104
1105   object_id = GST_READ_UINT16_BE (buf);
1106   buf += 2;
1107
1108   object = get_object (dvb_sub, object_id);
1109
1110   GST_DEBUG ("OBJECT: a new object segment has occurred for object_id = %u",
1111       object_id);
1112
1113   if (!object) {
1114     GST_WARNING ("Nothing known about object with ID %u yet, bailing out",
1115         object_id);
1116     return;
1117   }
1118
1119   coding_method = ((*buf) >> 2) & 3;
1120   non_modifying_color = ((*buf++) >> 1) & 1;
1121
1122   if (coding_method == 0) {
1123     const guint8 *block;
1124     DVBSubObjectDisplay *display;
1125     guint16 top_field_len, bottom_field_len;
1126
1127     top_field_len = GST_READ_UINT16_BE (buf);
1128     buf += 2;
1129     bottom_field_len = GST_READ_UINT16_BE (buf);
1130     buf += 2;
1131
1132     if (buf + top_field_len + bottom_field_len > buf_end) {
1133       GST_WARNING ("Field data size too large");
1134       return;
1135     }
1136
1137     /* FIXME: Potential optimization opportunity here - parse the object pixmap only once, and copy it to all the
1138      * FIXME: regions that need it. One object being in multiple regions is a rare occurrence in real life, however */
1139     for (display = object->display_list; display;
1140         display = display->object_list_next) {
1141       block = buf;
1142
1143       GST_DEBUG ("OBJECT: parsing top and bottom part of object id %d; "
1144           "top_field_len = %u, bottom_field_len = %u",
1145           display->object_id, top_field_len, bottom_field_len);
1146
1147       _dvb_sub_parse_pixel_data_block (dvb_sub, display, block, top_field_len,
1148           TOP_FIELD, non_modifying_color);
1149
1150       if (bottom_field_len > 0)
1151         block = buf + top_field_len;
1152       else
1153         bottom_field_len = top_field_len;
1154
1155       _dvb_sub_parse_pixel_data_block (dvb_sub, display, block,
1156           bottom_field_len, BOTTOM_FIELD, non_modifying_color);
1157     }
1158
1159   } else if (coding_method == 1) {
1160     GST_FIXME ("'a string of characters' coding method not supported yet!");
1161   } else {
1162     GST_WARNING ("Unknown object coding 0x%x", coding_method);
1163   }
1164 }
1165
1166 static gint
1167 _dvb_sub_parse_display_definition_segment (DvbSub * dvb_sub, guint8 * buf,
1168     gint buf_size)
1169 {
1170   int dds_version, info_byte;
1171   DvbSubPrivate *ctx = dvb_sub->private_data;
1172
1173   if (buf_size < 5)
1174     return -1;
1175
1176   info_byte = *buf++;
1177   dds_version = info_byte >> 4;
1178
1179   if (ctx->display_def.version == dds_version)
1180     return 0;                   /* already have this display definition version */
1181
1182   ctx->display_def.version = dds_version;
1183   ctx->display_def.display_width = GST_READ_UINT16_BE (buf) + 1;
1184   buf += 2;
1185   ctx->display_def.display_height = GST_READ_UINT16_BE (buf) + 1;
1186   buf += 2;
1187
1188   ctx->display_def.window_flag = info_byte & 1 << 3;
1189
1190   if (buf_size >= 13 && ctx->display_def.window_flag) {
1191     ctx->display_def.window_x = GST_READ_UINT16_BE (buf);
1192     buf += 2;
1193     ctx->display_def.window_y = GST_READ_UINT16_BE (buf);
1194     buf += 2;
1195     ctx->display_def.window_width =
1196         GST_READ_UINT16_BE (buf) - ctx->display_def.window_x + 1;
1197     buf += 2;
1198     ctx->display_def.window_height =
1199         GST_READ_UINT16_BE (buf) - ctx->display_def.window_y + 1;
1200     buf += 2;
1201   }
1202
1203   return 0;
1204 }
1205
1206 static gint
1207 _dvb_sub_parse_end_of_display_set (DvbSub * dvb_sub, guint16 page_id,
1208     guint8 * buf, gint buf_size, guint64 pts)
1209 {
1210   DvbSubPrivate *priv = (DvbSubPrivate *) dvb_sub->private_data;
1211
1212   DVBSubtitles *sub = g_slice_new0 (DVBSubtitles);
1213
1214   DVBSubRegion *region;
1215   DVBSubRegionDisplay *display;
1216   DVBSubtitleRect *rect;
1217   DVBSubCLUT *clut;
1218   guint32 *clut_table;
1219   int i;
1220
1221   static unsigned counter = 0;  /* DEBUG use only *//* FIXME: get rid */
1222
1223   GST_DEBUG ("DISPLAY SET END: page_id = %u, length = %d", page_id, buf_size);
1224
1225   sub->rects = NULL;
1226 #if 0                           /* FIXME: PTS stuff not figured out yet */
1227   sub->start_display_time = 0;
1228   sub->end_display_time = priv->page_time_out * 1000;
1229   sub->format = 0;              /* 0 = graphics */
1230 #endif
1231
1232   sub->num_rects = priv->display_list_size;
1233
1234   if (sub->num_rects > 0) {
1235     // FIXME-MEMORY-LEAK: This structure is not freed up yet
1236     sub->rects = g_malloc0 (sizeof (*sub->rects) * sub->num_rects);     /* GSlice? */
1237     for (i = 0; i < sub->num_rects; i++)
1238       sub->rects[i] = g_malloc0 (sizeof (*sub->rects[i]));      /* GSlice? */
1239   }
1240
1241   i = 0;
1242
1243   /* copy subtitle display and window information */
1244   sub->display_def = priv->display_def;
1245
1246   for (display = priv->display_list; display; display = display->next) {
1247     region = get_region (dvb_sub, display->region_id);
1248     rect = sub->rects[i];
1249
1250     if (!region)
1251       continue;
1252
1253     rect->x = display->x_pos;
1254     rect->y = display->y_pos;
1255     rect->w = region->width;
1256     rect->h = region->height;
1257 #if 0                           /* FIXME: Don't think we need to save the number of colors in the palette when we are saving as RGBA? */
1258     rect->nb_colors = 16;
1259 #endif
1260 #if 0                           /* FIXME: Needed to be specified once we support strings of characters based subtitles */
1261     rect->type = SUBTITLE_BITMAP;
1262 #endif
1263     rect->pict.rowstride = region->width;
1264     rect->pict.palette_bits_count = region->depth;
1265
1266     clut = get_clut (dvb_sub, region->clut);
1267
1268     if (!clut)
1269       clut = &default_clut;
1270
1271     switch (region->depth) {
1272       case 2:
1273         clut_table = clut->clut4;
1274         break;
1275       case 8:
1276         clut_table = clut->clut256;
1277         break;
1278       case 4:
1279       default:
1280         clut_table = clut->clut16;
1281         break;
1282     }
1283
1284     /* FIXME: Tweak this to be saved in a format most suitable for Qt and GStreamer instead.
1285      * Currently kept in AVPicture for quick save_display_set testing */
1286     rect->pict.palette = g_malloc ((1 << region->depth) * sizeof (guint32));    /* FIXME: Can we use GSlice here? */
1287     memcpy (rect->pict.palette, clut_table,
1288         (1 << region->depth) * sizeof (guint32));
1289
1290     GST_MEMDUMP ("rect->pict.data.palette content",
1291         (guint8 *) rect->pict.palette, (1 << region->depth) * sizeof (guint32));
1292
1293     rect->pict.data = g_malloc (region->buf_size);      /* FIXME: Can we use GSlice here? */
1294     memcpy (rect->pict.data, region->pbuf, region->buf_size);
1295
1296     ++counter;
1297     GST_DEBUG ("DISPLAY: an object rect created: number %u, iteration %u, "
1298         "pos: %d:%d, size: %dx%d", counter, i, rect->x, rect->y, rect->w,
1299         rect->h);
1300
1301     GST_MEMDUMP ("rect->pict.data content", rect->pict.data, region->buf_size);
1302
1303     ++i;
1304   }
1305
1306   sub->pts = pts;
1307   sub->page_time_out = priv->page_time_out;
1308   sub->num_rects = i;
1309
1310   if (priv->callbacks.new_data) {
1311     priv->callbacks.new_data (dvb_sub, sub, priv->user_data);
1312   } else {
1313     /* No-one responsible to clean up memory, so do it ourselves */
1314     /* FIXME: Just don't bother with all this palette image creation in the first place then... */
1315     dvb_subtitles_free (sub);
1316   }
1317
1318   return 1;                     /* FIXME: The caller of this function is probably supposed to do something with the return value */
1319 }
1320
1321 void
1322 dvb_subtitles_free (DVBSubtitles * sub)
1323 {
1324   int i;
1325   DVBSubtitleRect *rect;
1326
1327   if (sub == NULL)
1328     return;
1329
1330   /* Now free up all the temporary memory we allocated */
1331   for (i = 0; i < sub->num_rects; ++i) {
1332     rect = sub->rects[i];
1333
1334     g_free (rect->pict.palette);
1335     g_free (rect->pict.data);
1336     g_free (rect);
1337   }
1338   g_free (sub->rects);
1339   g_slice_free (DVBSubtitles, sub);
1340 }
1341
1342 DvbSub *
1343 dvb_sub_new (void)
1344 {
1345   static gsize inited = 0;
1346   DvbSubPrivate *sub;
1347   DvbSub *dvb_sub;
1348
1349   if (g_once_init_enter (&inited)) {
1350     dvb_sub_init ();
1351     g_once_init_leave (&inited, TRUE);
1352   }
1353
1354   dvb_sub = g_slice_new0 (DvbSub);
1355   dvb_sub->private_data = &dvb_sub->priv;
1356
1357   sub = dvb_sub->private_data;
1358
1359   /* TODO: Add initialization code here */
1360   /* FIXME: Do we have a reason to initiate the members to zero, or are we guaranteed that anyway? */
1361   sub->region_list = NULL;
1362   sub->object_list = NULL;
1363   sub->page_time_out = 0;       /* FIXME: Maybe 255 instead? */
1364   sub->pes_buffer = g_string_new (NULL);
1365
1366   /* display/window information */
1367   sub->display_def.version = -1;
1368   sub->display_def.window_flag = 0;
1369   sub->display_def.display_width = 720;
1370   sub->display_def.display_height = 576;
1371
1372   return dvb_sub;
1373 }
1374
1375 void
1376 dvb_sub_free (DvbSub * sub)
1377 {
1378   /* TODO: Add deinitalization code here */
1379   /* FIXME: Clear up region_list contents */
1380   delete_state (sub);
1381   g_string_free (sub->private_data->pes_buffer, TRUE);
1382   g_slice_free (DvbSub, sub);
1383 }
1384
1385 #define DVB_SUB_SEGMENT_PAGE_COMPOSITION 0x10
1386 #define DVB_SUB_SEGMENT_REGION_COMPOSITION 0x11
1387 #define DVB_SUB_SEGMENT_CLUT_DEFINITION 0x12
1388 #define DVB_SUB_SEGMENT_OBJECT_DATA 0x13
1389 #define DVB_SUB_SEGMENT_DISPLAY_DEFINITION 0x14
1390 #define DVB_SUB_SEGMENT_END_OF_DISPLAY_SET 0x80
1391 #define DVB_SUB_SEGMENT_STUFFING 0xFF
1392
1393 #define DVB_SUB_SYNC_BYTE 0x0f
1394 /**
1395  * dvb_sub_feed_with_pts:
1396  * @dvb_sub: a #DvbSub
1397  * @pts: The PTS of the data
1398  * @data: The data to feed to the parser
1399  * @len: Length of the data
1400  *
1401  * Feeds the DvbSub parser with new binary data to parse,
1402  * with an associated PTS value. E.g, data left after PES
1403  * packet header has been already parsed, which contains
1404  * the PTS information).
1405  *
1406  * Return value: -1 if data was unhandled (e.g, not a subtitle packet),
1407  *                               -2 if data parsing was unsuccesful (e.g, length was invalid),
1408  *                                0 or positive if data was handled. If positive, then amount of data consumed on success. FIXME: List the positive return values.
1409  */
1410 gint
1411 dvb_sub_feed_with_pts (DvbSub * dvb_sub, guint64 pts, guint8 * data, gint len)
1412 {
1413   unsigned int pos = 0;
1414   guint8 segment_type;
1415   guint16 segment_len;
1416   guint16 page_id;
1417
1418   GST_DEBUG ("pts=%" G_GUINT64_FORMAT " and length %d", pts, len);
1419
1420   g_return_val_if_fail (data != NULL, -1);
1421
1422   if (len <= 3) {               /* len(0x20 0x00 end_of_PES_data_field_marker) */
1423     GST_WARNING ("Data length too short");
1424     return -1;
1425   }
1426
1427   if (data[pos++] != 0x20) {
1428     GST_WARNING ("Tried to handle a PES packet private data that isn't a "
1429         "subtitle packet (does not start with 0x20)");
1430     return -1;
1431   }
1432
1433   if (data[pos++] != 0x00) {
1434     GST_WARNING ("'Subtitle stream in this PES packet' was not 0x00, so this "
1435         "is in theory not a DVB subtitle stream (but some other subtitle "
1436         "standard?); bailing out");
1437     return -1;
1438   }
1439
1440   while (data[pos++] == DVB_SUB_SYNC_BYTE) {
1441     if ((len - pos) < (2 * 2 + 1)) {
1442       GST_WARNING ("Data after SYNC BYTE too short, less than needed to "
1443           "even get to segment_length");
1444       return -2;
1445     }
1446     segment_type = data[pos++];
1447     GST_DEBUG ("=== Segment type is 0x%x", segment_type);
1448     page_id = (data[pos] << 8) | data[pos + 1];
1449     GST_DEBUG ("page_id is 0x%x", page_id);
1450     pos += 2;
1451     segment_len = (data[pos] << 8) | data[pos + 1];
1452     GST_DEBUG ("segment_length is %d (0x%x 0x%x)", segment_len, data[pos],
1453         data[pos + 1]);
1454     pos += 2;
1455     if ((len - pos) < segment_len) {
1456       GST_WARNING ("segment_length was told to be %u, but we only have "
1457           "%d bytes left", segment_len, len - pos);
1458       return -2;
1459     }
1460     // TODO: Parse the segment per type  (this is probably a leftover TODO that is now done?)
1461     /* FIXME: Handle differing PTS values - all segments of a given display set must be with the same PTS,
1462      * FIXME: but we let it slip and just take it for granted in end_of_display_set */
1463     switch (segment_type) {
1464       case DVB_SUB_SEGMENT_PAGE_COMPOSITION:
1465         GST_DEBUG ("Page composition segment at buffer pos %u", pos);
1466         _dvb_sub_parse_page_segment (dvb_sub, page_id, data + pos, segment_len);        /* FIXME: Not sure about args */
1467         break;
1468       case DVB_SUB_SEGMENT_REGION_COMPOSITION:
1469         GST_DEBUG ("Region composition segment at buffer pos %u", pos);
1470         _dvb_sub_parse_region_segment (dvb_sub, page_id, data + pos, segment_len);      /* FIXME: Not sure about args */
1471         break;
1472       case DVB_SUB_SEGMENT_CLUT_DEFINITION:
1473         GST_DEBUG ("CLUT definition segment at buffer pos %u", pos);
1474         _dvb_sub_parse_clut_segment (dvb_sub, page_id, data + pos, segment_len);        /* FIXME: Not sure about args */
1475         break;
1476       case DVB_SUB_SEGMENT_OBJECT_DATA:
1477         GST_DEBUG ("Object data segment at buffer pos %u", pos);
1478         _dvb_sub_parse_object_segment (dvb_sub, page_id, data + pos, segment_len);      /* FIXME: Not sure about args */
1479         break;
1480       case DVB_SUB_SEGMENT_DISPLAY_DEFINITION:
1481         GST_DEBUG ("display definition segment at buffer pos %u", pos);
1482         _dvb_sub_parse_display_definition_segment (dvb_sub, data + pos,
1483             segment_len);
1484         break;
1485       case DVB_SUB_SEGMENT_END_OF_DISPLAY_SET:
1486         GST_DEBUG ("End of display set at buffer pos %u", pos);
1487         _dvb_sub_parse_end_of_display_set (dvb_sub, page_id, data + pos, segment_len, pts);     /* FIXME: Not sure about args */
1488         break;
1489       default:
1490         GST_FIXME ("Unhandled segment type 0x%x", segment_type);
1491         break;
1492     }
1493
1494     pos += segment_len;
1495
1496     if (pos == len) {
1497       GST_WARNING ("Data ended without a PES data end marker");
1498       return 1;
1499     }
1500   }
1501
1502   GST_LOG ("Processed %d bytes out of %d", pos, len);
1503   return pos;
1504 }
1505
1506 /**
1507  * dvb_sub_set_callbacks:
1508  * @dvb_sub: a #DvbSub
1509  * @callbacks: the callbacks to install
1510  * @user_data: a user_data argument for the callback
1511  *
1512  * Set callback which will be executed when new subpictures are available.
1513  */
1514 void
1515 dvb_sub_set_callbacks (DvbSub * dvb_sub, DvbSubCallbacks * callbacks,
1516     gpointer user_data)
1517 {
1518   DvbSubPrivate *priv;
1519
1520   g_return_if_fail (dvb_sub != NULL);
1521   g_return_if_fail (callbacks != NULL);
1522
1523   priv = (DvbSubPrivate *) dvb_sub->private_data;
1524
1525   priv->callbacks = *callbacks;
1526   priv->user_data = user_data;
1527 }