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 * @title: GstDvbSub
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 AYUV(y,u,v,a) (((a) << 24) | ((y) << 16) | ((u) << 8) | (v))
57 #define RGBA_TO_AYUV(r,g,b,a) ((((guint32)(a)) << 24) | ((rgb_to_y(r,g,b)) << 16) | ((rgb_to_u(r,g,b)) << 8) | (rgb_to_v(r,g,b)))
58
59
60 typedef struct DVBSubCLUT
61 {
62 int id; /* default_clut uses -1 for this, so guint8 isn't fine without adaptations first */
63
64 guint32 clut4[4];
65 guint32 clut16[16];
66 guint32 clut256[256];
67
68 struct DVBSubCLUT *next;
69 } DVBSubCLUT;
70
71 static DVBSubCLUT default_clut;
72
73 typedef struct DVBSubObjectDisplay
74 {
75 /* FIXME: Use more correct sizes */
76 int object_id;
77 int region_id;
78
79 int x_pos;
80 int y_pos;
81
82 int fgcolor;
83 int bgcolor;
84
85 /* FIXME: Should we use GSList? The relating interaction and pointer assigment is quite complex and perhaps unsuited for a plain GSList anyway */
86 struct DVBSubObjectDisplay *region_list_next;
87 struct DVBSubObjectDisplay *object_list_next;
88 } DVBSubObjectDisplay;
89
90 typedef struct DVBSubObject
91 {
92 /* FIXME: Use more correct sizes */
93 int id; /* FIXME: Use guint8 after checking it's fine in all code using it */
94
95 int type;
96
97 /* FIXME: Should we use GSList? */
98 DVBSubObjectDisplay *display_list;
99 struct DVBSubObject *next;
100 } DVBSubObject;
101
102 typedef struct DVBSubRegionDisplay
103 { /* FIXME: Figure out if this structure is only used temporarily in page_segment parser, or also more */
104 int region_id;
105
106 int x_pos;
107 int y_pos;
108
109 struct DVBSubRegionDisplay *next;
110 } DVBSubRegionDisplay;
111
112 typedef struct DVBSubRegion
113 {
114 guint8 id;
115 guint16 width;
116 guint16 height;
117 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 */
118
119 guint8 clut;
120 guint8 bgcolor;
121
122 /* FIXME: Validate these fields existence and exact types */
123 guint8 *pbuf;
124 int buf_size;
125
126 DVBSubObjectDisplay *display_list;
127
128 struct DVBSubRegion *next;
129 } DVBSubRegion;
130
131 struct _DvbSub
132 {
133 DvbSubCallbacks callbacks;
134 gpointer user_data;
135
136 guint8 page_time_out;
137 DVBSubRegion *region_list;
138 DVBSubCLUT *clut_list;
139 DVBSubObject *object_list;
140 /* FIXME... */
141 int display_list_size;
142 DVBSubRegionDisplay *display_list;
143 GString *pes_buffer;
144 DVBSubtitleWindow display_def;
145 };
146
147 typedef enum
148 {
149 TOP_FIELD = 0,
150 BOTTOM_FIELD = 1
151 } DvbSubPixelDataSubBlockFieldType;
152
153 static inline gint
rgb_to_y(gint r,gint g,gint b)154 rgb_to_y (gint r, gint g, gint b)
155 {
156 gint ret;
157
158 ret = (gint) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16));
159 ret = CLAMP (ret, 0, 255);
160 return ret;
161 }
162
163 static inline gint
rgb_to_u(gint r,gint g,gint b)164 rgb_to_u (gint r, gint g, gint b)
165 {
166 gint ret;
167
168 ret =
169 (gint) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) +
170 128);
171 ret = CLAMP (ret, 0, 255);
172 return ret;
173 }
174
175 static inline gint
rgb_to_v(gint r,gint g,gint b)176 rgb_to_v (gint r, gint g, gint b)
177 {
178 gint ret;
179
180 ret =
181 (gint) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) +
182 128);
183 ret = CLAMP (ret, 0, 255);
184 return ret;
185 }
186
187 static DVBSubObject *
get_object(DvbSub * dvb_sub,guint16 object_id)188 get_object (DvbSub * dvb_sub, guint16 object_id)
189 {
190 DVBSubObject *ptr = dvb_sub->object_list;
191
192 while (ptr && ptr->id != object_id) {
193 ptr = ptr->next;
194 }
195
196 return ptr;
197 }
198
199 static DVBSubCLUT *
get_clut(DvbSub * dvb_sub,gint clut_id)200 get_clut (DvbSub * dvb_sub, gint clut_id)
201 {
202 DVBSubCLUT *ptr = dvb_sub->clut_list;
203
204 while (ptr && ptr->id != clut_id) {
205 ptr = ptr->next;
206 }
207
208 return ptr;
209 }
210
211 static DVBSubRegion *
get_region(DvbSub * dvb_sub,guint8 region_id)212 get_region (DvbSub * dvb_sub, guint8 region_id)
213 {
214 DVBSubRegion *ptr = dvb_sub->region_list;
215
216 while (ptr && ptr->id != region_id) {
217 ptr = ptr->next;
218 }
219
220 return ptr;
221 }
222
223 static void
delete_region_display_list(DvbSub * dvb_sub,DVBSubRegion * region)224 delete_region_display_list (DvbSub * dvb_sub, DVBSubRegion * region)
225 {
226 DVBSubObject *object, *obj2;
227 DVBSubObject **obj2_ptr;
228 DVBSubObjectDisplay *display, *obj_disp, **obj_disp_ptr;
229
230 while (region->display_list) {
231 display = region->display_list;
232
233 object = get_object (dvb_sub, display->object_id);
234
235 if (object) {
236 obj_disp_ptr = &object->display_list;
237 obj_disp = *obj_disp_ptr;
238
239 while (obj_disp && obj_disp != display) {
240 obj_disp_ptr = &obj_disp->object_list_next;
241 obj_disp = *obj_disp_ptr;
242 }
243
244 if (obj_disp) {
245 *obj_disp_ptr = obj_disp->object_list_next;
246
247 if (!object->display_list) {
248 obj2_ptr = (DVBSubObject **) & dvb_sub->object_list; /* FIXME: Evil casting */
249 obj2 = *obj2_ptr;
250
251 while (obj2 != object) {
252 g_assert (obj2);
253 obj2_ptr = &obj2->next;
254 obj2 = *obj2_ptr;
255 }
256
257 *obj2_ptr = obj2->next;
258
259 g_slice_free (DVBSubObject, obj2);
260 }
261 }
262 }
263
264 region->display_list = display->region_list_next;
265
266 g_slice_free (DVBSubObjectDisplay, display);
267 }
268 }
269
270 static void
delete_state(DvbSub * dvb_sub)271 delete_state (DvbSub * dvb_sub)
272 {
273 DVBSubRegion *region;
274
275 while (dvb_sub->region_list) {
276 region = dvb_sub->region_list;
277
278 dvb_sub->region_list = region->next;
279
280 delete_region_display_list (dvb_sub, region);
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
dvb_sub_init(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
_dvb_sub_parse_page_segment(DvbSub * dvb_sub,guint16 page_id,guint8 * buf,gint buf_size)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
_dvb_sub_parse_region_segment(DvbSub * dvb_sub,guint16 page_id,guint8 * buf,gint buf_size)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 g_free (region->pbuf);
476
477 region->buf_size = region->width * region->height;
478
479 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 */
480
481 fill = 1; /* FIXME: Validate from spec that fill is forced on (in the following codes context) when dimensions change */
482 }
483
484 region->depth = 1 << (((*buf++) >> 2) & 7);
485 if (region->depth < 2 || region->depth > 8) {
486 GST_WARNING ("region depth %d is invalid", region->depth);
487 region->depth = 4; /* FIXME: Check from spec this is the default? */
488 }
489
490 region->clut = *buf++;
491
492 if (region->depth == 8) {
493 region->bgcolor = *buf++;
494 buf += 1; /* Skip undefined 4-bit and 2-bit field */
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
_dvb_sub_parse_clut_segment(DvbSub * dvb_sub,guint16 page_id,guint8 * buf,gint buf_size)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
_dvb_sub_read_2bit_string(guint8 * destbuf,gint dbuf_len,const guint8 ** srcbuf,gint buf_size,guint8 non_mod,guint8 * map_table)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 GST_TRACE ("dbuf_len = %d", dbuf_len);
647
648 /* Need at least 2 bits remaining */
649 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 1)) {
650 guint run_length = 0, clut_index = 0;
651
652 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
653
654 if (bits) { /* 2-bit_pixel-code */
655 run_length = 1;
656 clut_index = bits;
657 } else { /* 2-bit_zero */
658 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1);
659 if (bits == 1) { /* switch_1 == '1' */
660 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 3);
661 run_length += 3;
662 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
663 } else { /* switch_1 == '0' */
664 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1);
665 if (bits == 1) { /* switch_2 == '1' */
666 run_length = 1; /* 1x pseudo-colour '00' */
667 } else { /* switch_2 == '0' */
668 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
669 switch (bits) { /* switch_3 */
670 case 0x0: /* end of 2-bit/pixel_code_string */
671 stop_parsing = TRUE;
672 break;
673 case 0x1: /* two pixels shall be set to pseudo colour (entry) '00' */
674 run_length = 2;
675 break;
676 case 0x2: /* the following 6 bits contain run length coded pixel data */
677 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
678 run_length += 12;
679 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
680 break;
681 case 0x3: /* the following 10 bits contain run length coded pixel data */
682 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8);
683 run_length += 29;
684 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
685 break;
686 }
687 }
688 }
689 }
690
691 /* If run_length is zero, continue. Only case happening is when
692 * stop_parsing is TRUE too, so next cycle shouldn't run */
693 if (run_length == 0)
694 continue;
695
696 /* Trim the run_length to not go beyond the line end and consume
697 * it from remaining length of dest line */
698 run_length = MIN (run_length, dbuf_len);
699 dbuf_len -= run_length;
700
701 /* Make clut_index refer to the index into the desired bit depths
702 * CLUT definition table */
703 if (map_table)
704 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
705
706 /* Now we can simply memset run_length count of destination bytes
707 * to clut_index, but only if not non_modifying */
708 GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer, "
709 "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
710
711 if (!(non_mod == 1 && clut_index == 1))
712 memset (destbuf, clut_index, run_length);
713
714 destbuf += run_length;
715 pixels_read += run_length;
716 }
717
718 // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
719 //gst_bit_reader_skip_to_byte (&gb);
720 *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
721
722 GST_TRACE ("PIXEL: returning, read %u pixels", pixels_read);
723 // FIXME: Shouldn't need this variable if tracking things in the loop better
724 return pixels_read;
725 }
726
727 // FFMPEG-FIXME: The same code in ffmpeg is much more complex, it could use the same
728 // FFMPEG-FIXME: refactoring as done here, explained in commit 895296c3
729 static int
_dvb_sub_read_4bit_string(guint8 * destbuf,gint dbuf_len,const guint8 ** srcbuf,gint buf_size,guint8 non_mod,guint8 * map_table)730 _dvb_sub_read_4bit_string (guint8 * destbuf, gint dbuf_len,
731 const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
732 {
733 GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
734 /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
735 gboolean stop_parsing = FALSE;
736 guint32 bits = 0;
737 guint32 pixels_read = 0;
738
739 GST_TRACE ("RUNLEN: srcbuf position %p, buf_size = %d; destination buffer "
740 "size is %d @ %p", *srcbuf, buf_size, dbuf_len, destbuf);
741
742 /* Need at least 4 bits */
743 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 3)) {
744 guint run_length = 0, clut_index = 0;
745
746 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
747
748 if (bits) {
749 run_length = 1;
750 clut_index = bits;
751 } else {
752 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1);
753 if (bits == 0) { /* switch_1 == '0' */
754 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 3);
755 if (!run_length) {
756 stop_parsing = TRUE;
757 } else {
758 run_length += 2;
759 }
760 } else { /* switch_1 == '1' */
761 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1);
762 if (bits == 0) { /* switch_2 == '0' */
763 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
764 run_length += 4;
765 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
766 } else { /* switch_2 == '1' */
767 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 2);
768 switch (bits) {
769 case 0x0: /* switch_3 == '00' */
770 run_length = 1; /* 1 pixel of pseudo-color 0 */
771 break;
772 case 0x1: /* switch_3 == '01' */
773 run_length = 2; /* 2 pixels of pseudo-color 0 */
774 break;
775 case 0x2: /* switch_3 == '10' */
776 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
777 run_length += 9;
778 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
779 break;
780 case 0x3: /* switch_3 == '11' */
781 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8);
782 run_length += 25;
783 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 4);
784 break;
785 }
786 }
787 }
788 }
789
790 /* If run_length is zero, continue. Only case happening is when
791 * stop_parsing is TRUE too, so next cycle shouldn't run */
792 if (run_length == 0)
793 continue;
794
795 /* Trim the run_length to not go beyond the line end and consume
796 * it from remaining length of dest line */
797 run_length = MIN (run_length, dbuf_len);
798 dbuf_len -= run_length;
799
800 /* Make clut_index refer to the index into the desired bit depths
801 * CLUT definition table */
802 if (map_table)
803 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
804
805 /* Now we can simply memset run_length count of destination bytes
806 * to clut_index, but only if not non_modifying */
807 GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer; "
808 "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
809
810 if (!(non_mod == 1 && clut_index == 1))
811 memset (destbuf, clut_index, run_length);
812
813 destbuf += run_length;
814 pixels_read += run_length;
815 }
816
817 // FIXME: Test skip_to_byte instead of adding 7 bits, once everything else is working good
818 //gst_bit_reader_skip_to_byte (&gb);
819 *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
820
821 GST_LOG ("Returning with %u pixels read", pixels_read);
822
823 // FIXME: Shouldn't need this variable if tracking things in the loop better
824 return pixels_read;
825 }
826
827 static int
_dvb_sub_read_8bit_string(guint8 * destbuf,gint dbuf_len,const guint8 ** srcbuf,gint buf_size,guint8 non_mod,guint8 * map_table)828 _dvb_sub_read_8bit_string (guint8 * destbuf, gint dbuf_len,
829 const guint8 ** srcbuf, gint buf_size, guint8 non_mod, guint8 * map_table)
830 {
831 GstBitReader gb = GST_BIT_READER_INIT (*srcbuf, buf_size);
832 /* FIXME: Handle FALSE returns from gst_bit_reader_get_* calls? */
833
834 gboolean stop_parsing = FALSE;
835 guint32 bits = 0;
836 guint32 pixels_read = 0;
837
838 GST_LOG ("dbuf_len = %d", dbuf_len);
839
840 /* FFMPEG-FIXME: ffmpeg uses a manual byte walking algorithm, which might be more performant,
841 * FFMPEG-FIXME: but it does almost absolutely no buffer length checking, so could walk over
842 * FFMPEG-FIXME: memory boundaries. While we don't check gst_bit_reader_get_bits_uint32
843 * FFMPEG-FIXME: return values either and therefore might get some pixels corrupted, we at
844 * FFMPEG-FIXME: lest have no chance of reading memory we don't own and visual corruption
845 * FFMPEG-FIXME: is guaranteed anyway when not all bytes are present */
846 /* Rephrased - it's better to work with bytes with default value '0' instead of reading from memory we don't own. */
847 while (!stop_parsing && (gst_bit_reader_get_remaining (&gb) > 7)) {
848 guint run_length = 0, clut_index = 0;
849 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8);
850
851 if (bits) { /* 8-bit_pixel-code */
852 run_length = 1;
853 clut_index = bits;
854 } else { /* 8-bit_zero */
855 bits = gst_bit_reader_get_bits_uint32_unchecked (&gb, 1);
856 if (bits == 0) { /* switch_1 == '0' */
857 /* run_length_1-127 for pseudo-colour _entry) '0x00' */
858 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 7);
859 if (run_length == 0) { /* end_of_string_signal */
860 stop_parsing = TRUE;
861 }
862 } else { /* switch_1 == '1' */
863 /* run_length_3-127 */
864 run_length = gst_bit_reader_get_bits_uint32_unchecked (&gb, 7);
865 clut_index = gst_bit_reader_get_bits_uint32_unchecked (&gb, 8);
866
867 if (run_length < 3) {
868 GST_WARNING ("runlength value was %u, but the spec requires it "
869 "must be >=3", run_length);
870 }
871 }
872 }
873
874 /* If run_length is zero, continue. Only case happening is when
875 * stop_parsing is TRUE too, so next cycle shouldn't run */
876 if (run_length == 0)
877 continue;
878
879 /* Trim the run_length to not go beyond the line end and consume
880 * it from remaining length of dest line */
881 run_length = MIN (run_length, dbuf_len);
882 dbuf_len -= run_length;
883
884 /* Make clut_index refer to the index into the desired bit depths
885 * CLUT definition table */
886 if (map_table)
887 clut_index = map_table[clut_index]; /* now clut_index signifies the index into map_table dest */
888
889 /* Now we can simply memset run_length count of destination bytes
890 * to clut_index, but only if not non_modifying */
891 GST_TRACE ("RUNLEN: setting %u pixels to color 0x%x in destination buffer; "
892 "dbuf_len left is %d pixels", run_length, clut_index, dbuf_len);
893
894 if (!(non_mod == 1 && clut_index == 1))
895 memset (destbuf, clut_index, run_length);
896
897 destbuf += run_length;
898 pixels_read += run_length;
899 }
900
901 GST_LOG ("Returning with %u pixels read", pixels_read);
902
903 *srcbuf += (gst_bit_reader_get_pos (&gb) + 7) >> 3;
904
905 // FIXME: Shouldn't need this variable if tracking things in the loop better
906 return pixels_read;
907 }
908
909 static void
_dvb_sub_parse_pixel_data_block(DvbSub * dvb_sub,DVBSubObjectDisplay * display,const guint8 * buf,gint buf_size,DvbSubPixelDataSubBlockFieldType top_bottom,guint8 non_mod)910 _dvb_sub_parse_pixel_data_block (DvbSub * dvb_sub,
911 DVBSubObjectDisplay * display, const guint8 * buf, gint buf_size,
912 DvbSubPixelDataSubBlockFieldType top_bottom, guint8 non_mod)
913 {
914 DVBSubRegion *region = get_region (dvb_sub, display->region_id);
915 const guint8 *buf_end = buf + buf_size;
916 guint8 *pbuf;
917 int x_pos, y_pos;
918 int i;
919 gboolean dest_buf_filled = FALSE;
920
921 guint8 map2to4[] = { 0x0, 0x7, 0x8, 0xf };
922 guint8 map2to8[] = { 0x00, 0x77, 0x88, 0xff };
923 guint8 map4to8[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
924 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
925 };
926 guint8 *map_table;
927
928 GST_LOG ("DVB pixel block size %d, %s field:", buf_size,
929 top_bottom ? "bottom" : "top");
930
931 GST_MEMDUMP ("packet", buf, buf_size);
932
933 if (region == NULL) {
934 GST_LOG ("Region is NULL, returning");
935 return;
936 }
937
938 pbuf = region->pbuf;
939
940 x_pos = display->x_pos;
941 y_pos = display->y_pos;
942
943 if ((y_pos & 1) != top_bottom)
944 y_pos++;
945
946 while (buf < buf_end) {
947 GST_LOG ("Iteration start, %u bytes remaining; buf = %p, "
948 "buf_end = %p; Region is number %u, with a dimension of %dx%d; "
949 "We are at position %dx%d", (guint) (buf_end - buf), buf, buf_end,
950 region->id, region->width, region->height, x_pos, y_pos);
951
952 // FFMPEG-FIXME: ffmpeg doesn't check for equality and so can overflow destination buffer later on with bad input data
953 // 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
954 // FIXME: Removed x_pos checking here, because we don't want to turn dest_buf_filled to TRUE permanently in that case
955 // FIXME: We assume that region->width - x_pos as dbuf_len to read_nbit_string will take care of that case nicely;
956 // FIXME: That is, that read_nbit_string never scribbles anything if dbuf_len passed to it is zero due to this.
957 if (y_pos >= region->height) {
958 dest_buf_filled = TRUE;
959 }
960
961 switch (*buf++) {
962 case 0x10:
963 if (dest_buf_filled) {
964 /* FIXME: Be more verbose */
965 GST_WARNING ("Invalid object location for data_type 0x%x!",
966 *(buf - 1));
967 GST_MEMDUMP ("Remaining data after invalid object location:", buf,
968 (guint) (buf_end - buf));
969 return;
970 }
971
972 if (region->depth == 8)
973 map_table = map2to8;
974 else if (region->depth == 4)
975 map_table = map2to4;
976 else
977 map_table = NULL;
978
979 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
980 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
981 x_pos +=
982 _dvb_sub_read_2bit_string (pbuf + (y_pos * region->width) + x_pos,
983 region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
984 break;
985 case 0x11:
986 if (dest_buf_filled) {
987 /* FIXME: Be more verbose */
988 GST_WARNING ("Invalid object location for data_type 0x%x!",
989 *(buf - 1));
990 GST_MEMDUMP ("Remaining data after invalid object location:", buf,
991 buf_end - buf);
992 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)
993 }
994
995 if (region->depth < 4) {
996 GST_WARNING ("4-bit pixel string in %d-bit region!", region->depth);
997 return;
998 }
999
1000 if (region->depth == 8)
1001 map_table = map4to8;
1002 else
1003 map_table = NULL;
1004
1005 GST_LOG ("READ_4BIT_STRING: String data into position %dx%d; "
1006 "buf before is %p", x_pos, y_pos, buf);
1007 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1008 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1009 x_pos +=
1010 _dvb_sub_read_4bit_string (pbuf + (y_pos * region->width) + x_pos,
1011 region->width - x_pos, &buf, buf_end - buf, non_mod, map_table);
1012 GST_DEBUG ("READ_4BIT_STRING finished: buf pointer now %p", buf);
1013 break;
1014 case 0x12:
1015 if (dest_buf_filled) {
1016 /* FIXME: Be more verbose */
1017 GST_WARNING ("Invalid object location for data_type 0x%x!",
1018 *(buf - 1));
1019 GST_MEMDUMP ("Remaining data after invalid object location:",
1020 buf, (guint) (buf_end - buf));
1021 return;
1022 }
1023
1024 if (region->depth < 8) {
1025 GST_WARNING ("8-bit pixel string in %d-bit region!", region->depth);
1026 return;
1027 }
1028 // FFMPEG-FIXME: ffmpeg code passes buf_size instead of buf_end - buf, and could
1029 // FFMPEG-FIXME: therefore potentially walk over the memory area we own
1030 x_pos +=
1031 _dvb_sub_read_8bit_string (pbuf + (y_pos * region->width) + x_pos,
1032 region->width - x_pos, &buf, buf_end - buf, non_mod, NULL);
1033 break;
1034
1035 case 0x20:
1036 GST_DEBUG ("handling map2to4 table data");
1037 /* FIXME: I don't see any guards about buffer size here - buf++ happens with the switch, but
1038 * FIXME: buffer is walked without length checks? Same deal in other map table cases */
1039 map2to4[0] = (*buf) >> 4;
1040 map2to4[1] = (*buf++) & 0xf;
1041 map2to4[2] = (*buf) >> 4;
1042 map2to4[3] = (*buf++) & 0xf;
1043 break;
1044 case 0x21:
1045 GST_DEBUG ("handling map2to8 table data");
1046 for (i = 0; i < 4; i++)
1047 map2to8[i] = *buf++;
1048 break;
1049 case 0x22:
1050 GST_DEBUG ("handling map4to8 table data");
1051 for (i = 0; i < 16; i++)
1052 map4to8[i] = *buf++;
1053 break;
1054 case 0xf0:
1055 GST_DEBUG ("end of object line code encountered");
1056 x_pos = display->x_pos;
1057 y_pos += 2;
1058 break;
1059 default:
1060 /* FIXME: Do we consume word align stuffing byte that could follow top/bottom data? */
1061 GST_WARNING ("Unknown/unsupported pixel block 0x%x", *(buf - 1));
1062 }
1063 }
1064 }
1065
1066 static void
_dvb_sub_parse_object_segment(DvbSub * dvb_sub,guint16 page_id,guint8 * buf,gint buf_size)1067 _dvb_sub_parse_object_segment (DvbSub * dvb_sub, guint16 page_id, guint8 * buf,
1068 gint buf_size)
1069 {
1070 const guint8 *buf_end = buf + buf_size;
1071 guint object_id;
1072 DVBSubObject *object;
1073
1074 guint8 coding_method, non_modifying_color;
1075
1076 object_id = GST_READ_UINT16_BE (buf);
1077 buf += 2;
1078
1079 object = get_object (dvb_sub, object_id);
1080
1081 GST_DEBUG ("OBJECT: a new object segment has occurred for object_id = %u",
1082 object_id);
1083
1084 if (!object) {
1085 GST_WARNING ("Nothing known about object with ID %u yet, bailing out",
1086 object_id);
1087 return;
1088 }
1089
1090 coding_method = ((*buf) >> 2) & 3;
1091 non_modifying_color = ((*buf++) >> 1) & 1;
1092
1093 if (coding_method == 0) {
1094 const guint8 *block;
1095 DVBSubObjectDisplay *display;
1096 guint16 top_field_len, bottom_field_len;
1097
1098 top_field_len = GST_READ_UINT16_BE (buf);
1099 buf += 2;
1100 bottom_field_len = GST_READ_UINT16_BE (buf);
1101 buf += 2;
1102
1103 if (buf + top_field_len + bottom_field_len > buf_end) {
1104 GST_WARNING ("Field data size too large");
1105 return;
1106 }
1107
1108 /* FIXME: Potential optimization opportunity here - parse the object pixmap only once, and copy it to all the
1109 * FIXME: regions that need it. One object being in multiple regions is a rare occurrence in real life, however */
1110 for (display = object->display_list; display;
1111 display = display->object_list_next) {
1112 block = buf;
1113
1114 GST_DEBUG ("OBJECT: parsing top and bottom part of object id %d; "
1115 "top_field_len = %u, bottom_field_len = %u",
1116 display->object_id, top_field_len, bottom_field_len);
1117
1118 _dvb_sub_parse_pixel_data_block (dvb_sub, display, block, top_field_len,
1119 TOP_FIELD, non_modifying_color);
1120
1121 if (bottom_field_len > 0)
1122 block = buf + top_field_len;
1123 else
1124 bottom_field_len = top_field_len;
1125
1126 _dvb_sub_parse_pixel_data_block (dvb_sub, display, block,
1127 bottom_field_len, BOTTOM_FIELD, non_modifying_color);
1128 }
1129
1130 } else if (coding_method == 1) {
1131 GST_FIXME ("'a string of characters' coding method not supported yet!");
1132 } else {
1133 GST_WARNING ("Unknown object coding 0x%x", coding_method);
1134 }
1135 }
1136
1137 static gint
_dvb_sub_parse_display_definition_segment(DvbSub * dvb_sub,guint8 * buf,gint buf_size)1138 _dvb_sub_parse_display_definition_segment (DvbSub * dvb_sub, guint8 * buf,
1139 gint buf_size)
1140 {
1141 int dds_version, info_byte;
1142
1143 if (buf_size < 5)
1144 return -1;
1145
1146 info_byte = *buf++;
1147 dds_version = info_byte >> 4;
1148
1149 if (dvb_sub->display_def.version == dds_version)
1150 return 0; /* already have this display definition version */
1151
1152 dvb_sub->display_def.version = dds_version;
1153 dvb_sub->display_def.display_width = GST_READ_UINT16_BE (buf) + 1;
1154 buf += 2;
1155 dvb_sub->display_def.display_height = GST_READ_UINT16_BE (buf) + 1;
1156 buf += 2;
1157
1158 dvb_sub->display_def.window_flag = info_byte & 1 << 3;
1159
1160 if (buf_size >= 13 && dvb_sub->display_def.window_flag) {
1161 dvb_sub->display_def.window_x = GST_READ_UINT16_BE (buf);
1162 buf += 2;
1163 dvb_sub->display_def.window_width =
1164 GST_READ_UINT16_BE (buf) - dvb_sub->display_def.window_x + 1;
1165 buf += 2;
1166 dvb_sub->display_def.window_y = GST_READ_UINT16_BE (buf);
1167 buf += 2;
1168 dvb_sub->display_def.window_height =
1169 GST_READ_UINT16_BE (buf) - dvb_sub->display_def.window_y + 1;
1170 }
1171
1172 return 0;
1173 }
1174
1175 static gint
_dvb_sub_parse_end_of_display_set(DvbSub * dvb_sub,guint16 page_id,guint64 pts)1176 _dvb_sub_parse_end_of_display_set (DvbSub * dvb_sub, guint16 page_id,
1177 guint64 pts)
1178 {
1179 DVBSubRegionDisplay *display;
1180 DVBSubtitles *sub;
1181 DVBSubCLUT *clut;
1182 guint32 *clut_table;
1183 int i;
1184
1185 GST_DEBUG ("DISPLAY SET END: page_id = %u", page_id);
1186
1187 sub = g_slice_new0 (DVBSubtitles);
1188
1189 #if 0 /* FIXME: PTS stuff not figured out yet */
1190 sub->start_display_time = 0;
1191 sub->end_display_time = priv->page_time_out * 1000;
1192 sub->format = 0; /* 0 = graphics */
1193 #endif
1194
1195 /* N.B. g_new0() will return NULL if num_rects is 0 */
1196 sub->num_rects = dvb_sub->display_list_size;
1197 sub->rects = g_new0 (DVBSubtitleRect, sub->num_rects);
1198
1199 i = 0;
1200
1201 /* copy subtitle display and window information */
1202 sub->display_def = dvb_sub->display_def;
1203
1204 for (display = dvb_sub->display_list; display; display = display->next) {
1205 DVBSubtitleRect *rect;
1206 DVBSubRegion *region;
1207
1208 region = get_region (dvb_sub, display->region_id);
1209
1210 if (!region)
1211 continue;
1212
1213 rect = &sub->rects[i];
1214 rect->x = display->x_pos;
1215 rect->y = display->y_pos;
1216 rect->w = region->width;
1217 rect->h = region->height;
1218 #if 0 /* FIXME: Don't think we need to save the number of colors in the palette when we are saving as RGBA? */
1219 rect->nb_colors = 16;
1220 #endif
1221 #if 0 /* FIXME: Needed to be specified once we support strings of characters based subtitles */
1222 rect->type = SUBTITLE_BITMAP;
1223 #endif
1224 rect->pict.rowstride = region->width;
1225 rect->pict.palette_bits_count = region->depth;
1226
1227 clut = get_clut (dvb_sub, region->clut);
1228
1229 if (!clut)
1230 clut = &default_clut;
1231
1232 switch (region->depth) {
1233 case 2:
1234 clut_table = clut->clut4;
1235 break;
1236 case 8:
1237 clut_table = clut->clut256;
1238 break;
1239 case 4:
1240 default:
1241 clut_table = clut->clut16;
1242 break;
1243 }
1244
1245 /* FIXME: Tweak this to be saved in a format most suitable for Qt and GStreamer instead.
1246 * Currently kept in AVPicture for quick save_display_set testing */
1247 rect->pict.palette = g_malloc ((1 << region->depth) * sizeof (guint32)); /* FIXME: Can we use GSlice here? */
1248 memcpy (rect->pict.palette, clut_table,
1249 (1 << region->depth) * sizeof (guint32));
1250
1251 GST_MEMDUMP ("rect->pict.data.palette content",
1252 (guint8 *) rect->pict.palette, (1 << region->depth) * sizeof (guint32));
1253
1254 rect->pict.data = g_malloc (region->buf_size); /* FIXME: Can we use GSlice here? */
1255 memcpy (rect->pict.data, region->pbuf, region->buf_size);
1256
1257 GST_DEBUG ("DISPLAY: an object rect created: iteration %u, "
1258 "pos: %d:%d, size: %dx%d", i, rect->x, rect->y, rect->w, rect->h);
1259
1260 GST_MEMDUMP ("rect->pict.data content", rect->pict.data, region->buf_size);
1261
1262 ++i;
1263 }
1264
1265 sub->pts = pts;
1266 sub->page_time_out = dvb_sub->page_time_out;
1267 sub->num_rects = i;
1268
1269 if (dvb_sub->callbacks.new_data) {
1270 dvb_sub->callbacks.new_data (dvb_sub, sub, dvb_sub->user_data);
1271 } else {
1272 /* No-one responsible to clean up memory, so do it ourselves */
1273 /* FIXME: Just don't bother with all this palette image creation in the first place then... */
1274 dvb_subtitles_free (sub);
1275 }
1276
1277 return 1; /* FIXME: The caller of this function is probably supposed to do something with the return value */
1278 }
1279
1280 void
dvb_subtitles_free(DVBSubtitles * sub)1281 dvb_subtitles_free (DVBSubtitles * sub)
1282 {
1283 int i;
1284
1285 if (sub == NULL)
1286 return;
1287
1288 /* Now free up all the temporary memory we allocated */
1289 for (i = 0; i < sub->num_rects; ++i) {
1290 g_free (sub->rects[i].pict.palette);
1291 g_free (sub->rects[i].pict.data);
1292 }
1293 g_free (sub->rects);
1294 g_slice_free (DVBSubtitles, sub);
1295 }
1296
1297 DvbSub *
dvb_sub_new(void)1298 dvb_sub_new (void)
1299 {
1300 static gsize inited = 0;
1301 DvbSub *sub;
1302
1303 if (g_once_init_enter (&inited)) {
1304 dvb_sub_init ();
1305 g_once_init_leave (&inited, TRUE);
1306 }
1307
1308 sub = g_slice_new0 (DvbSub);
1309
1310 /* TODO: Add initialization code here */
1311 /* FIXME: Do we have a reason to initiate the members to zero, or are we guaranteed that anyway? */
1312 sub->region_list = NULL;
1313 sub->object_list = NULL;
1314 sub->page_time_out = 0; /* FIXME: Maybe 255 instead? */
1315 sub->pes_buffer = g_string_new (NULL);
1316
1317 /* display/window information */
1318 sub->display_def.version = -1;
1319 sub->display_def.window_flag = 0;
1320 sub->display_def.display_width = 720;
1321 sub->display_def.display_height = 576;
1322
1323 return sub;
1324 }
1325
1326 void
dvb_sub_free(DvbSub * sub)1327 dvb_sub_free (DvbSub * sub)
1328 {
1329 /* TODO: Add deinitalization code here */
1330 /* FIXME: Clear up region_list contents */
1331 delete_state (sub);
1332 while (sub->display_list) {
1333 DVBSubRegionDisplay *tmp = sub->display_list->next;
1334 g_slice_free (DVBSubRegionDisplay, sub->display_list);
1335 sub->display_list = tmp;
1336 }
1337 g_string_free (sub->pes_buffer, TRUE);
1338 g_slice_free (DvbSub, sub);
1339 }
1340
1341 #define DVB_SUB_SEGMENT_PAGE_COMPOSITION 0x10
1342 #define DVB_SUB_SEGMENT_REGION_COMPOSITION 0x11
1343 #define DVB_SUB_SEGMENT_CLUT_DEFINITION 0x12
1344 #define DVB_SUB_SEGMENT_OBJECT_DATA 0x13
1345 #define DVB_SUB_SEGMENT_DISPLAY_DEFINITION 0x14
1346 #define DVB_SUB_SEGMENT_END_OF_DISPLAY_SET 0x80
1347 #define DVB_SUB_SEGMENT_STUFFING 0xFF
1348
1349 #define DVB_SUB_SYNC_BYTE 0x0f
1350 /**
1351 * dvb_sub_feed_with_pts:
1352 * @dvb_sub: a #DvbSub
1353 * @pts: The PTS of the data
1354 * @data: The data to feed to the parser
1355 * @len: Length of the data
1356 *
1357 * Feeds the DvbSub parser with new binary data to parse,
1358 * with an associated PTS value. E.g, data left after PES
1359 * packet header has been already parsed, which contains
1360 * the PTS information).
1361 *
1362 * Return value: -1 if data was unhandled (e.g, not a subtitle packet),
1363 * -2 if data parsing was unsuccesful (e.g, length was invalid),
1364 * 0 or positive if data was handled. If positive, then amount of data consumed on success. FIXME: List the positive return values.
1365 */
1366 gint
dvb_sub_feed_with_pts(DvbSub * dvb_sub,guint64 pts,guint8 * data,gint len)1367 dvb_sub_feed_with_pts (DvbSub * dvb_sub, guint64 pts, guint8 * data, gint len)
1368 {
1369 unsigned int pos = 0;
1370 guint8 segment_type;
1371 guint16 segment_len;
1372 guint16 page_id;
1373
1374 GST_DEBUG ("pts=%" G_GUINT64_FORMAT " and length %d", pts, len);
1375
1376 g_return_val_if_fail (data != NULL || len == 0, -1);
1377
1378 if (G_UNLIKELY (data == NULL)) {
1379 GST_DEBUG ("no data; forcing end-of-display-set");
1380 _dvb_sub_parse_end_of_display_set (dvb_sub, 0, pts);
1381 return 0;
1382 }
1383
1384 if (len <= 3) { /* len(0x20 0x00 end_of_PES_data_field_marker) */
1385 GST_WARNING ("Data length too short");
1386 return -1;
1387 }
1388
1389 if (data[pos++] != 0x20) {
1390 GST_WARNING ("Tried to handle a PES packet private data that isn't a "
1391 "subtitle packet (does not start with 0x20)");
1392 return -1;
1393 }
1394
1395 if (data[pos++] != 0x00) {
1396 GST_WARNING ("'Subtitle stream in this PES packet' was not 0x00, so this "
1397 "is in theory not a DVB subtitle stream (but some other subtitle "
1398 "standard?); bailing out");
1399 return -1;
1400 }
1401
1402 while (data[pos++] == DVB_SUB_SYNC_BYTE) {
1403 if ((len - pos) < (2 * 2 + 1)) {
1404 GST_WARNING ("Data after SYNC BYTE too short, less than needed to "
1405 "even get to segment_length");
1406 return -2;
1407 }
1408 segment_type = data[pos++];
1409 GST_DEBUG ("=== Segment type is 0x%x", segment_type);
1410 page_id = (data[pos] << 8) | data[pos + 1];
1411 GST_DEBUG ("page_id is 0x%x", page_id);
1412 pos += 2;
1413 segment_len = (data[pos] << 8) | data[pos + 1];
1414 GST_DEBUG ("segment_length is %d (0x%x 0x%x)", segment_len, data[pos],
1415 data[pos + 1]);
1416 pos += 2;
1417 if ((len - pos) < segment_len) {
1418 GST_WARNING ("segment_length was told to be %u, but we only have "
1419 "%d bytes left", segment_len, len - pos);
1420 return -2;
1421 }
1422 // TODO: Parse the segment per type (this is probably a leftover TODO that is now done?)
1423 /* FIXME: Handle differing PTS values - all segments of a given display set must be with the same PTS,
1424 * FIXME: but we let it slip and just take it for granted in end_of_display_set */
1425 switch (segment_type) {
1426 case DVB_SUB_SEGMENT_PAGE_COMPOSITION:
1427 GST_DEBUG ("Page composition segment at buffer pos %u", pos);
1428 _dvb_sub_parse_page_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1429 break;
1430 case DVB_SUB_SEGMENT_REGION_COMPOSITION:
1431 GST_DEBUG ("Region composition segment at buffer pos %u", pos);
1432 _dvb_sub_parse_region_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1433 break;
1434 case DVB_SUB_SEGMENT_CLUT_DEFINITION:
1435 GST_DEBUG ("CLUT definition segment at buffer pos %u", pos);
1436 _dvb_sub_parse_clut_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1437 break;
1438 case DVB_SUB_SEGMENT_OBJECT_DATA:
1439 GST_DEBUG ("Object data segment at buffer pos %u", pos);
1440 _dvb_sub_parse_object_segment (dvb_sub, page_id, data + pos, segment_len); /* FIXME: Not sure about args */
1441 break;
1442 case DVB_SUB_SEGMENT_DISPLAY_DEFINITION:
1443 GST_DEBUG ("display definition segment at buffer pos %u", pos);
1444 _dvb_sub_parse_display_definition_segment (dvb_sub, data + pos,
1445 segment_len);
1446 break;
1447 case DVB_SUB_SEGMENT_END_OF_DISPLAY_SET:
1448 GST_DEBUG ("End of display set at buffer pos %u", pos);
1449 _dvb_sub_parse_end_of_display_set (dvb_sub, page_id, pts); /* FIXME: Not sure about args */
1450 break;
1451 default:
1452 GST_FIXME ("Unhandled segment type 0x%x", segment_type);
1453 break;
1454 }
1455
1456 pos += segment_len;
1457
1458 if (pos == len) {
1459 GST_WARNING ("Data ended without a PES data end marker");
1460 return 1;
1461 }
1462 }
1463
1464 GST_LOG ("Processed %d bytes out of %d", pos, len);
1465 return pos;
1466 }
1467
1468 /**
1469 * dvb_sub_set_callbacks:
1470 * @dvb_sub: a #DvbSub
1471 * @callbacks: the callbacks to install
1472 * @user_data: a user_data argument for the callback
1473 *
1474 * Set callback which will be executed when new subpictures are available.
1475 */
1476 void
dvb_sub_set_callbacks(DvbSub * dvb_sub,DvbSubCallbacks * callbacks,gpointer user_data)1477 dvb_sub_set_callbacks (DvbSub * dvb_sub, DvbSubCallbacks * callbacks,
1478 gpointer user_data)
1479 {
1480 g_return_if_fail (dvb_sub != NULL);
1481 g_return_if_fail (callbacks != NULL);
1482
1483 dvb_sub->callbacks = *callbacks;
1484 dvb_sub->user_data = user_data;
1485 }
1486