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