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