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