| /* GStreamer Sub-Picture Unit - PGS handling |
| * Copyright (C) 2009 Jan Schmidt <thaytan@noraisin.net> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| */ |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include <string.h> |
| #include <gst/gst.h> |
| |
| #include "gstdvdspu.h" |
| #include "gstspu-pgs.h" |
| |
| const struct PgsFrameRateEntry |
| { |
| guint8 id; |
| guint fps_n; |
| guint fps_d; |
| } PgsFrameRates[] = { |
| { |
| 64, 30000, 1001} /* 29.97 FPS */ |
| }; |
| |
| typedef enum PgsCommandType PgsCommandType; |
| |
| enum PgsCommandType |
| { |
| PGS_COMMAND_SET_PALETTE = 0x14, |
| PGS_COMMAND_SET_OBJECT_DATA = 0x15, |
| PGS_COMMAND_PRESENTATION_SEGMENT = 0x16, |
| PGS_COMMAND_SET_WINDOW = 0x17, |
| PGS_COMMAND_INTERACTIVE_SEGMENT = 0x18, |
| |
| PGS_COMMAND_END_DISPLAY = 0x80, |
| |
| PGS_COMMAND_INVALID = 0xFFFF |
| }; |
| |
| static gint gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf); |
| |
| #define DUMP_CMDS 0 |
| #define DUMP_FULL_IMAGE 0 |
| #define DUMP_FULL_PALETTE 0 |
| |
| #if DUMP_CMDS |
| #define PGS_DUMP(...) g_print(__VA_ARGS__) |
| #else |
| #define PGS_DUMP(...) |
| #endif |
| |
| static void |
| dump_bytes (guint8 * data, guint16 len) |
| { |
| gint i; |
| |
| /* Dump the numbers */ |
| for (i = 0; i < len; i++) { |
| PGS_DUMP ("0x%02x ", data[i]); |
| if (!((i + 1) % 16)) |
| PGS_DUMP ("\n"); |
| } |
| if (len > 0 && (i % 16)) |
| PGS_DUMP ("\n"); |
| } |
| |
| static void |
| dump_rle_data (GstDVDSpu * dvdspu, guint8 * data, guint32 len) |
| { |
| guint16 obj_h G_GNUC_UNUSED; |
| guint16 obj_w; |
| guint8 *end = data + len; |
| guint x = 0; |
| |
| if (data + 4 > end) |
| return; |
| |
| /* RLE data: */ |
| obj_w = GST_READ_UINT16_BE (data); |
| obj_h = GST_READ_UINT16_BE (data + 2); |
| data += 4; |
| PGS_DUMP ("RLE image is %ux%u\n", obj_w, obj_h); |
| |
| while (data < end) { |
| guint8 pal_id; |
| guint16 run_len; |
| |
| pal_id = *data++; |
| if (pal_id != 0) { |
| // PGS_DUMP ("data 0x%02x\n", data[0]); |
| run_len = 1; |
| } else { |
| if (data + 1 > end) |
| return; |
| switch (data[0] & 0xC0) { |
| case 0x00: |
| //PGS_DUMP ("data 0x%02x\n", data[0]); |
| run_len = (data[0] & 0x3f); |
| data++; |
| break; |
| case 0x40: |
| if (data + 2 > end) |
| return; |
| //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); |
| run_len = ((data[0] << 8) | data[1]) & 0x3fff; |
| data += 2; |
| break; |
| case 0x80: |
| if (data + 2 > end) |
| return; |
| //PGS_DUMP ("data 0x%02x 0x%02x\n", data[0], data[1]); |
| run_len = (data[0] & 0x3f); |
| pal_id = data[1]; |
| data += 2; |
| break; |
| case 0xC0: |
| if (data + 3 > end) |
| return; |
| //PGS_DUMP ("data 0x%02x 0x%02x 0x%02x\n", data[0], data[1], data[2]); |
| run_len = ((data[0] << 8) | data[1]) & 0x3fff; |
| pal_id = data[2]; |
| data += 3; |
| break; |
| default: |
| run_len = 0; |
| break; |
| } |
| } |
| |
| #if DUMP_FULL_IMAGE |
| { |
| gint i; |
| #if 1 |
| if (dvdspu->spu_state.pgs.palette[pal_id].A) { |
| guint8 val = dvdspu->spu_state.pgs.palette[pal_id].A; |
| for (i = 0; i < run_len; i++) |
| PGS_DUMP ("%02x ", val); |
| } else { |
| for (i = 0; i < run_len; i++) |
| PGS_DUMP (" "); |
| } |
| if (!run_len || (x + run_len) > obj_w) |
| PGS_DUMP ("\n"); |
| #else |
| PGS_DUMP ("Run x: %d pix: %d col: %d\n", x, run_len, pal_id); |
| #endif |
| } |
| #endif |
| |
| x += run_len; |
| if (!run_len || x > obj_w) |
| x = 0; |
| }; |
| |
| PGS_DUMP ("\n"); |
| } |
| |
| static void |
| pgs_composition_object_render (PgsCompositionObject * obj, SpuState * state, |
| GstVideoFrame * frame) |
| { |
| SpuColour *colour; |
| guint8 *planes[3]; /* YUV frame pointers */ |
| gint strides[3]; |
| guint8 *data, *end; |
| guint16 obj_w; |
| guint16 obj_h G_GNUC_UNUSED; |
| guint x, y, i, min_x, max_x; |
| |
| if (G_UNLIKELY (obj->rle_data == NULL || obj->rle_data_size == 0 |
| || obj->rle_data_used != obj->rle_data_size)) |
| return; |
| |
| data = obj->rle_data; |
| end = data + obj->rle_data_used; |
| |
| if (data + 4 > end) |
| return; |
| |
| /* FIXME: Calculate and use the cropping window for the output, as the |
| * intersection of the crop rectangle for this object (if any) and the |
| * window specified by the object's window_id */ |
| |
| /* Store the start of each plane */ |
| planes[0] = GST_VIDEO_FRAME_COMP_DATA (frame, 0); |
| planes[1] = GST_VIDEO_FRAME_COMP_DATA (frame, 1); |
| planes[2] = GST_VIDEO_FRAME_COMP_DATA (frame, 2); |
| |
| strides[0] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); |
| strides[1] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); |
| strides[2] = GST_VIDEO_FRAME_COMP_STRIDE (frame, 2); |
| |
| y = MIN (obj->y, state->info.height); |
| |
| planes[0] += strides[0] * y; |
| planes[1] += strides[1] * (y / 2); |
| planes[2] += strides[2] * (y / 2); |
| |
| /* RLE data: */ |
| obj_w = GST_READ_UINT16_BE (data); |
| obj_h = GST_READ_UINT16_BE (data + 2); |
| data += 4; |
| |
| min_x = MIN (obj->x, strides[0]); |
| max_x = MIN (obj->x + obj_w, strides[0]); |
| |
| state->comp_left = x = min_x; |
| state->comp_right = max_x; |
| |
| gstspu_clear_comp_buffers (state); |
| |
| while (data < end) { |
| guint8 pal_id; |
| guint16 run_len; |
| |
| pal_id = *data++; |
| if (pal_id != 0) { |
| run_len = 1; |
| } else { |
| if (data + 1 > end) |
| return; |
| switch (data[0] & 0xC0) { |
| case 0x00: |
| run_len = (data[0] & 0x3f); |
| data++; |
| break; |
| case 0x40: |
| if (data + 2 > end) |
| return; |
| run_len = ((data[0] << 8) | data[1]) & 0x3fff; |
| data += 2; |
| break; |
| case 0x80: |
| if (data + 2 > end) |
| return; |
| run_len = (data[0] & 0x3f); |
| pal_id = data[1]; |
| data += 2; |
| break; |
| case 0xC0: |
| if (data + 3 > end) |
| return; |
| run_len = ((data[0] << 8) | data[1]) & 0x3fff; |
| pal_id = data[2]; |
| data += 3; |
| break; |
| default: |
| run_len = 0; |
| break; |
| } |
| } |
| |
| colour = &state->pgs.palette[pal_id]; |
| if (colour->A) { |
| guint32 inv_A = 0xff - colour->A; |
| if (G_UNLIKELY (x + run_len > max_x)) |
| run_len = (max_x - x); |
| |
| for (i = 0; i < run_len; i++) { |
| planes[0][x] = (inv_A * planes[0][x] + colour->Y) / 0xff; |
| |
| state->comp_bufs[0][x / 2] += colour->U; |
| state->comp_bufs[1][x / 2] += colour->V; |
| state->comp_bufs[2][x / 2] += colour->A; |
| x++; |
| } |
| } else { |
| x += run_len; |
| } |
| |
| if (!run_len || x > max_x) { |
| x = min_x; |
| planes[0] += strides[0]; |
| |
| if (y % 2) { |
| gstspu_blend_comp_buffers (state, planes); |
| gstspu_clear_comp_buffers (state); |
| |
| planes[1] += strides[1]; |
| planes[2] += strides[2]; |
| } |
| y++; |
| if (y >= state->info.height) |
| return; /* Hit the bottom */ |
| } |
| } |
| |
| if (y % 2) |
| gstspu_blend_comp_buffers (state, planes); |
| } |
| |
| static void |
| pgs_composition_object_clear (PgsCompositionObject * obj) |
| { |
| if (obj->rle_data) { |
| g_free (obj->rle_data); |
| obj->rle_data = NULL; |
| } |
| obj->rle_data_size = obj->rle_data_used = 0; |
| } |
| |
| static void |
| pgs_presentation_segment_set_object_count (PgsPresentationSegment * ps, |
| guint8 n_objects) |
| { |
| if (ps->objects == NULL) { |
| ps->objects = |
| g_array_sized_new (FALSE, TRUE, sizeof (PgsCompositionObject), |
| n_objects); |
| g_array_set_size (ps->objects, n_objects); |
| return; |
| } |
| |
| /* Clear memory in any extraneous objects */ |
| if (ps->objects->len > n_objects) { |
| guint i; |
| for (i = n_objects; i < ps->objects->len; i++) { |
| PgsCompositionObject *cur = |
| &g_array_index (ps->objects, PgsCompositionObject, i); |
| pgs_composition_object_clear (cur); |
| } |
| } |
| |
| g_array_set_size (ps->objects, n_objects); |
| |
| if (n_objects == 0) { |
| g_array_free (ps->objects, TRUE); |
| ps->objects = NULL; |
| } |
| } |
| |
| static PgsCompositionObject * |
| pgs_presentation_segment_find_object (PgsPresentationSegment * ps, |
| guint16 obj_id) |
| { |
| guint i; |
| if (ps->objects == NULL) |
| return NULL; |
| |
| for (i = 0; i < ps->objects->len; i++) { |
| PgsCompositionObject *cur = |
| &g_array_index (ps->objects, PgsCompositionObject, i); |
| if (cur->id == obj_id) |
| return cur; |
| } |
| |
| return NULL; |
| } |
| |
| static int |
| parse_presentation_segment (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, |
| guint16 len) |
| { |
| guint8 *end = payload + len; |
| PgsPresentationSegment *ps = &dvdspu->spu_state.pgs.pres_seg; |
| guint8 n_objects, palette_id; |
| gint i; |
| |
| /* Parse video descriptor */ |
| if (payload + 5 > end) |
| return 0; |
| |
| ps->vid_w = GST_READ_UINT16_BE (payload); |
| ps->vid_h = GST_READ_UINT16_BE (payload + 2); |
| ps->vid_fps_code = payload[4]; |
| payload += 5; |
| |
| /* Parse composition descriptor */ |
| if (payload + 3 > end) |
| return 0; |
| ps->composition_no = GST_READ_UINT16_BE (payload); |
| ps->composition_state = payload[2]; |
| payload += 3; |
| |
| /* Parse other bits */ |
| if (payload + 3 > end) |
| return 0; |
| |
| ps->flags = payload[0]; |
| |
| palette_id = payload[1]; |
| n_objects = payload[2]; |
| payload += 3; |
| |
| if (ps->flags & PGS_PRES_SEGMENT_FLAG_UPDATE_PALETTE) |
| ps->palette_id = palette_id; |
| |
| PGS_DUMP ("Video width %u height %u fps code %u\n", ps->vid_w, ps->vid_h, |
| ps->vid_fps_code); |
| PGS_DUMP |
| ("Composition num %u state 0x%02x flags 0x%02x palette id %u n_objects %u\n", |
| ps->composition_no, ps->composition_state, ps->flags, ps->palette_id, |
| n_objects); |
| |
| pgs_presentation_segment_set_object_count (ps, n_objects); |
| |
| for (i = 0; i < (gint) n_objects; i++) { |
| PgsCompositionObject *obj = |
| &g_array_index (ps->objects, PgsCompositionObject, i); |
| |
| if (payload + 8 > end) |
| break; |
| obj->id = GST_READ_UINT16_BE (payload); |
| obj->win_id = payload[2]; |
| obj->flags = payload[3]; |
| obj->x = GST_READ_UINT16_BE (payload + 4); |
| obj->y = GST_READ_UINT16_BE (payload + 6); |
| obj->rle_data_size = obj->rle_data_used = 0; |
| |
| payload += 8; |
| |
| PGS_DUMP ("Composition object %d Object ID %u Window ID %u flags 0x%02x " |
| "x %u y %u\n", i, obj->id, obj->win_id, obj->flags, obj->x, obj->y); |
| |
| if (obj->flags & PGS_COMPOSITION_OBJECT_FLAG_CROPPED) { |
| if (payload + 8 > end) |
| break; |
| |
| obj->crop_x = GST_READ_UINT16_BE (payload); |
| obj->crop_y = GST_READ_UINT16_BE (payload + 2); |
| obj->crop_w = GST_READ_UINT16_BE (payload + 4); |
| obj->crop_h = GST_READ_UINT16_BE (payload + 6); |
| |
| payload += 8; |
| |
| PGS_DUMP ("Cropping window x %u y %u w %u h %u\n", |
| obj->crop_x, obj->crop_y, obj->crop_w, obj->crop_h); |
| } |
| |
| if (obj->flags & ~(PGS_COMPOSITION_OBJECT_FLAG_CROPPED | |
| PGS_COMPOSITION_OBJECT_FLAG_FORCED)) |
| GST_ERROR ("PGS Composition Object has unknown flags: 0x%02x", |
| obj->flags); |
| } |
| |
| if (payload != end) { |
| GST_ERROR ("PGS Composition Object: %" G_GSSIZE_FORMAT |
| " bytes not consumed", (gssize) (end - payload)); |
| dump_bytes (payload, end - payload); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| parse_set_palette (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, |
| guint16 len) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| |
| const gint PGS_PALETTE_ENTRY_SIZE = 5; |
| guint8 *end = payload + len; |
| guint8 palette_id G_GNUC_UNUSED; |
| guint8 palette_version G_GNUC_UNUSED; |
| gint n_entries, i; |
| |
| if (len < 2) /* Palette command too short */ |
| return 0; |
| palette_id = payload[0]; |
| palette_version = payload[1]; |
| payload += 2; |
| |
| n_entries = (len - 2) / PGS_PALETTE_ENTRY_SIZE; |
| |
| PGS_DUMP ("Palette ID %u version %u. %d entries\n", |
| palette_id, palette_version, n_entries); |
| for (i = 0; i < 256; i++) |
| state->pgs.palette[i].A = 0; |
| for (i = 0; i < n_entries; i++) { |
| guint8 n, Y, U, V, A; |
| n = payload[0]; |
| Y = payload[1]; |
| U = payload[2]; |
| V = payload[3]; |
| A = payload[4]; |
| |
| #if DUMP_FULL_PALETTE |
| PGS_DUMP ("Entry %3d: Y %3d U %3d V %3d A %3d ", n, Y, U, V, A); |
| if (((i + 1) % 2) == 0) |
| PGS_DUMP ("\n"); |
| #endif |
| |
| /* Premultiply the palette entries by the alpha */ |
| state->pgs.palette[n].Y = Y * A; |
| state->pgs.palette[n].U = U * A; |
| state->pgs.palette[n].V = V * A; |
| state->pgs.palette[n].A = A; |
| |
| payload += PGS_PALETTE_ENTRY_SIZE; |
| } |
| |
| #if DUMP_FULL_PALETTE |
| if (n_entries > 0 && (i % 2)) |
| PGS_DUMP ("\n"); |
| #endif |
| |
| if (payload != end) { |
| GST_ERROR ("PGS Set Palette: %" G_GSSIZE_FORMAT " bytes not consumed", |
| (gssize) (end - payload)); |
| dump_bytes (payload, end - payload); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| parse_set_window (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, |
| guint16 len) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| guint8 *end = payload + len; |
| guint8 win_count, win_id G_GNUC_UNUSED; |
| gint i; |
| |
| if (payload + 1 > end) |
| return 0; |
| |
| dump_bytes (payload, len); |
| |
| win_count = payload[0]; |
| payload++; |
| |
| for (i = 0; i < win_count; i++) { |
| if (payload + 9 > end) |
| return 0; |
| |
| /* FIXME: Store each window ID separately into an array */ |
| win_id = payload[0]; |
| state->pgs.win_x = GST_READ_UINT16_BE (payload + 1); |
| state->pgs.win_y = GST_READ_UINT16_BE (payload + 3); |
| state->pgs.win_w = GST_READ_UINT16_BE (payload + 5); |
| state->pgs.win_h = GST_READ_UINT16_BE (payload + 7); |
| payload += 9; |
| |
| PGS_DUMP ("Win ID %u x %d y %d w %d h %d\n", |
| win_id, state->pgs.win_x, state->pgs.win_y, state->pgs.win_w, |
| state->pgs.win_h); |
| } |
| |
| if (payload != end) { |
| GST_ERROR ("PGS Set Window: %" G_GSSIZE_FORMAT " bytes not consumed", |
| (gssize) (end - payload)); |
| dump_bytes (payload, end - payload); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| parse_set_object_data (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, |
| guint16 len) |
| { |
| SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; |
| PgsCompositionObject *obj; |
| guint8 *end = payload + len; |
| guint16 obj_id; |
| guint8 obj_ver, flags; |
| |
| if (payload + 4 > end) |
| return 0; |
| |
| obj_id = GST_READ_UINT16_BE (payload); |
| obj_ver = payload[2]; |
| flags = payload[3]; |
| payload += 4; |
| |
| obj = pgs_presentation_segment_find_object (&(pgs_state->pres_seg), obj_id); |
| |
| PGS_DUMP ("Object ID %d ver %u flags 0x%02x\n", obj_id, obj_ver, flags); |
| |
| if (flags & PGS_OBJECT_UPDATE_FLAG_START_RLE) { |
| obj->rle_data_ver = obj_ver; |
| |
| if (payload + 3 > end) |
| return 0; |
| |
| obj->rle_data_size = GST_READ_UINT24_BE (payload); |
| payload += 3; |
| |
| PGS_DUMP ("%d bytes of RLE data, of %d bytes total.\n", |
| end - payload, obj->rle_data_size); |
| |
| obj->rle_data = g_realloc (obj->rle_data, obj->rle_data_size); |
| obj->rle_data_used = end - payload; |
| memcpy (obj->rle_data, payload, end - payload); |
| payload = end; |
| } else { |
| PGS_DUMP ("%d bytes of additional RLE data\n", end - payload); |
| /* Check that the data chunk is for this object version, and fits in the buffer */ |
| if (obj->rle_data_ver == obj_ver && |
| obj->rle_data_used + end - payload <= obj->rle_data_size) { |
| |
| memcpy (obj->rle_data + obj->rle_data_used, payload, end - payload); |
| obj->rle_data_used += end - payload; |
| payload = end; |
| } |
| } |
| |
| if (obj->rle_data_size == obj->rle_data_used) |
| dump_rle_data (dvdspu, obj->rle_data, obj->rle_data_size); |
| |
| if (payload != end) { |
| GST_ERROR ("PGS Set Object Data: %" G_GSSIZE_FORMAT " bytes not consumed", |
| (gssize) (end - payload)); |
| dump_bytes (payload, end - payload); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| parse_pgs_packet (GstDVDSpu * dvdspu, guint8 type, guint8 * payload, |
| guint16 len) |
| { |
| SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; |
| int ret = 0; |
| |
| if (!pgs_state->in_presentation_segment |
| && type != PGS_COMMAND_PRESENTATION_SEGMENT) { |
| PGS_DUMP ("Expected BEGIN PRESENTATION SEGMENT command. " |
| "Got command type 0x%02x len %u. Skipping\n", type, len); |
| return 0; |
| } |
| |
| switch (type) { |
| case PGS_COMMAND_PRESENTATION_SEGMENT: |
| PGS_DUMP ("*******************************************\n" |
| "Begin PRESENTATION_SEGMENT (0x%02x) packet len %u\n", type, len); |
| pgs_state->in_presentation_segment = |
| pgs_state->have_presentation_segment = TRUE; |
| ret = parse_presentation_segment (dvdspu, type, payload, len); |
| break; |
| case PGS_COMMAND_SET_OBJECT_DATA: |
| PGS_DUMP ("*** Set Object Data (0x%02x) packet len %u\n", type, len); |
| ret = parse_set_object_data (dvdspu, type, payload, len); |
| break; |
| case PGS_COMMAND_SET_PALETTE: |
| PGS_DUMP ("*** Set Palette (0x%02x) packet len %u\n", type, len); |
| ret = parse_set_palette (dvdspu, type, payload, len); |
| break; |
| case PGS_COMMAND_SET_WINDOW: |
| PGS_DUMP ("*** Set Window command (0x%02x) packet len %u\n", type, len); |
| ret = parse_set_window (dvdspu, type, payload, len); |
| break; |
| case PGS_COMMAND_INTERACTIVE_SEGMENT: |
| PGS_DUMP ("*** Interactive Segment command(0x%02x) packet len %u\n", |
| type, len); |
| dump_bytes (payload, len); |
| break; |
| case PGS_COMMAND_END_DISPLAY: |
| PGS_DUMP ("*** End Display command (0x%02x) packet len %u\n", type, |
| len); |
| pgs_state->in_presentation_segment = FALSE; |
| break; |
| default: |
| GST_ERROR ("Unknown PGS command: type 0x%02x len %u", type, len); |
| dump_bytes (payload, len); |
| break; |
| } |
| PGS_DUMP ("\n"); |
| |
| return ret; |
| } |
| |
| gint |
| gstspu_exec_pgs_buffer (GstDVDSpu * dvdspu, GstBuffer * buf) |
| { |
| GstMapInfo map; |
| guint8 *pos, *end; |
| guint8 type; |
| guint16 packet_len; |
| |
| gst_buffer_map (buf, &map, GST_MAP_READ); |
| |
| pos = map.data; |
| end = pos + map.size; |
| |
| /* Need at least 3 bytes */ |
| if (pos + 3 > end) { |
| PGS_DUMP ("Not enough bytes to be a PGS packet\n"); |
| goto error; |
| } |
| |
| PGS_DUMP ("Begin dumping command buffer of size %u ts %" GST_TIME_FORMAT "\n", |
| end - pos, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); |
| do { |
| type = *pos++; |
| packet_len = GST_READ_UINT16_BE (pos); |
| pos += 2; |
| |
| if (pos + packet_len > end) { |
| gst_buffer_unmap (buf, &map); |
| PGS_DUMP ("Invalid packet length %u (only have %u bytes)\n", packet_len, |
| end - pos); |
| goto error; |
| } |
| |
| if (parse_pgs_packet (dvdspu, type, pos, packet_len)) |
| goto error; |
| |
| pos += packet_len; |
| } while (pos + 3 <= end); |
| |
| PGS_DUMP ("End dumping command buffer with %u bytes remaining\n", end - pos); |
| return (pos - map.data); |
| |
| /* ERRORS */ |
| error: |
| { |
| gst_buffer_unmap (buf, &map); |
| return -1; |
| } |
| } |
| |
| void |
| gstspu_pgs_handle_new_buf (GstDVDSpu * dvdspu, GstClockTime event_ts, |
| GstBuffer * buf) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| |
| state->next_ts = event_ts; |
| state->pgs.pending_cmd = buf; |
| } |
| |
| gboolean |
| gstspu_pgs_execute_event (GstDVDSpu * dvdspu) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| |
| if (state->pgs.pending_cmd) { |
| gstspu_exec_pgs_buffer (dvdspu, state->pgs.pending_cmd); |
| gst_buffer_unref (state->pgs.pending_cmd); |
| state->pgs.pending_cmd = NULL; |
| } |
| |
| state->next_ts = GST_CLOCK_TIME_NONE; |
| |
| state->flags &= ~SPU_STATE_DISPLAY; |
| if (state->pgs.have_presentation_segment) { |
| if (state->pgs.pres_seg.objects && state->pgs.pres_seg.objects->len > 0) |
| state->flags |= SPU_STATE_DISPLAY; |
| } |
| return FALSE; |
| } |
| |
| void |
| gstspu_pgs_render (GstDVDSpu * dvdspu, GstVideoFrame * frame) |
| { |
| SpuState *state = &dvdspu->spu_state; |
| PgsPresentationSegment *ps = &state->pgs.pres_seg; |
| guint i; |
| |
| if (ps->objects == NULL) |
| return; |
| |
| for (i = 0; i < ps->objects->len; i++) { |
| PgsCompositionObject *cur = |
| &g_array_index (ps->objects, PgsCompositionObject, i); |
| pgs_composition_object_render (cur, state, frame); |
| } |
| } |
| |
| gboolean |
| gstspu_pgs_handle_dvd_event (GstDVDSpu * dvdspu, GstEvent * event) |
| { |
| return FALSE; |
| } |
| |
| void |
| gstspu_pgs_flush (GstDVDSpu * dvdspu) |
| { |
| SpuPgsState *pgs_state = &dvdspu->spu_state.pgs; |
| |
| if (pgs_state->pending_cmd) { |
| gst_buffer_unref (pgs_state->pending_cmd); |
| pgs_state->pending_cmd = NULL; |
| } |
| |
| pgs_state->have_presentation_segment = FALSE; |
| pgs_state->in_presentation_segment = FALSE; |
| pgs_presentation_segment_set_object_count (&pgs_state->pres_seg, 0); |
| |
| pgs_state->win_x = pgs_state->win_y = pgs_state->win_w = pgs_state->win_h = 0; |
| } |