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