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