• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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