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