1 /* GStreamer
2 * Copyright (C) <2020> Jan Schmidt <jan@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdlib.h>
25
26 //#define HACK_2BIT /* Force 2-bit output by discarding colours */
27 //#define HACK_4BIT /* Force 4-bit output by discarding colours */
28
29 #include "gstdvbsubenc.h"
30 #include <gst/base/gstbytewriter.h>
31 #include <gst/base/gstbitwriter.h>
32
33 #include "libimagequant/libimagequant.h"
34
35 #define DVB_SEGMENT_SYNC_BYTE 0xF
36
37 enum DVBSegmentType
38 {
39 DVB_SEGMENT_TYPE_PAGE_COMPOSITION = 0x10,
40 DVB_SEGMENT_TYPE_REGION_COMPOSITION = 0x11,
41 DVB_SEGMENT_TYPE_CLUT_DEFINITION = 0x12,
42 DVB_SEGMENT_TYPE_OBJECT_DATA = 0x13,
43 DVB_SEGMENT_TYPE_DISPLAY_DEFINITION = 0x14,
44
45 DVB_SEGMENT_TYPE_END_OF_DISPLAY = 0x80
46 };
47
48 enum DVBPixelDataType
49 {
50 DVB_PIXEL_DATA_TYPE_2BIT = 0x10,
51 DVB_PIXEL_DATA_TYPE_4BIT = 0x11,
52 DVB_PIXEL_DATA_TYPE_8BIT = 0x12,
53 DVB_PIXEL_DATA_TYPE_END_OF_LINE = 0xF0
54 };
55
56 struct HistogramEntry
57 {
58 guint32 colour;
59 guint32 count;
60 guint32 substitution;
61 };
62
63 struct ColourEntry
64 {
65 guint32 colour;
66 guint32 pix_index;
67 };
68
69 typedef struct HistogramEntry HistogramEntry;
70 typedef struct ColourEntry ColourEntry;
71
72 static gint
compare_uint32(gconstpointer a,gconstpointer b)73 compare_uint32 (gconstpointer a, gconstpointer b)
74 {
75 guint32 v1 = *(guint32 *) (a);
76 guint32 v2 = *(guint32 *) (b);
77
78 if (v1 < v2)
79 return -1;
80 if (v1 > v2)
81 return 1;
82 return 0;
83 }
84
85 static gint
compare_colour_entry_colour(gconstpointer a,gconstpointer b)86 compare_colour_entry_colour (gconstpointer a, gconstpointer b)
87 {
88 const ColourEntry *c1 = (ColourEntry *) (a);
89 const ColourEntry *c2 = (ColourEntry *) (b);
90
91 /* Reverse order, so highest alpha comes first: */
92 return compare_uint32 (&c2->colour, &c1->colour);
93 }
94
95 static void
image_get_rgba_row_callback(liq_color row_out[],int row_index,int width,void * user_info)96 image_get_rgba_row_callback (liq_color row_out[], int row_index, int width,
97 void *user_info)
98 {
99 int column_index;
100 GstVideoFrame *src = (GstVideoFrame *) (user_info);
101 guint8 *src_pixels = (guint8 *) (src->data[0]);
102 const guint32 src_stride = GST_VIDEO_INFO_PLANE_STRIDE (&src->info, 0);
103 guint8 *src_row = src_pixels + (row_index * src_stride);
104 gint offset = 0;
105
106 for (column_index = 0; column_index < width; column_index++) {
107 liq_color *col = row_out + column_index;
108 guint8 *p = src_row + offset;
109
110 /* FIXME: We pass AYUV into the ARGB colour values,
111 * which works but probably makes suboptimal choices about
112 * which colours to preserve. It would be better to convert to RGBA
113 * and back again, or to modify libimagequant to handle ayuv */
114 col->a = p[0];
115 col->r = p[1];
116 col->g = p[2];
117 col->b = p[3];
118
119 offset += 4;
120 }
121 }
122
123 /*
124 * Utility function to unintelligently extract a
125 * (max) 256 colour image from an AYUV input
126 * Dumb for now, but could be improved if needed. If there's
127 * more than 256 colours in the input, it will reduce it 256
128 * by taking the most common 255 colours + transparent and mapping all
129 * remaining colours to the nearest neighbour.
130 *
131 * FIXME: Integrate a better palette selection algorithm.
132 */
133 gboolean
gst_dvbsubenc_ayuv_to_ayuv8p(GstVideoFrame * src,GstVideoFrame * dest,int max_colours,guint32 * out_num_colours)134 gst_dvbsubenc_ayuv_to_ayuv8p (GstVideoFrame * src, GstVideoFrame * dest,
135 int max_colours, guint32 * out_num_colours)
136 {
137 /* Allocate a temporary array the size of the input frame, copy in
138 * the source pixels, sort them by value and then count the first
139 * up to 256 colours. */
140 gboolean ret = FALSE;
141
142 GArray *colours, *histogram;
143 gint i, num_pixels, dest_y_index, out_index;
144 guint num_colours, cur_count;
145 guint32 last;
146 guint8 *s;
147 HistogramEntry *h;
148 ColourEntry *c;
149 const guint32 src_stride = GST_VIDEO_INFO_PLANE_STRIDE (&src->info, 0);
150 const guint32 dest_stride = GST_VIDEO_INFO_PLANE_STRIDE (&dest->info, 0);
151
152 if (GST_VIDEO_INFO_FORMAT (&src->info) != GST_VIDEO_FORMAT_AYUV)
153 return FALSE;
154
155 if (GST_VIDEO_INFO_WIDTH (&src->info) != GST_VIDEO_INFO_WIDTH (&dest->info) ||
156 GST_VIDEO_INFO_HEIGHT (&src->info) != GST_VIDEO_INFO_HEIGHT (&dest->info))
157 return FALSE;
158
159 num_pixels =
160 GST_VIDEO_INFO_WIDTH (&src->info) * GST_VIDEO_INFO_HEIGHT (&src->info);
161 s = (guint8 *) (src->data[0]);
162
163 colours = g_array_sized_new (FALSE, FALSE, sizeof (ColourEntry), num_pixels);
164 colours = g_array_set_size (colours, num_pixels);
165
166 histogram =
167 g_array_sized_new (FALSE, TRUE, sizeof (HistogramEntry), num_pixels);
168 histogram = g_array_set_size (histogram, num_pixels);
169
170 /* Copy the pixels to an array we can sort, dropping any stride padding,
171 * and recording the output index into the destination bitmap in the
172 * pix_index field */
173 dest_y_index = 0;
174 out_index = 0;
175 for (i = 0; i < GST_VIDEO_INFO_HEIGHT (&src->info); i++) {
176 guint32 x_index;
177 gint x;
178
179 for (x = 0, x_index = 0; x < GST_VIDEO_INFO_WIDTH (&src->info);
180 x++, x_index += 4) {
181 guint8 *pix = s + x_index;
182
183 c = &g_array_index (colours, ColourEntry, out_index);
184 c->colour = GST_READ_UINT32_BE (pix);
185 c->pix_index = dest_y_index + x;
186
187 out_index++;
188 }
189
190 s += src_stride;
191 dest_y_index += dest_stride;
192 }
193
194 /* Build a histogram of the highest colour counts: */
195 g_array_sort (colours, compare_colour_entry_colour);
196 c = &g_array_index (colours, ColourEntry, 0);
197 last = c->colour;
198 num_colours = 0;
199 cur_count = 1;
200 for (i = 1; i < num_pixels; i++) {
201 ColourEntry *c = &g_array_index (colours, ColourEntry, i);
202 guint32 cur = c->colour;
203
204 if (cur == last) {
205 cur_count++;
206 continue;
207 }
208
209 /* Colour changed - add an entry to the histogram */
210 h = &g_array_index (histogram, HistogramEntry, num_colours);
211 h->colour = last;
212 h->count = cur_count;
213
214 num_colours++;
215 cur_count = 1;
216 last = cur;
217 }
218 h = &g_array_index (histogram, HistogramEntry, num_colours);
219 h->colour = last;
220 h->count = cur_count;
221 num_colours++;
222
223 GST_LOG ("image has %u colours", num_colours);
224 histogram = g_array_set_size (histogram, num_colours);
225
226 if (num_colours > max_colours) {
227 liq_image *image;
228 liq_result *res;
229 const liq_palette *pal;
230 int i;
231 int height = GST_VIDEO_INFO_HEIGHT (&src->info);
232 unsigned char **dest_rows = malloc (height * sizeof (void *));
233 guint8 *dest_palette = (guint8 *) (dest->data[1]);
234 liq_attr *attr = liq_attr_create ();
235 gint out_index = 0;
236
237 for (i = 0; i < height; i++) {
238 dest_rows[i] = (guint8 *) (dest->data[0]) + i * dest_stride;
239 }
240
241 liq_set_max_colors (attr, max_colours);
242
243 image = liq_image_create_custom (attr, image_get_rgba_row_callback, src,
244 GST_VIDEO_INFO_WIDTH (&src->info), GST_VIDEO_INFO_HEIGHT (&src->info),
245 0);
246
247 res = liq_quantize_image (attr, image);
248
249 liq_write_remapped_image_rows (res, image, dest_rows);
250
251 pal = liq_get_palette (res);
252 num_colours = pal->count;
253
254 /* Write out the palette */
255 for (i = 0; i < num_colours; i++) {
256 guint8 *c = dest_palette + out_index;
257 const liq_color *col = pal->entries + i;
258
259 c[0] = col->a;
260 c[1] = col->r;
261 c[2] = col->g;
262 c[3] = col->b;
263
264 out_index += 4;
265 }
266
267 free (dest_rows);
268
269 liq_attr_destroy (attr);
270 liq_image_destroy (image);
271 liq_result_destroy (res);
272 } else {
273 guint8 *d = (guint8 *) (dest->data[0]);
274 guint8 *palette = (guint8 *) (dest->data[1]);
275 gint out_index = 0;
276
277 /* Write out the palette */
278 for (i = 0; i < num_colours; i++) {
279 h = &g_array_index (histogram, HistogramEntry, i);
280 GST_WRITE_UINT32_BE (palette + out_index, h->colour);
281 out_index += 4;
282 }
283
284 /* Write out the palette image. At this point, both the
285 * colours and histogram arrays are sorted in descending AYUV value,
286 * so walk them both and write out the current palette index */
287 out_index = 0;
288 for (i = 0; i < num_pixels; i++) {
289 c = &g_array_index (colours, ColourEntry, i);
290 h = &g_array_index (histogram, HistogramEntry, out_index);
291
292 if (c->colour != h->colour) {
293 out_index++;
294 h = &g_array_index (histogram, HistogramEntry, out_index);
295 g_assert (h->colour == c->colour); /* We must be walking colours in the same order in both arrays */
296 }
297 d[c->pix_index] = out_index;
298 }
299 }
300
301 ret = TRUE;
302 if (out_num_colours)
303 *out_num_colours = num_colours;
304
305 g_array_free (colours, TRUE);
306 g_array_free (histogram, TRUE);
307
308 return ret;
309 }
310
311 typedef void (*EncodeRLEFunc) (GstByteWriter * b, const guint8 * pixels,
312 const gint stride, const gint w, const gint h);
313
314 static void
encode_rle2(GstByteWriter * b,const guint8 * pixels,const gint stride,const gint w,const gint h)315 encode_rle2 (GstByteWriter * b, const guint8 * pixels,
316 const gint stride, const gint w, const gint h)
317 {
318 GstBitWriter bits;
319
320 int y;
321
322 gst_bit_writer_init (&bits);
323
324 for (y = 0; y < h; y++) {
325 int x = 0;
326 guint size;
327
328 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_2BIT);
329
330 while (x < w) {
331 int x_end = x;
332 int run_length;
333 guint8 pix;
334
335 pix = pixels[x_end++];
336 while (x_end < w && pixels[x_end] == pix)
337 x_end++;
338
339 #ifdef HACK_2BIT
340 pix >>= 6; /* HACK to convert 8 bit to 2 bit palette */
341 #endif
342
343 /* 284 is the largest run length we can encode */
344 run_length = MIN (x_end - x, 284);
345
346 if (run_length >= 29) {
347 /* 000011LLLL = run 29 to 284 pixels */
348 if (run_length > 284)
349 run_length = 284;
350
351 gst_bit_writer_put_bits_uint8 (&bits, 0x03, 6);
352 gst_bit_writer_put_bits_uint8 (&bits, run_length - 29, 8);
353 gst_bit_writer_put_bits_uint8 (&bits, pix, 2);
354 } else if (run_length >= 12 && run_length <= 27) {
355 /* 000010LLLL = run 12 to 27 pixels */
356 gst_bit_writer_put_bits_uint8 (&bits, 0x02, 6);
357 gst_bit_writer_put_bits_uint8 (&bits, run_length - 12, 4);
358 gst_bit_writer_put_bits_uint8 (&bits, pix, 2);
359 } else if (run_length >= 3 && run_length <= 10) {
360 /* 001LL = run 3 to 10 pixels */
361 gst_bit_writer_put_bits_uint8 (&bits, 0, 2);
362 gst_bit_writer_put_bits_uint8 (&bits, 0x8 + run_length - 3, 4);
363 gst_bit_writer_put_bits_uint8 (&bits, pix, 2);
364 }
365 /* Missed cases - 11 pixels, 28 pixels or a short length 1 or 2 pixels
366 * - write out a single pixel if != 0, or 1 or 2 pixels of black */
367 else if (pix != 0) {
368 gst_bit_writer_put_bits_uint8 (&bits, pix, 2);
369 run_length = 1;
370 } else if (run_length == 2) {
371 /* 0000 01 - 2 pixels colour 0 */
372 gst_bit_writer_put_bits_uint8 (&bits, 0x1, 6);
373 run_length = 2;
374 } else {
375 /* 0001 - single pixel colour 0 */
376 gst_bit_writer_put_bits_uint8 (&bits, 0x1, 4);
377 run_length = 1;
378 }
379
380 x += run_length;
381 GST_LOG ("%u pixels = colour %u", run_length, pix);
382 }
383
384 /* End of line 0x00 */
385 gst_bit_writer_put_bits_uint8 (&bits, 0x00, 8);
386
387 /* pad by 4 bits if needed to byte align, then
388 * write bit string to output */
389 gst_bit_writer_align_bytes (&bits, 0);
390 size = gst_bit_writer_get_size (&bits);
391
392 gst_byte_writer_put_data (b, gst_bit_writer_get_data (&bits), size / 8);
393
394 gst_bit_writer_reset (&bits);
395 gst_bit_writer_init (&bits);
396
397 GST_LOG ("y %u 2-bit RLE string = %u bits", y, size);
398 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_END_OF_LINE);
399 pixels += stride;
400 }
401 }
402
403 static void
encode_rle4(GstByteWriter * b,const guint8 * pixels,const gint stride,const gint w,const gint h)404 encode_rle4 (GstByteWriter * b, const guint8 * pixels,
405 const gint stride, const gint w, const gint h)
406 {
407 GstBitWriter bits;
408
409 int y;
410
411 gst_bit_writer_init (&bits);
412
413 for (y = 0; y < h; y++) {
414 int x = 0;
415 guint size;
416
417 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_4BIT);
418
419 while (x < w) {
420 int x_end = x;
421 int run_length;
422 guint8 pix;
423
424 pix = pixels[x_end++];
425 while (x_end < w && pixels[x_end] == pix)
426 x_end++;
427
428 /* 280 is the largest run length we can encode */
429 run_length = MIN (x_end - x, 280);
430
431 GST_LOG ("Encoding run %u pixels = colour %u", run_length, pix);
432
433 #ifdef HACK_4BIT
434 pix >>= 4; /* HACK to convert 8 bit to 4 palette */
435 #endif
436
437 if (pix == 0 && run_length >= 3 && run_length <= 9) {
438 gst_bit_writer_put_bits_uint8 (&bits, 0, 4);
439 gst_bit_writer_put_bits_uint8 (&bits, run_length - 2, 4);
440 } else if (run_length >= 4 && run_length < 25) {
441 /* 4 to 7 pixels encoding */
442 if (run_length > 7)
443 run_length = 7;
444
445 gst_bit_writer_put_bits_uint8 (&bits, 0, 4);
446 gst_bit_writer_put_bits_uint8 (&bits, 0x8 + run_length - 4, 4);
447 gst_bit_writer_put_bits_uint8 (&bits, pix, 4);
448 } else if (run_length >= 25) {
449 /* Run length 25 to 280 pixels */
450 if (run_length > 280)
451 run_length = 280;
452
453 gst_bit_writer_put_bits_uint8 (&bits, 0x0f, 8);
454 gst_bit_writer_put_bits_uint8 (&bits, run_length - 25, 8);
455 gst_bit_writer_put_bits_uint8 (&bits, pix, 4);
456 }
457 /* Short length, 1, 2 or 3 pixels - write out a single pixel if != 0,
458 * or 1 or 2 pixels of black */
459 else if (pix != 0) {
460 gst_bit_writer_put_bits_uint8 (&bits, pix, 4);
461 run_length = 1;
462 } else if (run_length > 1) {
463 /* 0000 1101 */
464 gst_bit_writer_put_bits_uint8 (&bits, 0xd, 8);
465 run_length = 2;
466 } else {
467 /* 0000 1100 */
468 gst_bit_writer_put_bits_uint8 (&bits, 0xc, 8);
469 run_length = 1;
470 }
471 x += run_length;
472
473 GST_LOG ("Put %u pixels = colour %u", run_length, pix);
474 }
475
476 /* End of line 0x00 */
477 gst_bit_writer_put_bits_uint8 (&bits, 0x00, 8);
478
479 /* pad by 4 bits if needed to byte align, then
480 * write bit string to output */
481 gst_bit_writer_align_bytes (&bits, 0);
482 size = gst_bit_writer_get_size (&bits);
483
484 gst_byte_writer_put_data (b, gst_bit_writer_get_data (&bits), size / 8);
485
486 gst_bit_writer_reset (&bits);
487 gst_bit_writer_init (&bits);
488
489 GST_LOG ("y %u 4-bit RLE string = %u bits", y, size);
490 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_END_OF_LINE);
491 pixels += stride;
492 }
493 }
494
495 static void
encode_rle8(GstByteWriter * b,const guint8 * pixels,const gint stride,const gint w,const gint h)496 encode_rle8 (GstByteWriter * b, const guint8 * pixels,
497 const gint stride, const gint w, const gint h)
498 {
499 int y;
500
501 for (y = 0; y < h; y++) {
502 int x = 0;
503
504 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_8BIT);
505
506 while (x < w) {
507 int x_end = x;
508 int run_length;
509 guint8 pix;
510
511 pix = pixels[x_end++];
512 while (x_end < w && pixels[x_end] == pix)
513 x_end++;
514
515 /* 127 is the largest run length we can encode */
516 run_length = MIN (x_end - x, 127);
517
518 if (run_length == 1 && pix != 0) {
519 /* a single non-zero pixel - encode directly */
520 gst_byte_writer_put_uint8 (b, pix);
521 } else if (pix == 0) {
522 /* Encode up to 1-127 pixels of colour 0 */
523 gst_byte_writer_put_uint8 (b, 0);
524 gst_byte_writer_put_uint8 (b, run_length);
525 } else if (run_length > 2) {
526 /* Encode 3-127 pixels of colour 'pix' directly */
527 gst_byte_writer_put_uint8 (b, 0);
528 gst_byte_writer_put_uint8 (b, 0x80 | run_length);
529 gst_byte_writer_put_uint8 (b, pix);
530 } else {
531 /* Short 1-2 pixel run, encode it directly */
532 if (run_length == 2)
533 gst_byte_writer_put_uint8 (b, pix);
534 gst_byte_writer_put_uint8 (b, pix);
535 g_assert (run_length == 1 || run_length == 2);
536 }
537 x += run_length;
538 }
539
540 /* End of line bytes */
541 gst_byte_writer_put_uint8 (b, 0x00);
542 // This 2nd 0x00 byte is correct from the spec, but ffmpeg
543 // as of 2020-04-24 does not like it
544 gst_byte_writer_put_uint8 (b, 0x00);
545 gst_byte_writer_put_uint8 (b, DVB_PIXEL_DATA_TYPE_END_OF_LINE);
546 pixels += stride;
547 }
548 }
549
550 static gboolean
dvbenc_write_object_data(GstByteWriter * b,int object_version,int page_id,int object_id,SubpictureRect * s)551 dvbenc_write_object_data (GstByteWriter * b, int object_version, int page_id,
552 int object_id, SubpictureRect * s)
553 {
554 guint seg_size_pos, end_pos;
555 guint pixel_fields_size_pos, top_start_pos, bottom_start_pos;
556 EncodeRLEFunc encode_rle_func;
557 const gint stride = GST_VIDEO_INFO_PLANE_STRIDE (&s->frame->info, 0);
558 const gint w = GST_VIDEO_INFO_WIDTH (&s->frame->info);
559 const gint h = GST_VIDEO_INFO_HEIGHT (&s->frame->info);
560 const guint8 *pixels = (guint8 *) (s->frame->data[0]);
561
562 if (s->nb_colours <= 4)
563 encode_rle_func = encode_rle2;
564 else if (s->nb_colours <= 16)
565 encode_rle_func = encode_rle4;
566 else
567 encode_rle_func = encode_rle8;
568
569 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_SYNC_BYTE);
570 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_TYPE_OBJECT_DATA);
571 gst_byte_writer_put_uint16_be (b, page_id);
572 seg_size_pos = gst_byte_writer_get_pos (b);
573 gst_byte_writer_put_uint16_be (b, 0);
574 gst_byte_writer_put_uint16_be (b, object_id);
575 /* version number, coding_method (0), non-modifying-flag (0), reserved bit */
576 gst_byte_writer_put_uint8 (b, (object_version << 4) | 0x01);
577
578 pixel_fields_size_pos = gst_byte_writer_get_pos (b);
579 gst_byte_writer_put_uint16_be (b, 0);
580 gst_byte_writer_put_uint16_be (b, 0);
581
582 /* Write the top field (even) lines (round up lines / 2) */
583 top_start_pos = gst_byte_writer_get_pos (b);
584 encode_rle_func (b, pixels, stride * 2, w, (h + 1) / 2);
585
586 /* Write the bottom field (odd) lines (round down lines / 2) */
587 bottom_start_pos = gst_byte_writer_get_pos (b);
588 if (h > 1)
589 encode_rle_func (b, pixels + stride, stride * 2, w, h >> 1);
590
591 end_pos = gst_byte_writer_get_pos (b);
592
593 /* If the encoded size of the top+bottom field data blocks is even,
594 * add a stuffing byte */
595 if (((end_pos - top_start_pos) & 1) == 0) {
596 gst_byte_writer_put_uint8 (b, 0);
597 end_pos = gst_byte_writer_get_pos (b);
598 }
599
600 /* Re-write the size fields */
601 gst_byte_writer_set_pos (b, seg_size_pos);
602 if (end_pos - (seg_size_pos + 2) > G_MAXUINT16)
603 return FALSE; /* Data too big */
604 gst_byte_writer_put_uint16_be (b, end_pos - (seg_size_pos + 2));
605
606 if (bottom_start_pos - top_start_pos > G_MAXUINT16)
607 return FALSE; /* Data too big */
608 if (end_pos - bottom_start_pos > G_MAXUINT16)
609 return FALSE; /* Data too big */
610
611 gst_byte_writer_set_pos (b, pixel_fields_size_pos);
612 gst_byte_writer_put_uint16_be (b, bottom_start_pos - top_start_pos);
613 gst_byte_writer_put_uint16_be (b, end_pos - bottom_start_pos);
614 gst_byte_writer_set_pos (b, end_pos);
615
616 GST_LOG ("Object seg size %u top_size %u bottom_size %u",
617 end_pos - (seg_size_pos + 2), bottom_start_pos - top_start_pos,
618 end_pos - bottom_start_pos);
619
620 return TRUE;
621 }
622
623 static void
dvbenc_write_clut(GstByteWriter * b,int object_version,int page_id,int clut_id,SubpictureRect * s)624 dvbenc_write_clut (GstByteWriter * b, int object_version, int page_id,
625 int clut_id, SubpictureRect * s)
626 {
627 guint8 *palette;
628 int clut_entry_flag;
629 guint seg_size_pos, pos;
630 int i;
631
632 if (s->nb_colours <= 4)
633 clut_entry_flag = 4;
634 else if (s->nb_colours <= 16)
635 clut_entry_flag = 2;
636 else
637 clut_entry_flag = 1;
638 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_SYNC_BYTE);
639 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_TYPE_CLUT_DEFINITION);
640 gst_byte_writer_put_uint16_be (b, page_id);
641 seg_size_pos = gst_byte_writer_get_pos (b);
642 gst_byte_writer_put_uint16_be (b, 0);
643 gst_byte_writer_put_uint8 (b, clut_id);
644 /* version number, reserved bits */
645 gst_byte_writer_put_uint8 (b, (object_version << 4) | 0x0F);
646
647 palette = (guint8 *) (s->frame->data[1]);
648 for (i = 0; i < s->nb_colours; i++) {
649
650 gst_byte_writer_put_uint8 (b, i);
651 /* clut_entry_flag | 4-bits reserved | full_range_flag = 1 */
652 gst_byte_writer_put_uint8 (b, clut_entry_flag << 5 | 0x1F);
653 /* Write YVUT value, where T (transparency) = 255 - A, Palette is AYUV */
654 gst_byte_writer_put_uint8 (b, palette[1]); /* Y */
655 gst_byte_writer_put_uint8 (b, palette[3]); /* V */
656 gst_byte_writer_put_uint8 (b, palette[2]); /* U */
657 gst_byte_writer_put_uint8 (b, 255 - palette[0]); /* A */
658
659 #if defined (HACK_2BIT)
660 palette += 4 * 64; /* HACK to generate 4-colour palette */
661 #elif defined (HACK_4BIT)
662 palette += 4 * 16; /* HACK to generate 16-colour palette */
663 #else
664 palette += 4;
665 #endif
666 }
667
668 /* Re-write the size field */
669 pos = gst_byte_writer_get_pos (b);
670 gst_byte_writer_set_pos (b, seg_size_pos);
671 gst_byte_writer_put_uint16_be (b, pos - (seg_size_pos + 2));
672 gst_byte_writer_set_pos (b, pos);
673 }
674
675 static void
dvbenc_write_region_segment(GstByteWriter * b,int object_version,int page_id,int region_id,SubpictureRect * s)676 dvbenc_write_region_segment (GstByteWriter * b, int object_version, int page_id,
677 int region_id, SubpictureRect * s)
678 {
679 int region_depth;
680 guint seg_size_pos, pos;
681 gint w = GST_VIDEO_INFO_WIDTH (&s->frame->info);
682 gint h = GST_VIDEO_INFO_HEIGHT (&s->frame->info);
683
684 if (s->nb_colours <= 4)
685 region_depth = 1;
686 else if (s->nb_colours <= 16)
687 region_depth = 2;
688 else
689 region_depth = 3;
690
691 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_SYNC_BYTE);
692 gst_byte_writer_put_uint8 (b, DVB_SEGMENT_TYPE_REGION_COMPOSITION);
693 gst_byte_writer_put_uint16_be (b, page_id);
694
695 /* Size placeholder */
696 seg_size_pos = gst_byte_writer_get_pos (b);
697 gst_byte_writer_put_uint16_be (b, 0);
698
699 gst_byte_writer_put_uint8 (b, region_id);
700 /* version number, fill flag, reserved bits */
701 gst_byte_writer_put_uint8 (b, (object_version << 4) | (0 << 3) | 0x07);
702 gst_byte_writer_put_uint16_be (b, w);
703 gst_byte_writer_put_uint16_be (b, h);
704 /* level_of_compatibility and depth */
705 gst_byte_writer_put_uint8 (b, region_depth << 5 | region_depth << 2 | 0x03);
706 /* CLUT id */
707 gst_byte_writer_put_uint8 (b, region_id);
708 /* Dummy flags for the fill colours */
709 gst_byte_writer_put_uint16_be (b, 0x0003);
710
711 /* Object ID = region_id = CLUT id */
712 gst_byte_writer_put_uint16_be (b, region_id);
713 /* object type = 0, x,y corner = 0 */
714 gst_byte_writer_put_uint16_be (b, 0x0000);
715 gst_byte_writer_put_uint16_be (b, 0xf000);
716
717 /* Re-write the size field */
718 pos = gst_byte_writer_get_pos (b);
719 gst_byte_writer_set_pos (b, seg_size_pos);
720 gst_byte_writer_put_uint16_be (b, pos - (seg_size_pos + 2));
721 gst_byte_writer_set_pos (b, pos);
722 }
723
724 GstBuffer *
gst_dvbenc_encode(int object_version,int page_id,SubpictureRect * s,guint num_subpictures)725 gst_dvbenc_encode (int object_version, int page_id, SubpictureRect * s,
726 guint num_subpictures)
727 {
728 GstByteWriter b;
729 guint seg_size_pos, pos;
730 guint i;
731
732 #ifdef HACK_2BIT
733 /* HACK: Only output 4 colours (results may be garbage, but tests
734 * the encoding */
735 s->nb_colours = 4;
736 #elif defined (HACK_4BIT)
737 /* HACK: Only output 16 colours */
738 s->nb_colours = 16;
739 #endif
740
741 gst_byte_writer_init (&b);
742
743 /* GStreamer passes DVB subpictures as private PES packets with
744 * 0x20 0x00 prefixed */
745 gst_byte_writer_put_uint16_be (&b, 0x2000);
746
747 /* Page Composition Segment */
748 gst_byte_writer_put_uint8 (&b, DVB_SEGMENT_SYNC_BYTE);
749 gst_byte_writer_put_uint8 (&b, DVB_SEGMENT_TYPE_PAGE_COMPOSITION);
750 gst_byte_writer_put_uint16_be (&b, page_id);
751 seg_size_pos = gst_byte_writer_get_pos (&b);
752 gst_byte_writer_put_uint16_be (&b, 0);
753 gst_byte_writer_put_uint8 (&b, 30);
754
755 /* We always write complete overlay subregions, so use page_state = 2 (mode change) */
756 gst_byte_writer_put_uint8 (&b, (object_version << 4) | (2 << 2) | 0x3);
757
758 for (i = 0; i < num_subpictures; i++) {
759 gst_byte_writer_put_uint8 (&b, i);
760 gst_byte_writer_put_uint8 (&b, 0xFF);
761 gst_byte_writer_put_uint16_be (&b, s[i].x);
762 gst_byte_writer_put_uint16_be (&b, s[i].y);
763 }
764
765 /* Rewrite the size field */
766 pos = gst_byte_writer_get_pos (&b);
767 gst_byte_writer_set_pos (&b, seg_size_pos);
768 gst_byte_writer_put_uint16_be (&b, pos - (seg_size_pos + 2));
769 gst_byte_writer_set_pos (&b, pos);
770
771 /* Region Composition */
772 for (i = 0; i < num_subpictures; i++) {
773 dvbenc_write_region_segment (&b, object_version, page_id, i, s + i);
774 }
775 /* CLUT definitions */
776 for (i = 0; i < num_subpictures; i++) {
777 dvbenc_write_clut (&b, object_version, page_id, i, s + i);
778 }
779 /* object data */
780 for (i = 0; i < num_subpictures; i++) {
781 /* FIXME: Any object data could potentially overflow the 64K field
782 * size, in which case we should split it */
783 if (!dvbenc_write_object_data (&b, object_version, page_id, i, s + i)) {
784 GST_WARNING ("Object data was too big to encode");
785 goto fail;
786 }
787 }
788 /* End of Display Set segment */
789 gst_byte_writer_put_uint8 (&b, DVB_SEGMENT_SYNC_BYTE);
790 gst_byte_writer_put_uint8 (&b, DVB_SEGMENT_TYPE_END_OF_DISPLAY);
791 gst_byte_writer_put_uint16_be (&b, page_id);
792 gst_byte_writer_put_uint16_be (&b, 0);
793
794 /* End of PES data marker */
795 gst_byte_writer_put_uint8 (&b, 0xFF);
796
797 return gst_byte_writer_reset_and_get_buffer (&b);
798
799 fail:
800 gst_byte_writer_reset (&b);
801 return NULL;
802 }
803