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