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 #ifdef OHOS_OPT_CVE
597 if (end - payload > obj->rle_data_size)
598 return 0;
599 #endif
600
601 PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n",
602 (int) (end - payload), obj->rle_data_size);
603
604 obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size);
605 obj->rle_data_used = end - payload;
606 memcpy (obj->rle_data, payload, end - payload);
607 payload = end;
608 } else {
609 PGS_DUMP ("%d bytes of additional RLE data\n", (int) (end - payload));
610 /* Check that the data chunk is for this object version, and fits in the buffer */
611 #ifdef OHOS_OPT_CVE
612 if (obj->rle_data_ver == obj_ver &&
613 end - payload <= obj->rle_data_size &&
614 obj->rle_data_used <= obj->rle_data_size - (end - payload)) {
615 #else
616 if (obj->rle_data_ver == obj_ver &&
617 obj->rle_data_used + end - payload <= obj->rle_data_size) {
618 #endif
619
620 memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload);
621 obj->rle_data_used += end - payload;
622 payload = end;
623 }
624 }
625
626 if (obj->rle_data_size == obj->rle_data_used)
627 dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size);
628
629 if (payload != end) {
630 GST_ERROR ("PGS Set Object Data: %" G_GSSIZE_FORMAT " bytes not consumed",
631 (gssize) (end - payload));
632 dump_bytes (payload, end - payload);
633 }
634
635 return 0;
636 }
637
638 static int
639 parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload,
640 guint16 len)
641 {
642 SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
643 int ret = 0;
644
645 if (!pgs_state->in_presentation_segment
646 && type != PGS_COMMAND_PRESENTATION_SEGMENT) {
647 PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. "
648 "Got command type 0x%02x len %u. Skipping\n", type, len);
649 return 0;
650 }
651
652 switch (type) {
653 case PGS_COMMAND_PRESENTATION_SEGMENT:
654 PGS_DUMP ("*******************************************\n"
655 "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len);
656 pgs_state->in_presentation_segment =
657 pgs_state->have_presentation_segment = TRUE;
658 ret = parse_presentation_segment (dvdspu, type, payload, len);
659 break;
660 case PGS_COMMAND_SET_OBJECT_DATA:
661 PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len);
662 ret = parse_set_object_data (dvdspu, type, payload, len);
663 break;
664 case PGS_COMMAND_SET_PALETTE:
665 PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len);
666 ret = parse_set_palette (dvdspu, type, payload, len);
667 break;
668 case PGS_COMMAND_SET_WINDOW:
669 PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len);
670 ret = parse_set_window (dvdspu, type, payload, len);
671 break;
672 case PGS_COMMAND_INTERACTIVE_SEGMENT:
673 PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n",
674 type, len);
675 dump_bytes (payload, len);
676 break;
677 case PGS_COMMAND_END_DISPLAY:
678 PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type,
679 len);
680 pgs_state->in_presentation_segment = FALSE;
681 break;
682 default:
683 GST_ERROR ("Unknown PGS command: type 0x%02x len %u", type, len);
684 dump_bytes (payload, len);
685 break;
686 }
687 PGS_DUMP ("\n");
688
689 return ret;
690 }
691
692 gint
693 gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf)
694 {
695 GstMapInfo map;
696 guint8 *pos, *end;
697 guint8 type;
698 guint16 packet_len;
699 gint remaining;
700
701 gst_buffer_map (buf, &map, GST_MAP_READ);
702
703 pos = map.data;
704 end = pos + map.size;
705
706 /* Need at least 3 bytes */
707 if (pos + 3 > end) {
708 PGS_DUMP ("Not enough bytes to be a PGS packet\n");
709 goto error;
710 }
711
712 PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n",
713 (guint) (end - pos), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
714 do {
715 type = *pos++;
716 packet_len = GST_READ_UINT16_BE (pos);
717 pos += 2;
718
719 if (pos + packet_len > end) {
720 PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len,
721 (guint) (end - pos));
722 goto error;
723 }
724
725 if (parse_pgs_packet (dvdspu, type, pos, packet_len))
726 goto error;
727
728 pos += packet_len;
729 } while (pos + 3 <= end);
730
731 PGS_DUMP ("End dumping command buffer with %u bytes remaining\n",
732 (guint) (end - pos));
733 remaining = (gint) (pos - map.data);
734 gst_buffer_unmap (buf, &map);
735 return remaining;
736
737 /* ERRORS */
738 error:
739 {
740 gst_buffer_unmap (buf, &map);
741 return -1;
742 }
743 }
744
745 void
746 gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts,
747 GstBuffer * buf)
748 {
749 SpuState *state = &dvdspu->spu_state;
750
751 state->next_ts = event_ts;
752 state->pgs.pending_cmd = buf;
753 }
754
755 gboolean
756 gstspu_pgs_execute_event (GstDVDSpu * dvdspu)
757 {
758 SpuState *state = &dvdspu->spu_state;
759
760 if (state->pgs.pending_cmd) {
761 gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd);
762 gst_buffer_unref (state->pgs.pending_cmd);
763 state->pgs.pending_cmd = NULL;
764 }
765
766 state->next_ts = GST_CLOCK_TIME_NONE;
767
768 state->flags &= ~SPU_STATE_DISPLAY;
769 if (state->pgs.have_presentation_segment) {
770 if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0)
771 state->flags |= SPU_STATE_DISPLAY;
772 }
773 return FALSE;
774 }
775
776 void
777 gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame)
778 {
779 SpuState *state = &dvdspu->spu_state;
780 PgsPresentationSegment *ps = &state->pgs.pres_seg;
781 guint i;
782
783 if (ps->objects == NULL)
784 return;
785
786 for (i = 0; i < ps->objects->len; i++) {
787 PgsCompositionObject *cur =
788 &g_array_index (ps->objects, PgsCompositionObject, i);
789 pgs_composition_object_render (cur, state, frame);
790 }
791 }
792
793 gboolean
794 gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event)
795 {
796 gst_event_unref (event);
797 return FALSE;
798 }
799
800 void
801 gstspu_pgs_flush (GstDVDSpu * dvdspu)
802 {
803 SpuPgsState *pgs_state = &dvdspu->spu_state.pgs;
804
805 if (pgs_state->pending_cmd) {
806 gst_buffer_unref (pgs_state->pending_cmd);
807 pgs_state->pending_cmd = NULL;
808 }
809
810 pgs_state->have_presentation_segment = FALSE;
811 pgs_state->in_presentation_segment = FALSE;
812 pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0);
813
814 pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0;
815 }
816