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