1 /* GStreamer Sub-Picture Unit - PGS handling
2  * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net>
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 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22 
23 #include <string.h>
24 #include <gst/gst.h>
25 
26 #include "gstdvdspu.h"
27 #include "gstspu-pgs.h"
28 
29 const struct PgsFrameRateEntry
30 {
31   guint8 id;
32   guint fps_n;
33   guint fps_d;
34 } PgsFrameRates[] = {
35   {
36   64, 30000, 1001}              /* 29.97 FPS */
37 };
38 
39 typedef enum PgsCommandType PgsCommandType;
40 
41 enum PgsCommandType
42 {
43   PGS_COMMAND_SET_PALETTE = 0x14,
44   PGS_COMMAND_SET_OBJECT_DATA = 0x15,
45   PGS_COMMAND_PRESENTATION_SEGMENT = 0x16,
46   PGS_COMMAND_SET_WINDOW = 0x17,
47   PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18,
48 
49   PGS_COMMAND_END_DISPLAY = 0x80,
50 
51   PGS_COMMAND_INVALID = 0xFFFF
52 };
53 
54 static gint gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf);
55 
56 #define DUMP_CMDS 0
57 #define DUMP_FULL_IMAGE 0
58 #define DUMP_FULL_PALETTE 0
59 
60 #if DUMP_CMDS
61 #define PGS_DUMP(...) g_print(__VA_ARGS__)
62 #else
63 #define PGS_DUMP(...)
64 #endif
65 
66 static void
dump_bytes(guint8 * data,guint16 len)67 dump_bytes (guint8 * data, guint16 len)
68 {
69   gint i;
70 
71   /* Dump the numbers */
72   for (i = 0; i < len; i++) {
73     PGS_DUMP ("0x%02x ", data[i]);
74     if (!((i + 1) % 16))
75       PGS_DUMP ("\n");
76   }
77   if (len > 0 && (i % 16))
78     PGS_DUMP ("\n");
79 }
80 
81 static void
dump_rle_data(GstDVDSpu * dvdspu,guint8 * data,guint32 len)82 dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len)
83 {
84 #if DUMP_FULL_IMAGE
85   guint16 obj_h;
86   guint16 obj_w;
87   guint8 *end = data + len;
88   guint x = 0;
89 
90   if (data + 4 > end)
91     return;
92 
93   /* RLE data: */
94   obj_w = GST_READ_UINT16_BE (data);
95   obj_h = GST_READ_UINT16_BE (data + 2);
96   data += 4;
97   PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h);
98 
99   while (data < end) {
100     guint8 pal_id;
101     guint16 run_len;
102 
103     pal_id = *data++;
104     if (pal_id != 0) {
105       // PGS_DUMP ("data 0x%02x\n", data[0]);
106       run_len = 1;
107     } else {
108       if (data + 1 > end)
109         return;
110       switch (data[0] & 0xC0) {
111         case 0x00:
112           //PGS_DUMP ("data 0x%02x\n", data[0]);
113           run_len = (data[0] & 0x3f);
114           data++;
115           break;
116         case 0x40:
117           if (data + 2 > end)
118             return;
119           //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
120           run_len = ((data[0] << 8) | data[1]) & 0x3fff;
121           data += 2;
122           break;
123         case 0x80:
124           if (data + 2 > end)
125             return;
126           //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]);
127           run_len = (data[0] & 0x3f);
128           pal_id = data[1];
129           data += 2;
130           break;
131         case 0xC0:
132           if (data + 3 > end)
133             return;
134           //PGS_DUMP ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]);
135           run_len = ((data[0] << 8) | data[1]) & 0x3fff;
136           pal_id = data[2];
137           data += 3;
138           break;
139         default:
140           run_len = 0;
141           break;
142       }
143     }
144 
145     {
146       gint i;
147 #if 1
148       if (dvdspu->spu_state.pgs.palette[pal_id].A) {
149         guint8 val = dvdspu->spu_state.pgs.palette[pal_id].A;
150         for (i = 0; i < run_len; i++)
151           PGS_DUMP ("%02x ", val);
152       } else {
153         for (i = 0; i < run_len; i++)
154           PGS_DUMP ("   ");
155       }
156       if (!run_len || (x + run_len) > obj_w)
157         PGS_DUMP ("\n");
158 #else
159       PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id);
160 #endif
161     }
162 
163     x += run_len;
164     if (!run_len || x > obj_w)
165       x = 0;
166   };
167 
168   PGS_DUMP ("\n");
169 #endif
170 }
171 
172 static void
pgs_composition_object_render(PgsCompositionObject * obj,SpuState * state,GstVideoFrame * frame)173 pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state,
174     GstVideoFrame * frame)
175 {
176   SpuColour *colour;
177   guint8 *planes[3];            /* YUV frame pointers */
178   gint strides[3];
179   guint8 *data, *end;
180   guint16 obj_w;
181   guint16 obj_h G_GNUC_UNUSED;
182   guint x, y, i, min_x, max_x;
183 
184   if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0
185           || obj->rle_data_used != obj->rle_data_size))
186     return;
187 
188   data = obj->rle_data;
189   end = data + obj->rle_data_used;
190 
191   if (data + 4 > end)
192     return;
193 
194   /* FIXME: Calculate and use the cropping window for the output, as the
195    * intersection of the crop rectangle for this object (if any) and the
196    * window specified by the object's window_id */
197 
198   /* Store the start of each plane */
199   planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0);
200   planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1);
201   planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2);
202 
203   strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
204   strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1);
205   strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2);
206 
207   y = MIN (obj->y, state->info.height);
208 
209   planes[0] += strides[0] * y;
210   planes[1] += strides[1] * (y / 2);
211   planes[2] += strides[2] * (y / 2);
212 
213   /* RLE data: */
214   obj_w = GST_READ_UINT16_BE (data);
215   obj_h = GST_READ_UINT16_BE (data + 2);
216   data += 4;
217 
218   min_x = MIN (obj->x, strides[0]);
219   max_x = MIN (obj->x + obj_w, strides[0]);
220 
221   state->comp_left = x = min_x;
222   state->comp_right = max_x;
223 
224   gstspu_clear_comp_buffers (state);
225 
226   while (data < end) {
227     guint8 pal_id;
228     guint16 run_len;
229 
230     pal_id = *data++;
231     if (pal_id != 0) {
232       run_len = 1;
233     } else {
234       if (data + 1 > end)
235         return;
236       switch (data[0] & 0xC0) {
237         case 0x00:
238           run_len = (data[0] & 0x3f);
239           data++;
240           break;
241         case 0x40:
242           if (data + 2 > end)
243             return;
244           run_len = ((data[0] << 8) | data[1]) & 0x3fff;
245           data += 2;
246           break;
247         case 0x80:
248           if (data + 2 > end)
249             return;
250           run_len = (data[0] & 0x3f);
251           pal_id = data[1];
252           data += 2;
253           break;
254         case 0xC0:
255           if (data + 3 > end)
256             return;
257           run_len = ((data[0] << 8) | data[1]) & 0x3fff;
258           pal_id = data[2];
259           data += 3;
260           break;
261         default:
262           run_len = 0;
263           break;
264       }
265     }
266 
267     colour = &state->pgs.palette[pal_id];
268     if (colour->A) {
269       guint32 inv_A = 0xff - colour->A;
270       if (G_UNLIKELY (x + run_len > max_x))
271         run_len = (max_x - x);
272 
273       for (i = 0; i < run_len; i++) {
274         planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff;
275 
276         state->comp_bufs[0][x / 2] += colour->U;
277         state->comp_bufs[1][x / 2] += colour->V;
278         state->comp_bufs[2][x / 2] += colour->A;
279         x++;
280       }
281     } else {
282       x += run_len;
283     }
284 
285     if (!run_len || x > max_x) {
286       x = min_x;
287       planes[0] += strides[0];
288 
289       if (y % 2) {
290         gstspu_blend_comp_buffers (state, planes);
291         gstspu_clear_comp_buffers (state);
292 
293         planes[1] += strides[1];
294         planes[2] += strides[2];
295       }
296       y++;
297       if (y >= state->info.height)
298         return;                 /* Hit the bottom */
299     }
300   }
301 
302   if (y % 2)
303     gstspu_blend_comp_buffers (state, planes);
304 }
305 
306 static void
pgs_composition_object_clear(PgsCompositionObject * obj)307 pgs_composition_object_clear (PgsCompositionObject * obj)
308 {
309   if (obj->rle_data) {
310     g_free (obj->rle_data);
311     obj->rle_data = NULL;
312   }
313   obj->rle_data_size = obj->rle_data_used = 0;
314 }
315 
316 static void
pgs_presentation_segment_set_object_count(PgsPresentationSegment * ps,guint8 n_objects)317 pgs_presentation_segment_set_object_count (PgsPresentationSegment * ps,
318     guint8 n_objects)
319 {
320   if (ps->objects == NULL) {
321     if (n_objects == 0)
322       return;
323     ps->objects =
324         g_array_sized_new (FALSE, TRUE, sizeof (PgsCompositionObject),
325         n_objects);
326     g_array_set_size (ps->objects, n_objects);
327     return;
328   }
329 
330   /* Clear memory in any extraneous objects */
331   if (ps->objects->len > n_objects) {
332     guint i;
333     for (i = n_objects; i < ps->objects->len; i++) {
334       PgsCompositionObject *cur =
335           &g_array_index (ps->objects, PgsCompositionObject, i);
336       pgs_composition_object_clear (cur);
337     }
338   }
339 
340   g_array_set_size (ps->objects, n_objects);
341 
342   if (n_objects == 0) {
343     g_array_free (ps->objects, TRUE);
344     ps->objects = NULL;
345   }
346 }
347 
348 static PgsCompositionObject *
pgs_presentation_segment_find_object(PgsPresentationSegment * ps,guint16 obj_id)349 pgs_presentation_segment_find_object (PgsPresentationSegment * ps,
350     guint16 obj_id)
351 {
352   guint i;
353   if (ps->objects == NULL)
354     return NULL;
355 
356   for (i = 0; i < ps->objects->len; i++) {
357     PgsCompositionObject *cur =
358         &g_array_index (ps->objects, PgsCompositionObject, i);
359     if (cur->id == obj_id)
360       return cur;
361   }
362 
363   return NULL;
364 }
365 
366 static int
parse_presentation_segment(GstDVDSpu * dvdspu,guint8 type,guint8 * payload,guint16 len)367 parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
368     guint16 len)
369 {
370   guint8 *end = payload + len;
371   PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg;
372   guint8 n_objects, palette_id;
373   gint i;
374 
375   /* Parse video descriptor */
376   if (payload + 5 > end)
377     return 0;
378 
379   ps->vid_w = GST_READ_UINT16_BE (payload);
380   ps->vid_h = GST_READ_UINT16_BE (payload + 2);
381   ps->vid_fps_code = payload[4];
382   payload += 5;
383 
384   /* Parse composition descriptor */
385   if (payload + 3 > end)
386     return 0;
387   ps->composition_no = GST_READ_UINT16_BE (payload);
388   ps->composition_state = payload[2];
389   payload += 3;
390 
391   /* Parse other bits */
392   if (payload + 3 > end)
393     return 0;
394 
395   ps->flags = payload[0];
396 
397   palette_id = payload[1];
398   n_objects = payload[2];
399   payload += 3;
400 
401   if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE)
402     ps->palette_id = palette_id;
403 
404   PGS_DUMP ("Video width %u height %u fps code %u\n", ps->vid_w, ps->vid_h,
405       ps->vid_fps_code);
406   PGS_DUMP
407       ("Composition num %u state 0x%02x flags 0x%02x palette id %u n_objects %u\n",
408       ps->composition_no, ps->composition_state, ps->flags, ps->palette_id,
409       n_objects);
410 
411   pgs_presentation_segment_set_object_count (ps, n_objects);
412 
413   for (i = 0; i < (gint) n_objects; i++) {
414     PgsCompositionObject *obj =
415         &g_array_index (ps->objects, PgsCompositionObject, i);
416 
417     if (payload + 8 > end)
418       break;
419     obj->id = GST_READ_UINT16_BE (payload);
420     obj->win_id = payload[2];
421     obj->flags = payload[3];
422     obj->x = GST_READ_UINT16_BE (payload + 4);
423     obj->y = GST_READ_UINT16_BE (payload + 6);
424     obj->rle_data_size = obj->rle_data_used = 0;
425 
426     payload += 8;
427 
428     PGS_DUMP ("Composition object %d Object ID %u Window ID %u flags 0x%02x "
429         "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y);
430 
431     if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) {
432       if (payload + 8 > end)
433         break;
434 
435       obj->crop_x = GST_READ_UINT16_BE (payload);
436       obj->crop_y = GST_READ_UINT16_BE (payload + 2);
437       obj->crop_w = GST_READ_UINT16_BE (payload + 4);
438       obj->crop_h = GST_READ_UINT16_BE (payload + 6);
439 
440       payload += 8;
441 
442       PGS_DUMP ("Cropping window x %u y %u w %u h %u\n",
443           obj->crop_x, obj->crop_y, obj->crop_w, obj->crop_h);
444     }
445 
446     if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED |
447             PGS_COMPOSITION_OBJECT_FLAG_FORCED))
448       GST_ERROR ("PGS Composition Object has unknown flags: 0x%02x",
449           obj->flags);
450   }
451 
452   if (payload != end) {
453     GST_ERROR ("PGS Composition Object: %" G_GSSIZE_FORMAT
454         " bytes not consumed", (gssize) (end - payload));
455     dump_bytes (payload, end - payload);
456   }
457 
458   return 0;
459 }
460 
461 static int
parse_set_palette(GstDVDSpu * dvdspu,guint8 type,guint8 * payload,guint16 len)462 parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
463     guint16 len)
464 {
465   SpuState *state = &dvdspu->spu_state;
466 
467   const gint PGS_PALETTE_ENTRY_SIZE = 5;
468   guint8 *end = payload + len;
469   guint8 palette_id G_GNUC_UNUSED;
470   guint8 palette_version G_GNUC_UNUSED;
471   gint n_entries, i;
472 
473   if (len < 2)                  /* Palette command too short */
474     return 0;
475   palette_id = payload[0];
476   palette_version = payload[1];
477   payload += 2;
478 
479   n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE;
480 
481   PGS_DUMP ("Palette ID %u version %u. %d entries\n",
482       palette_id, palette_version, n_entries);
483   for (i = 0; i < 256; i++)
484     state->pgs.palette[i].A = 0;
485   for (i = 0; i < n_entries; i++) {
486     guint8 n, Y, U, V, A;
487     n = payload[0];
488     Y = payload[1];
489     V = payload[2];
490     U = payload[3];
491     A = payload[4];
492 
493 #if DUMP_FULL_PALETTE
494     PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d  ", n, Y, U, V, A);
495     if (((i + 1) % 2) == 0)
496       PGS_DUMP ("\n");
497 #endif
498 
499     /* Premultiply the palette entries by the alpha */
500     state->pgs.palette[n].Y = Y * A;
501     state->pgs.palette[n].U = U * A;
502     state->pgs.palette[n].V = V * A;
503     state->pgs.palette[n].A = A;
504 
505     payload += PGS_PALETTE_ENTRY_SIZE;
506   }
507 
508 #if DUMP_FULL_PALETTE
509   if (n_entries > 0 && (i % 2))
510     PGS_DUMP ("\n");
511 #endif
512 
513   if (payload != end) {
514     GST_ERROR ("PGS Set Palette: %" G_GSSIZE_FORMAT " bytes not consumed",
515         (gssize) (end - payload));
516     dump_bytes (payload, end - payload);
517   }
518 
519   return 0;
520 }
521 
522 static int
parse_set_window(GstDVDSpu * dvdspu,guint8 type,guint8 * payload,guint16 len)523 parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
524     guint16 len)
525 {
526   SpuState *state = &dvdspu->spu_state;
527   guint8 *end = payload + len;
528   guint8 win_count, win_id G_GNUC_UNUSED;
529   gint i;
530 
531   if (payload + 1 > end)
532     return 0;
533 
534   dump_bytes (payload, len);
535 
536   win_count = payload[0];
537   payload++;
538 
539   for (i = 0; i < win_count; i++) {
540     if (payload + 9 > end)
541       return 0;
542 
543     /* FIXME: Store each window ID separately into an array */
544     win_id = payload[0];
545     state->pgs.win_x = GST_READ_UINT16_BE (payload + 1);
546     state->pgs.win_y = GST_READ_UINT16_BE (payload + 3);
547     state->pgs.win_w = GST_READ_UINT16_BE (payload + 5);
548     state->pgs.win_h = GST_READ_UINT16_BE (payload + 7);
549     payload += 9;
550 
551     PGS_DUMP ("Win ID %u x %d y %d w %d h %d\n",
552         win_id, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w,
553         state->pgs.win_h);
554   }
555 
556   if (payload != end) {
557     GST_ERROR ("PGS Set Window: %" G_GSSIZE_FORMAT " bytes not consumed",
558         (gssize) (end - payload));
559     dump_bytes (payload, end - payload);
560   }
561 
562   return 0;
563 }
564 
565 static int
parse_set_object_data(GstDVDSpu * dvdspu,guint8 type,guint8 * payload,guint16 len)566 parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
567     guint16 len)
568 {
569   SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
570   PgsCompositionObject *obj;
571   guint8 *end = payload + len;
572   guint16 obj_id;
573   guint8 obj_ver, flags;
574 
575   if (payload + 4 > end)
576     return 0;
577 
578   obj_id = GST_READ_UINT16_BE (payload);
579   obj_ver = payload[2];
580   flags = payload[3];
581   payload += 4;
582 
583   obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id);
584 
585   PGS_DUMP ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, flags);
586 
587   if (flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) {
588     obj->rle_data_ver = obj_ver;
589 
590     if (payload + 3 > end)
591       return 0;
592 
593     obj->rle_data_size = GST_READ_UINT24_BE (payload);
594     payload += 3;
595 
596     PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n",
597         (int) (end - payload), obj->rle_data_size);
598 
599     obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size);
600     obj->rle_data_used = end - payload;
601     memcpy (obj->rle_data, payload, end - payload);
602     payload = end;
603   } else {
604     PGS_DUMP ("%d bytes of additional RLE data\n", (int) (end - payload));
605     /* Check that the data chunk is for this object version, and fits in the buffer */
606     if (obj->rle_data_ver == obj_ver &&
607         obj->rle_data_used + end - payload <= obj->rle_data_size) {
608 
609       memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload);
610       obj->rle_data_used += end - payload;
611       payload = end;
612     }
613   }
614 
615   if (obj->rle_data_size == obj->rle_data_used)
616     dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size);
617 
618   if (payload != end) {
619     GST_ERROR ("PGS Set Object Data: %" G_GSSIZE_FORMAT " bytes not consumed",
620         (gssize) (end - payload));
621     dump_bytes (payload, end - payload);
622   }
623 
624   return 0;
625 }
626 
627 static int
parse_pgs_packet(GstDVDSpu * dvdspu,guint8 type,guint8 * payload,guint16 len)628 parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
629     guint16 len)
630 {
631   SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
632   int ret = 0;
633 
634   if (!pgs_state->in_presentation_segment
635       && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
636     PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. "
637         "Got command type 0x%02x len %u. Skipping\n", type, len);
638     return 0;
639   }
640 
641   switch (type) {
642     case PGS_COMMAND_PRESENTATION_SEGMENT:
643       PGS_DUMP ("*******************************************\n"
644           "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
645       pgs_state->in_presentation_segment =
646           pgs_state->have_presentation_segment = TRUE;
647       ret = parse_presentation_segment (dvdspu, type, payload, len);
648       break;
649     case PGS_COMMAND_SET_OBJECT_DATA:
650       PGS_DUMP ("***   Set Object Data (0x%02x) packet len %u\n", type, len);
651       ret = parse_set_object_data (dvdspu, type, payload, len);
652       break;
653     case PGS_COMMAND_SET_PALETTE:
654       PGS_DUMP ("***   Set Palette (0x%02x) packet len %u\n", type, len);
655       ret = parse_set_palette (dvdspu, type, payload, len);
656       break;
657     case PGS_COMMAND_SET_WINDOW:
658       PGS_DUMP ("***   Set Window command (0x%02x) packet len %u\n", type, len);
659       ret = parse_set_window (dvdspu, type, payload, len);
660       break;
661     case PGS_COMMAND_INTERACTIVE_SEGMENT:
662       PGS_DUMP ("***   Interactive Segment command(0x%02x) packet len %u\n",
663           type, len);
664       dump_bytes (payload, len);
665       break;
666     case PGS_COMMAND_END_DISPLAY:
667       PGS_DUMP ("***   End Display command (0x%02x) packet len %u\n", type,
668           len);
669       pgs_state->in_presentation_segment = FALSE;
670       break;
671     default:
672       GST_ERROR ("Unknown PGS command: type 0x%02x len %u", type, len);
673       dump_bytes (payload, len);
674       break;
675   }
676   PGS_DUMP ("\n");
677 
678   return ret;
679 }
680 
681 gint
gstspu_exec_pgs_buffer(GstDVDSpu * dvdspu,GstBuffer * buf)682 gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
683 {
684   GstMapInfo map;
685   guint8 *pos, *end;
686   guint8 type;
687   guint16 packet_len;
688   gint remaining;
689 
690   gst_buffer_map (buf, &map, GST_MAP_READ);
691 
692   pos = map.data;
693   end = pos + map.size;
694 
695   /* Need at least 3 bytes */
696   if (pos + 3 > end) {
697     PGS_DUMP ("Not enough bytes to be a PGS packet\n");
698     goto error;
699   }
700 
701   PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n",
702       (guint) (end - pos), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
703   do {
704     type = *pos++;
705     packet_len = GST_READ_UINT16_BE (pos);
706     pos += 2;
707 
708     if (pos + packet_len > end) {
709       PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len,
710           (guint) (end - pos));
711       goto error;
712     }
713 
714     if (parse_pgs_packet (dvdspu, type, pos, packet_len))
715       goto error;
716 
717     pos += packet_len;
718   } while (pos + 3 <= end);
719 
720   PGS_DUMP ("End dumping command buffer with %u bytes remaining\n",
721       (guint) (end - pos));
722   remaining = (gint) (pos - map.data);
723   gst_buffer_unmap (buf, &map);
724   return remaining;
725 
726   /* ERRORS */
727 error:
728   {
729     gst_buffer_unmap (buf, &map);
730     return -1;
731   }
732 }
733 
734 void
gstspu_pgs_handle_new_buf(GstDVDSpu * dvdspu,GstClockTime event_ts,GstBuffer * buf)735 gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts,
736     GstBuffer * buf)
737 {
738   SpuState *state = &dvdspu->spu_state;
739 
740   state->next_ts = event_ts;
741   state->pgs.pending_cmd = buf;
742 }
743 
744 gboolean
gstspu_pgs_execute_event(GstDVDSpu * dvdspu)745 gstspu_pgs_execute_event (GstDVDSpu * dvdspu)
746 {
747   SpuState *state = &dvdspu->spu_state;
748 
749   if (state->pgs.pending_cmd) {
750     gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd);
751     gst_buffer_unref (state->pgs.pending_cmd);
752     state->pgs.pending_cmd = NULL;
753   }
754 
755   state->next_ts = GST_CLOCK_TIME_NONE;
756 
757   state->flags &= ~SPU_STATE_DISPLAY;
758   if (state->pgs.have_presentation_segment) {
759     if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0)
760       state->flags |= SPU_STATE_DISPLAY;
761   }
762   return FALSE;
763 }
764 
765 void
gstspu_pgs_render(GstDVDSpu * dvdspu,GstVideoFrame * frame)766 gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
767 {
768   SpuState *state = &dvdspu->spu_state;
769   PgsPresentationSegment *ps = &state->pgs.pres_seg;
770   guint i;
771 
772   if (ps->objects == NULL)
773     return;
774 
775   for (i = 0; i < ps->objects->len; i++) {
776     PgsCompositionObject *cur =
777         &g_array_index (ps->objects, PgsCompositionObject, i);
778     pgs_composition_object_render (cur, state, frame);
779   }
780 }
781 
782 gboolean
gstspu_pgs_handle_dvd_event(GstDVDSpu * dvdspu,GstEvent * event)783 gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
784 {
785   gst_event_unref (event);
786   return FALSE;
787 }
788 
789 void
gstspu_pgs_flush(GstDVDSpu * dvdspu)790 gstspu_pgs_flush (GstDVDSpu * dvdspu)
791 {
792   SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
793 
794   if (pgs_state->pending_cmd) {
795     gst_buffer_unref (pgs_state->pending_cmd);
796     pgs_state->pending_cmd = NULL;
797   }
798 
799   pgs_state->have_presentation_segment = FALSE;
800   pgs_state->in_presentation_segment = FALSE;
801   pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0);
802 
803   pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0;
804 }
805