diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libspuhdmv/xine_hdmv_decoder.c | 278 |
1 files changed, 177 insertions, 101 deletions
diff --git a/src/libspuhdmv/xine_hdmv_decoder.c b/src/libspuhdmv/xine_hdmv_decoder.c index be5e6b638..d0a82da09 100644 --- a/src/libspuhdmv/xine_hdmv_decoder.c +++ b/src/libspuhdmv/xine_hdmv_decoder.c @@ -46,9 +46,22 @@ #endif #define XINE_HDMV_TRACE(x...) printf(x) -/*#define TRACE(x...) */ +/*#define XINE_HDMV_TRACE(x...) */ #define XINE_HDMV_ERROR(x...) fprintf(stderr, "spuhdmv: " x) -/*#define ERROR(x...) lprintf(x) */ +/*#define XINE_HDMV_ERROR(x...) lprintf(x) */ + +#ifndef MAX +# define MAX(a,b) (a>b)?(a):(b) +#endif + +enum { + SEGTYPE_PALETTE = 0x14, + SEGTYPE_OBJECT = 0x15, + SEGTYPE_PRESENTATION_SEGMENT = 0x16, + SEGTYPE_WINDOW_DEFINITION = 0x17, + SEGTYPE_INTERACTIVE = 0x18, + SEGTYPE_END_OF_DISPLAY = 0x80, +} eSegmentType; /* * cached palette (xine-lib format) @@ -72,15 +85,16 @@ struct subtitle_object_s { uint16_t xpos, ypos; uint16_t width, height; + /* xine format */ rle_elem_t *rle; unsigned int num_rle; size_t data_size; -#if 0 - uint8_t *raw_data; /* partial RLE data in HDMV format */ - size_t raw_data_len; - size_t raw_data_size; -#endif + /* HDMV format (used when object does not fit to single segment) */ + uint32_t data_len; /* size of complete object */ + uint8_t *raw_data; /* partial RLE data in HDMV format */ + size_t raw_data_len; /* bytes buffered */ + size_t raw_data_size; /* allocated size */ subtitle_object_t *next; @@ -108,7 +122,7 @@ struct window_def_s { typedef struct composition_object_s composition_object_t; struct composition_object_s { uint8_t window_id_ref; - uint8_t object_id_ref; + uint16_t object_id_ref; uint16_t xpos, ypos; @@ -149,27 +163,27 @@ struct presentation_segment_s { */ #define LIST_REPLACE(list, obj, FREE_FUNC) \ - do { \ - unsigned int id = obj->id; \ - \ - /* insert to list */ \ - obj->next = list; \ - list = obj; \ - \ - /* remove old */ \ - while (obj->next && obj->next->id != id) \ - obj = obj->next; \ - if (obj->next) { \ - void *tmp = (void*)obj->next; \ - obj->next = obj->next->next; \ - FREE_FUNC(tmp); \ - } \ + do { \ + unsigned int id = obj->id; \ + \ + /* insert to list */ \ + obj->next = list; \ + list = obj; \ + \ + /* remove old */ \ + while (obj->next && obj->next->id != id) \ + obj = obj->next; \ + if (obj->next) { \ + void *tmp = (void*)obj->next; \ + obj->next = obj->next->next; \ + FREE_FUNC(tmp); \ + } \ } while (0); #define LIST_DESTROY(list, FREE_FUNC) \ - while (list) { \ + while (list) { \ void *tmp = (void*)list; \ - list = list->next; \ + list = list->next; \ FREE_FUNC(tmp); \ } @@ -177,6 +191,7 @@ static void free_subtitle_object(void *ptr) { if (ptr) { free(((subtitle_object_t*)ptr)->rle); + free(((subtitle_object_t*)ptr)->raw_data); free(ptr); } } @@ -250,8 +265,8 @@ static void segbuf_parse_segment_header(segment_buffer_t *buf) buf->error = 0; if ( buf->segment_type < 0x14 || - ( buf->segment_type > 0x18 && - buf->segment_type != 0x80)) { + ( buf->segment_type > 0x18 && + buf->segment_type != 0x80)) { XINE_HDMV_ERROR("unknown segment type, resetting\n"); segbuf_reset(buf); } @@ -279,7 +294,7 @@ static void segbuf_fill(segment_buffer_t *buf, uint8_t *data, size_t len) static int segbuf_segment_complete(segment_buffer_t *buf) { - return (buf->segment_len >= 0) && (buf->len >= buf->segment_len + 3); + return (buf->segment_len >= 0) && (buf->len >= (unsigned)buf->segment_len + 3); } static void segbuf_skip_segment(segment_buffer_t *buf) @@ -294,7 +309,7 @@ static void segbuf_skip_segment(segment_buffer_t *buf) XINE_HDMV_TRACE(" skip_segment: %zd bytes left\n", buf->len); } else { XINE_HDMV_ERROR(" skip_segment: ERROR - %zd bytes queued, %d required\n", - buf->len, buf->segment_len); + buf->len, buf->segment_len); segbuf_reset (buf); } } @@ -319,7 +334,7 @@ static uint8_t segbuf_get_u8(segment_buffer_t *buf) { if (!(buf->error = ++buf->segment_data > buf->segment_end)) return buf->segment_data[-1]; - XINE_HDMV_ERROR("segbuf_get_u8: read failed (end of segment reached) !"); + XINE_HDMV_ERROR("segbuf_get_u8: read failed (end of segment reached) !\n"); return 0; } @@ -333,19 +348,6 @@ static uint32_t segbuf_get_u24(segment_buffer_t *buf) return (segbuf_get_u8(buf) << 16) | (segbuf_get_u8(buf) << 8) | segbuf_get_u8(buf); } -static uint8_t *segbuf_get_string(segment_buffer_t *buf, size_t len) -{ - if (len > 0) { - uint8_t *val = buf->segment_data; - buf->segment_data += len; - if (buf->segment_data <= buf->segment_end) - return val; - } - XINE_HDMV_ERROR("segbuf_get_string(%zd): read failed (end of segment reached) !", len); - buf->error = 1; - return NULL; -} - /* * decode segments */ @@ -357,18 +359,18 @@ static subtitle_clut_t *segbuf_decode_palette(segment_buffer_t *buf) size_t len = segbuf_data_length(buf); size_t entries = len / 5; - int i; + size_t i; if (buf->error) return NULL; if (len % 5) { XINE_HDMV_ERROR(" decode_palette: segment size error (%zd ; expected %zd for %zd entries)\n", - len, (5 * entries), entries); + len, (5 * entries), entries); return NULL; } XINE_HDMV_TRACE("decode_palette: %zd items (id %d, version %d)\n", - entries, palette_id, palette_version_number); + entries, palette_id, palette_version_number); /* convert to xine-lib clut */ subtitle_clut_t *clut = calloc(1, sizeof(subtitle_clut_t)); @@ -409,17 +411,17 @@ static int segbuf_decode_rle(segment_buffer_t *buf, subtitle_object_t *obj) } else { byte = segbuf_get_u8 (buf); if (!(byte & 0x80)) { - rlep->color = 0; - if (!(byte & 0x40)) - rlep->len = byte & 0x3f; - else - rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf); + rlep->color = 0; + if (!(byte & 0x40)) + rlep->len = byte & 0x3f; + else + rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf); } else { - if (!(byte & 0x40)) - rlep->len = byte & 0x3f; - else - rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf); - rlep->color = segbuf_get_u8 (buf); + if (!(byte & 0x40)) + rlep->len = byte & 0x3f; + else + rlep->len = ((byte & 0x3f) << 8) | segbuf_get_u8 (buf); + rlep->color = segbuf_get_u8 (buf); } } @@ -431,10 +433,10 @@ static int segbuf_decode_rle(segment_buffer_t *buf, subtitle_object_t *obj) } else { /* end of line marker (00 00) */ if (x < obj->width) { - rlep->len = obj->width - x; - rlep->color = 0xff; - rlep++; - obj->num_rle ++; + rlep->len = obj->width - x; + rlep->color = 0xff; + rlep++; + obj->num_rle ++; } x = 0; y++; @@ -451,42 +453,104 @@ static int segbuf_decode_rle(segment_buffer_t *buf, subtitle_object_t *obj) return buf->error; } -static subtitle_object_t *segbuf_decode_object(segment_buffer_t *buf) +static subtitle_object_t *segbuf_decode_object(segment_buffer_t *buf, subtitle_object_t *objects) { - uint8_t object_id = segbuf_get_u16(buf); + uint16_t object_id = segbuf_get_u16(buf); uint8_t version = segbuf_get_u8 (buf); uint8_t seq_desc = segbuf_get_u8 (buf); XINE_HDMV_TRACE(" decode_object: object_id %d, version %d, seq 0x%x\n", - object_id, version, seq_desc); - - //LIST_FIND(); - subtitle_object_t *obj = calloc(1, sizeof(subtitle_object_t)); - obj->id = object_id; + object_id, version, seq_desc); if (seq_desc & 0x80) { + /* new object (first-in-sequence flag set) */ + + subtitle_object_t *obj = calloc(1, sizeof(subtitle_object_t)); - uint32_t data_len = segbuf_get_u24(buf); + obj->id = object_id; + obj->data_len = segbuf_get_u24(buf); obj->width = segbuf_get_u16(buf); obj->height = segbuf_get_u16(buf); - XINE_HDMV_TRACE(" object length %d bytes, size %dx%d\n", data_len, obj->width, obj->height); + if (buf->error) { + XINE_HDMV_TRACE(" decode error at object header\n"); + free_subtitle_object(obj); + return NULL; + } + + obj->data_len -= 4; /* width, height parsed */ + + XINE_HDMV_TRACE(" object length %d bytes, size %dx%d\n", obj->data_len, obj->width, obj->height); + + if (obj->data_len > segbuf_data_length(buf)) { + XINE_HDMV_TRACE(" object length %d bytes, have only %zd bytes -> missing %d bytes\n", + obj->data_len, segbuf_data_length(buf), obj->data_len - (int)segbuf_data_length(buf)); + + if (obj->raw_data) + free(obj->raw_data); + + /* store partial RLE data in HDMV format */ + obj->raw_data_len = segbuf_data_length(buf); + obj->raw_data_size = MAX(obj->data_len, obj->raw_data_len); + obj->raw_data = malloc(obj->raw_data_size); + memcpy(obj->raw_data, buf->segment_data, obj->raw_data_len); + + return obj; + } segbuf_decode_rle (buf, obj); if (buf->error) { + XINE_HDMV_TRACE(" decode error at RLE data\n"); free_subtitle_object(obj); return NULL; } - } else { - XINE_HDMV_ERROR(" TODO: APPEND RLE, length %d bytes\n", buf->segment_len - 4); - /* TODO */ - free_subtitle_object(obj); + return obj; + } + + /* not first-of-sequence --> append data to already existing objct */ + + /* search for object */ + while (objects && objects->id != object_id) + objects = objects->next; + + if (!objects) { + XINE_HDMV_TRACE(" object not found from list, discarding segment\n"); return NULL; } - return obj; + /* store partial RLE data in HDMV format */ + if (objects->raw_data_size < objects->raw_data_len + segbuf_data_length(buf)) { + XINE_HDMV_ERROR("object larger than object size !\n"); + return NULL; + } + memcpy(objects->raw_data + objects->raw_data_len, buf->segment_data, segbuf_data_length(buf)); + objects->raw_data_len += segbuf_data_length(buf); + + /* if complete, decode RLE data */ + if (objects->raw_data_len >= objects->data_len) { + /* create dummy buffer for segbuf_decode_rle */ + segment_buffer_t tmpbuf = { + .segment_data = objects->raw_data, + .segment_end = objects->raw_data + objects->raw_data_len, + }; + + /* decode RLE data */ + segbuf_decode_rle (&tmpbuf, objects); + + if (tmpbuf.error) { + XINE_HDMV_TRACE(" error decoding multi-segment object\n"); + } + + /* free decode buffer */ + free(objects->raw_data); + objects->raw_data = NULL; + objects->raw_data_len = 0; + objects->raw_data_size = 0; + } + + return NULL; } static window_def_t *segbuf_decode_window_definition(segment_buffer_t *buf) @@ -501,7 +565,7 @@ static window_def_t *segbuf_decode_window_definition(segment_buffer_t *buf) wnd->height = segbuf_get_u16 (buf); XINE_HDMV_TRACE(" window: [%02x %d] %d,%d %dx%d\n", a, - wnd->id, wnd->xpos, wnd->ypos, wnd->width, wnd->height); + wnd->id, wnd->xpos, wnd->ypos, wnd->width, wnd->height); if (buf->error) { free(wnd); @@ -518,13 +582,14 @@ static int segbuf_decode_video_descriptor(segment_buffer_t *buf) uint8_t frame_rate = segbuf_get_u8 (buf); XINE_HDMV_TRACE(" video_descriptor: %dx%d fps %d\n", width, height, frame_rate); + return buf->error; } static int segbuf_decode_composition_descriptor(segment_buffer_t *buf, composition_descriptor_t *descr) { descr->number = segbuf_get_u16(buf); - descr->state = segbuf_get_u8 (buf); + descr->state = segbuf_get_u8 (buf) & 0xc0; XINE_HDMV_TRACE(" composition_descriptor: number %d, state %d\n", descr->number, descr->state); return buf->error; @@ -556,8 +621,8 @@ static composition_object_t *segbuf_decode_composition_object(segment_buffer_t * } XINE_HDMV_TRACE(" composition_object: id: %d, win: %d, position %d,%d crop %d forced %d\n", - cobj->object_id_ref, cobj->window_id_ref, cobj->xpos, cobj->ypos, - cobj->cropped_flag, cobj->forced_flag); + cobj->object_id_ref, cobj->window_id_ref, cobj->xpos, cobj->ypos, + cobj->cropped_flag, cobj->forced_flag); return cobj; } @@ -575,7 +640,7 @@ static presentation_segment_t *segbuf_decode_presentation_segment(segment_buffer seg->object_number = segbuf_get_u8 (buf); XINE_HDMV_TRACE(" presentation_segment: object_number %d, palette %d\n", - seg->object_number, seg->palette_id_ref); + seg->object_number, seg->palette_id_ref); for (index = 0; index < seg->object_number; index++) { composition_object_t *cobj = segbuf_decode_composition_object (buf); @@ -628,6 +693,14 @@ typedef struct spuhdmv_decoder_s { } spuhdmv_decoder_t; +static void free_objs(spuhdmv_decoder_t *this) +{ + LIST_DESTROY (this->cluts, free); + LIST_DESTROY (this->objects, free_subtitle_object); + LIST_DESTROY (this->windows, free); + LIST_DESTROY (this->segments, free_presentation_segment); +} + static int decode_palette(spuhdmv_decoder_t *this) { /* decode */ @@ -643,7 +716,7 @@ static int decode_palette(spuhdmv_decoder_t *this) static int decode_object(spuhdmv_decoder_t *this) { /* decode */ - subtitle_object_t *obj = segbuf_decode_object(this->buf); + subtitle_object_t *obj = segbuf_decode_object(this->buf, this->objects); if (!obj) return 1; @@ -673,6 +746,11 @@ static int decode_presentation_segment(spuhdmv_decoder_t *this) seg->pts = this->pts; + /* epoch start or acquistion point -> drop cached objects */ + if (seg->comp_descr.state) { + free_objs(this); + } + /* replace */ if (this->segments) LIST_DESTROY(this->segments, free_presentation_segment); @@ -682,7 +760,7 @@ static int decode_presentation_segment(spuhdmv_decoder_t *this) } static int show_overlay(spuhdmv_decoder_t *this, composition_object_t *cobj, unsigned int palette_id_ref, - int overlay_index, int64_t pts, int force_update) + int overlay_index, int64_t pts, int force_update) { video_overlay_manager_t *ovl_manager = this->stream->video_out->get_overlay_manager(this->stream->video_out); metronom_t *metronom = this->stream->metronom; @@ -706,6 +784,10 @@ static int show_overlay(spuhdmv_decoder_t *this, composition_object_t *cobj, uns XINE_HDMV_TRACE(" show_overlay: object %d not found !\n", cobj->object_id_ref); return -1; } + if (!obj->rle) { + XINE_HDMV_TRACE(" show_overlay: object %d RLE data not decoded !\n", cobj->object_id_ref); + return -1; + } /* find window */ window_def_t *wnd = this->windows; @@ -746,7 +828,7 @@ static int show_overlay(spuhdmv_decoder_t *this, composition_object_t *cobj, uns overlay.hili_right = -1; XINE_HDMV_TRACE(" -> overlay: %d,%d %dx%d\n", - overlay.x, overlay.y, overlay.width, overlay.height); + overlay.x, overlay.y, overlay.width, overlay.height); /* set timings */ @@ -805,7 +887,7 @@ static void update_overlays(spuhdmv_decoder_t *this) while (pseg) { - if (!pseg->comp_descr.state) { + if (!pseg->object_number) { /* HIDE */ if (!pseg->shown) @@ -833,46 +915,40 @@ static void update_overlays(spuhdmv_decoder_t *this) } } -static void free_objs(spuhdmv_decoder_t *this) -{ - LIST_DESTROY (this->cluts, free); - LIST_DESTROY (this->objects, free_subtitle_object); - LIST_DESTROY (this->windows, free); - LIST_DESTROY (this->segments, free_presentation_segment); -} - static void decode_segment(spuhdmv_decoder_t *this) { - XINE_HDMV_TRACE("*** new segment, pts %010ld: 0x%02x (%8d bytes)", - this->pts, this->buf->segment_type, this->buf->segment_len); + XINE_HDMV_TRACE("*** new segment, pts %010"PRId64": 0x%02x (%8d bytes)\n", + this->pts, this->buf->segment_type, this->buf->segment_len); - switch (this->buf->segment_type) { - case 0x14: + switch (segbuf_segment_type(this->buf)) { + case SEGTYPE_PALETTE: XINE_HDMV_TRACE(" segment: PALETTE\n"); decode_palette(this); break; - case 0x15: + case SEGTYPE_OBJECT: XINE_HDMV_TRACE(" segment: OBJECT\n"); decode_object(this); break; - case 0x16: + case SEGTYPE_PRESENTATION_SEGMENT: XINE_HDMV_TRACE(" segment: PRESENTATION SEGMENT\n"); decode_presentation_segment(this); break; - case 0x17: + case SEGTYPE_WINDOW_DEFINITION: XINE_HDMV_TRACE(" segment: WINDOW DEFINITION\n"); decode_window_definition(this); break; - case 0x18: + case SEGTYPE_INTERACTIVE: XINE_HDMV_TRACE(" segment: INTERACTIVE\n"); break; - case 0x80: + case SEGTYPE_END_OF_DISPLAY: XINE_HDMV_TRACE(" segment: END OF DISPLAY\n"); +#if 0 /* drop all cached objects */ free_objs(this); +#endif break; default: - XINE_HDMV_ERROR(" segment type 0x%x unknown, skipping\n", this->buf->segment_type); + XINE_HDMV_ERROR(" segment type 0x%x unknown, skipping\n", segbuf_segment_type(this->buf)); break; } if (this->buf->error) { |