diff options
Diffstat (limited to 'libavcodec/dvbsubdec.c')
-rw-r--r-- | libavcodec/dvbsubdec.c | 238 |
1 files changed, 161 insertions, 77 deletions
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index 1dcefae4e3..9d74b1a0c0 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -2,20 +2,20 @@ * DVB subtitle decoding * Copyright (c) 2005 Ian Caulfield * - * This file is part of Libav. + * This file is part of FFmpeg. * - * Libav is free software; you can redistribute it and/or + * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * - * Libav is distributed in the hope that it will be useful, + * FFmpeg 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software + * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -152,6 +152,7 @@ static void png_save2(const char *filename, uint32_t *bitmap, int w, int h) typedef struct DVBSubCLUT { int id; + int version; uint32_t clut4[4]; uint32_t clut16[16]; @@ -178,6 +179,7 @@ typedef struct DVBSubObjectDisplay { typedef struct DVBSubObject { int id; + int version; int type; @@ -197,6 +199,7 @@ typedef struct DVBSubRegionDisplay { typedef struct DVBSubRegion { int id; + int version; int width; int height; @@ -207,6 +210,7 @@ typedef struct DVBSubRegion { uint8_t *pbuf; int buf_size; + int dirty; DVBSubObjectDisplay *display_list; @@ -226,12 +230,12 @@ typedef struct DVBSubContext { int composition_id; int ancillary_id; + int version; int time_out; DVBSubRegion *region_list; DVBSubCLUT *clut_list; DVBSubObject *object_list; - int display_list_size; DVBSubRegionDisplay *display_list; DVBSubDisplayDefinition *display_definition; } DVBSubContext; @@ -316,21 +320,10 @@ static void delete_region_display_list(DVBSubContext *ctx, DVBSubRegion *region) } -static void delete_state(DVBSubContext *ctx) +static void delete_cluts(DVBSubContext *ctx) { - DVBSubRegion *region; DVBSubCLUT *clut; - while (ctx->region_list) { - region = ctx->region_list; - - ctx->region_list = region->next; - - delete_region_display_list(ctx, region); - av_free(region->pbuf); - av_free(region); - } - while (ctx->clut_list) { clut = ctx->clut_list; @@ -338,12 +331,35 @@ static void delete_state(DVBSubContext *ctx) av_free(clut); } +} - av_freep(&ctx->display_definition); +static void delete_objects(DVBSubContext *ctx) +{ + DVBSubObject *object; + + while (ctx->object_list) { + object = ctx->object_list; + + ctx->object_list = object->next; - /* Should already be null */ - if (ctx->object_list) - av_log(0, AV_LOG_ERROR, "Memory deallocation error!\n"); + av_free(object); + } +} + +static void delete_regions(DVBSubContext *ctx) +{ + DVBSubRegion *region; + + while (ctx->region_list) { + region = ctx->region_list; + + ctx->region_list = region->next; + + delete_region_display_list(ctx, region); + + av_free(region->pbuf); + av_free(region); + } } static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) @@ -351,15 +367,21 @@ static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) int i, r, g, b, a = 0; DVBSubContext *ctx = avctx->priv_data; - if (!avctx->extradata || avctx->extradata_size != 4) { - av_log(avctx, AV_LOG_WARNING, "Invalid extradata, subtitle streams may be combined!\n"); + if (!avctx->extradata || (avctx->extradata_size < 4) || ((avctx->extradata_size % 5 != 0) && (avctx->extradata_size != 4))) { + av_log(avctx, AV_LOG_WARNING, "Invalid DVB subtitles stream extradata!\n"); ctx->composition_id = -1; ctx->ancillary_id = -1; } else { + if (avctx->extradata_size > 5) { + av_log(avctx, AV_LOG_WARNING, "Decoding first DVB subtitles sub-stream\n"); + } + ctx->composition_id = AV_RB16(avctx->extradata); ctx->ancillary_id = AV_RB16(avctx->extradata + 2); } + ctx->version = -1; + default_clut.id = -1; default_clut.next = NULL; @@ -428,7 +450,13 @@ static av_cold int dvbsub_close_decoder(AVCodecContext *avctx) DVBSubContext *ctx = avctx->priv_data; DVBSubRegionDisplay *display; - delete_state(ctx); + delete_regions(ctx); + + delete_objects(ctx); + + delete_cluts(ctx); + + av_freep(&ctx->display_definition); while (ctx->display_list) { display = ctx->display_list; @@ -442,16 +470,18 @@ static av_cold int dvbsub_close_decoder(AVCodecContext *avctx) static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, const uint8_t **srcbuf, int buf_size, - int non_mod, uint8_t *map_table) + int non_mod, uint8_t *map_table, int x_pos) { GetBitContext gb; int bits; int run_length; - int pixels_read = 0; + int pixels_read = x_pos; init_get_bits(&gb, *srcbuf, buf_size << 3); + destbuf += x_pos; + while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) { bits = get_bits(&gb, 2); @@ -512,14 +542,14 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } } } else if (bits == 1) { - pixels_read += 2; if (map_table) bits = map_table[0]; else bits = 0; - if (pixels_read <= dbuf_len) { - *destbuf++ = bits; + run_length = 2; + while (run_length-- > 0 && pixels_read < dbuf_len) { *destbuf++ = bits; + pixels_read++; } } else { (*srcbuf) += (get_bits_count(&gb) + 7) >> 3; @@ -547,16 +577,18 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, const uint8_t **srcbuf, int buf_size, - int non_mod, uint8_t *map_table) + int non_mod, uint8_t *map_table, int x_pos) { GetBitContext gb; int bits; int run_length; - int pixels_read = 0; + int pixels_read = x_pos; init_get_bits(&gb, *srcbuf, buf_size << 3); + destbuf += x_pos; + while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) { bits = get_bits(&gb, 4); @@ -636,14 +668,14 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } } } else if (bits == 1) { - pixels_read += 2; if (map_table) bits = map_table[0]; else bits = 0; - if (pixels_read <= dbuf_len) { - *destbuf++ = bits; + run_length = 2; + while (run_length-- > 0 && pixels_read < dbuf_len) { *destbuf++ = bits; + pixels_read++; } } else { if (map_table) @@ -668,12 +700,14 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len, const uint8_t **srcbuf, int buf_size, - int non_mod, uint8_t *map_table) + int non_mod, uint8_t *map_table, int x_pos) { const uint8_t *sbuf_end = (*srcbuf) + buf_size; int bits; int run_length; - int pixels_read = 0; + int pixels_read = x_pos; + + destbuf += x_pos; while (*srcbuf < sbuf_end && pixels_read < dbuf_len) { bits = *(*srcbuf)++; @@ -742,6 +776,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; uint8_t *map_table; +#if 0 av_dlog(avctx, "DVB pixel block size %d, %s field:\n", buf_size, top_bottom ? "bottom" : "top"); @@ -756,21 +791,22 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis if (i % 16) av_dlog(avctx, "\n"); +#endif if (region == 0) return; pbuf = region->pbuf; + region->dirty = 1; x_pos = display->x_pos; y_pos = display->y_pos; - if ((y_pos & 1) != top_bottom) - y_pos++; + y_pos += top_bottom; while (buf < buf_end) { - if (x_pos > region->width || y_pos > region->height) { - av_log(avctx, AV_LOG_ERROR, "Invalid object location!\n"); + if ((*buf!=0xf0 && x_pos >= region->width) || y_pos >= region->height) { + av_log(avctx, AV_LOG_ERROR, "Invalid object location! %d-%d %d-%d %02x\n", x_pos, region->width, y_pos, region->height, *buf); return; } @@ -783,9 +819,9 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis else map_table = NULL; - x_pos += dvbsub_read_2bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_end - buf, - non_mod, map_table); + x_pos = dvbsub_read_2bit_string(pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, map_table, x_pos); break; case 0x11: if (region->depth < 4) { @@ -798,9 +834,9 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis else map_table = NULL; - x_pos += dvbsub_read_4bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_end - buf, - non_mod, map_table); + x_pos = dvbsub_read_4bit_string(pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, map_table, x_pos); break; case 0x12: if (region->depth < 8) { @@ -808,9 +844,9 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis return; } - x_pos += dvbsub_read_8bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_end - buf, - non_mod, NULL); + x_pos = dvbsub_read_8bit_string(pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, NULL, x_pos); break; case 0x20: @@ -845,7 +881,6 @@ static void dvbsub_parse_object_segment(AVCodecContext *avctx, DVBSubContext *ctx = avctx->priv_data; const uint8_t *buf_end = buf + buf_size; - const uint8_t *block; int object_id; DVBSubObject *object; DVBSubObjectDisplay *display; @@ -876,7 +911,8 @@ static void dvbsub_parse_object_segment(AVCodecContext *avctx, } for (display = object->display_list; display; display = display->object_list_next) { - block = buf; + const uint8_t *block = buf; + int bfl = bottom_field_len; dvbsub_parse_pixel_data_block(avctx, display, block, top_field_len, 0, non_modifying_color); @@ -884,9 +920,9 @@ static void dvbsub_parse_object_segment(AVCodecContext *avctx, if (bottom_field_len > 0) block = buf + top_field_len; else - bottom_field_len = top_field_len; + bfl = top_field_len; - dvbsub_parse_pixel_data_block(avctx, display, block, bottom_field_len, 1, + dvbsub_parse_pixel_data_block(avctx, display, block, bfl, 1, non_modifying_color); } @@ -898,13 +934,14 @@ static void dvbsub_parse_object_segment(AVCodecContext *avctx, } -static void dvbsub_parse_clut_segment(AVCodecContext *avctx, +static int dvbsub_parse_clut_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size) { DVBSubContext *ctx = avctx->priv_data; const uint8_t *buf_end = buf + buf_size; int i, clut_id; + int version; DVBSubCLUT *clut; int entry_id, depth , full_range; int y, cr, cb, alpha; @@ -922,6 +959,7 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, av_dlog(avctx, "\n"); clut_id = *buf++; + version = ((*buf)>>4)&15; buf += 1; clut = get_clut(ctx, clut_id); @@ -932,11 +970,16 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, memcpy(clut, &default_clut, sizeof(DVBSubCLUT)); clut->id = clut_id; + clut->version = -1; clut->next = ctx->clut_list; ctx->clut_list = clut; } + if (clut->version != version) { + + clut->version = version; + while (buf + 4 < buf_end) { entry_id = *buf++; @@ -944,7 +987,7 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, if (depth == 0) { av_log(avctx, AV_LOG_ERROR, "Invalid clut depth 0x%x!\n", *buf); - return; + return 0; } full_range = (*buf++) & 1; @@ -970,14 +1013,21 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, YUV_TO_RGB2_CCIR(r, g, b, y); av_dlog(avctx, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha); + if (!!(depth & 0x80) + !!(depth & 0x40) + !!(depth & 0x20) > 1) { + av_dlog(avctx, "More than one bit level marked: %x\n", depth); + if (avctx->strict_std_compliance > FF_COMPLIANCE_NORMAL) + return AVERROR_INVALIDDATA; + } if (depth & 0x80) clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha); - if (depth & 0x40) + else if (depth & 0x40) clut->clut16[entry_id] = RGBA(r,g,b,255 - alpha); - if (depth & 0x20) + else if (depth & 0x20) clut->clut256[entry_id] = RGBA(r,g,b,255 - alpha); } + } + return 0; } @@ -988,6 +1038,7 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, const uint8_t *buf_end = buf + buf_size; int region_id, object_id; + int av_unused version; DVBSubRegion *region; DVBSubObject *object; DVBSubObjectDisplay *display; @@ -1004,11 +1055,13 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, region = av_mallocz(sizeof(DVBSubRegion)); region->id = region_id; + region->version = -1; region->next = ctx->region_list; ctx->region_list = region; } + version = ((*buf)>>4) & 15; fill = ((*buf++) >> 3) & 1; region->width = AV_RB16(buf); @@ -1024,6 +1077,7 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, region->pbuf = av_malloc(region->buf_size); fill = 1; + region->dirty = 0; } region->depth = 1 << (((*buf++) >> 2) & 7); @@ -1033,9 +1087,10 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, } region->clut = *buf++; - if (region->depth == 8) + if (region->depth == 8) { region->bgcolor = *buf++; - else { + buf += 1; + } else { buf += 1; if (region->depth == 4) @@ -1102,22 +1157,31 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, const uint8_t *buf_end = buf + buf_size; int region_id; int page_state; + int timeout; + int version; if (buf_size < 1) return; - ctx->time_out = *buf++; + timeout = *buf++; + version = ((*buf)>>4) & 15; page_state = ((*buf++) >> 2) & 3; + if (ctx->version != version) { + + ctx->time_out = timeout; + ctx->version = version; + av_dlog(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state); - if (page_state == 2) { - delete_state(ctx); + if (page_state == 1 || page_state == 2) { + delete_regions(ctx); + delete_objects(ctx); + delete_cluts(ctx); } tmp_display_list = ctx->display_list; ctx->display_list = NULL; - ctx->display_list_size = 0; while (buf + 5 < buf_end) { region_id = *buf++; @@ -1145,7 +1209,6 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, display->next = ctx->display_list; ctx->display_list = display; - ctx->display_list_size++; av_dlog(avctx, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos); } @@ -1157,6 +1220,7 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, av_free(display); } + } } @@ -1284,14 +1348,18 @@ static void dvbsub_parse_display_definition_segment(AVCodecContext *avctx, display_def->y = 0; display_def->width = bytestream_get_be16(&buf) + 1; display_def->height = bytestream_get_be16(&buf) + 1; + if (!avctx->width || !avctx->height) { + avctx->width = display_def->width; + avctx->height = display_def->height; + } if (buf_size < 13) return; if (info_byte & 1<<3) { // display_window_flag display_def->x = bytestream_get_be16(&buf); - display_def->y = bytestream_get_be16(&buf); display_def->width = bytestream_get_be16(&buf) - display_def->x + 1; + display_def->y = bytestream_get_be16(&buf); display_def->height = bytestream_get_be16(&buf) - display_def->y + 1; } } @@ -1310,38 +1378,43 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, int i; int offset_x=0, offset_y=0; - sub->rects = NULL; - sub->start_display_time = 0; sub->end_display_time = ctx->time_out * 1000; - sub->format = 0; if (display_def) { offset_x = display_def->x; offset_y = display_def->y; } - sub->num_rects = ctx->display_list_size; + sub->num_rects = 0; + for (display = ctx->display_list; display; display = display->next) + { + region = get_region(ctx, display->region_id); + if (region && region->dirty) + sub->num_rects++; + } if (sub->num_rects > 0){ - sub->rects = av_mallocz(sizeof(*sub->rects) * sub->num_rects); + sub->rects = av_mallocz_array(sizeof(*sub->rects), sub->num_rects); for(i=0; i<sub->num_rects; i++) sub->rects[i] = av_mallocz(sizeof(*sub->rects[i])); - } i = 0; for (display = ctx->display_list; display; display = display->next) { region = get_region(ctx, display->region_id); - rect = sub->rects[i]; if (!region) continue; + if (!region->dirty) + continue; + + rect = sub->rects[i]; rect->x = display->x_pos + offset_x; rect->y = display->y_pos + offset_y; rect->w = region->width; rect->h = region->height; - rect->nb_colors = 16; + rect->nb_colors = (1 << region->depth); rect->type = SUBTITLE_BITMAP; rect->pict.linesize[0] = region->width; @@ -1371,9 +1444,7 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, i++; } - - sub->num_rects = i; - + } #ifdef DEBUG save_display_set(ctx); #endif @@ -1394,6 +1465,8 @@ static int dvbsub_decode(AVCodecContext *avctx, int page_id; int segment_length; int i; + int ret; + int got_segment = 0; av_dlog(avctx, "DVB sub packet:\n"); @@ -1432,20 +1505,27 @@ static int dvbsub_decode(AVCodecContext *avctx, switch (segment_type) { case DVBSUB_PAGE_SEGMENT: dvbsub_parse_page_segment(avctx, p, segment_length); + got_segment |= 1; break; case DVBSUB_REGION_SEGMENT: dvbsub_parse_region_segment(avctx, p, segment_length); + got_segment |= 2; break; case DVBSUB_CLUT_SEGMENT: - dvbsub_parse_clut_segment(avctx, p, segment_length); + ret = dvbsub_parse_clut_segment(avctx, p, segment_length); + if (ret < 0) return ret; + got_segment |= 4; break; case DVBSUB_OBJECT_SEGMENT: dvbsub_parse_object_segment(avctx, p, segment_length); + got_segment |= 8; break; case DVBSUB_DISPLAYDEFINITION_SEGMENT: dvbsub_parse_display_definition_segment(avctx, p, segment_length); + break; case DVBSUB_DISPLAY_SEGMENT: *data_size = dvbsub_display_end_segment(avctx, p, segment_length, sub); + got_segment |= 16; break; default: av_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n", @@ -1456,6 +1536,10 @@ static int dvbsub_decode(AVCodecContext *avctx, p += segment_length; } + // Some streams do not send a display segment but if we have all the other + // segments then we need no further data. + if (got_segment == 15 && sub) + *data_size = dvbsub_display_end_segment(avctx, p, 0, sub); return p - buf; } |