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