diff options
Diffstat (limited to 'libavcodec/dvbsubdec.c')
-rw-r--r-- | libavcodec/dvbsubdec.c | 785 |
1 files changed, 531 insertions, 254 deletions
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index b97ff80..bc4a17b 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -2,28 +2,30 @@ * 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 */ #include "avcodec.h" -#include "bitstream.h" +#include "get_bits.h" #include "bytestream.h" #include "internal.h" #include "libavutil/colorspace.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" #define DVBSUB_PAGE_SEGMENT 0x10 #define DVBSUB_REGION_SEGMENT 0x11 @@ -38,6 +40,7 @@ typedef struct DVBSubCLUT { int id; + int version; uint32_t clut4[4]; uint32_t clut16[16]; @@ -64,6 +67,7 @@ typedef struct DVBSubObjectDisplay { typedef struct DVBSubObject { int id; + int version; int type; @@ -83,6 +87,7 @@ typedef struct DVBSubRegionDisplay { typedef struct DVBSubRegion { int id; + int version; int width; int height; @@ -91,8 +96,12 @@ typedef struct DVBSubRegion { int clut; int bgcolor; + uint8_t computed_clut[4*256]; + int has_computed_clut; + uint8_t *pbuf; int buf_size; + int dirty; DVBSubObjectDisplay *display_list; @@ -109,15 +118,22 @@ typedef struct DVBSubDisplayDefinition { } DVBSubDisplayDefinition; typedef struct DVBSubContext { + AVClass *class; int composition_id; int ancillary_id; + int version; int time_out; + int compute_edt; /**< if 1 end display time calculated using pts + if 0 (Default) calculated using time out */ + int compute_clut; + int clut_count2[257][256]; + int substream; + int64_t prev_start; DVBSubRegion *region_list; DVBSubCLUT *clut_list; DVBSubObject *object_list; - int display_list_size; DVBSubRegionDisplay *display_list; DVBSubDisplayDefinition *display_definition; } DVBSubContext; @@ -183,53 +199,59 @@ static void delete_region_display_list(DVBSubContext *ctx, DVBSubRegion *region) obj2 = *obj2_ptr; while (obj2 != object) { - assert(obj2); + av_assert0(obj2); obj2_ptr = &obj2->next; obj2 = *obj2_ptr; } *obj2_ptr = obj2->next; - av_free(obj2); + av_freep(&obj2); } } } region->display_list = display->region_list_next; - av_free(display); + av_freep(&display); } } -static void delete_state(DVBSubContext *ctx) +static void delete_cluts(DVBSubContext *ctx) { - DVBSubRegion *region; - DVBSubCLUT *clut; - - while (ctx->region_list) { - region = ctx->region_list; + while (ctx->clut_list) { + DVBSubCLUT *clut = ctx->clut_list; - ctx->region_list = region->next; + ctx->clut_list = clut->next; - delete_region_display_list(ctx, region); - av_free(region->pbuf); - av_free(region); + av_freep(&clut); } +} - while (ctx->clut_list) { - clut = ctx->clut_list; +static void delete_objects(DVBSubContext *ctx) +{ + while (ctx->object_list) { + DVBSubObject *object = ctx->object_list; - ctx->clut_list = clut->next; + ctx->object_list = object->next; - av_free(clut); + av_freep(&object); } +} - av_freep(&ctx->display_definition); +static void delete_regions(DVBSubContext *ctx) +{ + while (ctx->region_list) { + DVBSubRegion *region = ctx->region_list; + + ctx->region_list = region->next; + + delete_region_display_list(ctx, region); - /* Should already be null */ - if (ctx->object_list) - av_log(NULL, AV_LOG_ERROR, "Memory deallocation error!\n"); + av_freep(®ion->pbuf); + av_freep(®ion); + } } static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) @@ -237,15 +259,27 @@ 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 (ctx->substream < 0) { + ctx->composition_id = -1; + ctx->ancillary_id = -1; + } else 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 { - ctx->composition_id = AV_RB16(avctx->extradata); - ctx->ancillary_id = AV_RB16(avctx->extradata + 2); + if (avctx->extradata_size > 5*ctx->substream + 2) { + ctx->composition_id = AV_RB16(avctx->extradata + 5*ctx->substream); + ctx->ancillary_id = AV_RB16(avctx->extradata + 5*ctx->substream + 2); + } else { + av_log(avctx, AV_LOG_WARNING, "Selected DVB subtitles sub-stream %d is not available\n", ctx->substream); + ctx->composition_id = AV_RB16(avctx->extradata); + ctx->ancillary_id = AV_RB16(avctx->extradata + 2); + } } + ctx->version = -1; + ctx->prev_start = AV_NOPTS_VALUE; + default_clut.id = -1; default_clut.next = NULL; @@ -314,32 +348,41 @@ 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; ctx->display_list = display->next; - av_free(display); + av_freep(&display); } return 0; } -static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, +static int dvbsub_read_2bit_string(AVCodecContext *avctx, + 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) { - BitstreamContext bc; + GetBitContext gb; int bits; int run_length; - int pixels_read = 0; + int pixels_read = x_pos; + + init_get_bits(&gb, *srcbuf, buf_size << 3); - bitstream_init8(&bc, *srcbuf, buf_size); + destbuf += x_pos; - while (bitstream_tell(&bc) < buf_size << 3 && pixels_read < dbuf_len) { - bits = bitstream_read(&bc, 2); + while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) { + bits = get_bits(&gb, 2); if (bits) { if (non_mod != 1 || bits != 1) { @@ -350,10 +393,10 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } pixels_read++; } else { - bits = bitstream_read_bit(&bc); + bits = get_bits1(&gb); if (bits == 1) { - run_length = bitstream_read(&bc, 3) + 3; - bits = bitstream_read(&bc, 2); + run_length = get_bits(&gb, 3) + 3; + bits = get_bits(&gb, 2); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -366,12 +409,12 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } } } else { - bits = bitstream_read_bit(&bc); + bits = get_bits1(&gb); if (bits == 0) { - bits = bitstream_read(&bc, 2); + bits = get_bits(&gb, 2); if (bits == 2) { - run_length = bitstream_read(&bc, 4) + 12; - bits = bitstream_read(&bc, 2); + run_length = get_bits(&gb, 4) + 12; + bits = get_bits(&gb, 2); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -384,8 +427,8 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } } } else if (bits == 3) { - run_length = bitstream_read(&bc, 8) + 29; - bits = bitstream_read(&bc, 2); + run_length = get_bits(&gb, 8) + 29; + bits = get_bits(&gb, 2); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -398,17 +441,17 @@ 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 += (bitstream_tell(&bc) + 7) >> 3; + (*srcbuf) += (get_bits_count(&gb) + 7) >> 3; return pixels_read; } } else { @@ -423,28 +466,30 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } } - if (bitstream_read(&bc, 6)) - av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n"); + if (get_bits(&gb, 6)) + av_log(avctx, AV_LOG_ERROR, "line overflow\n"); - *srcbuf += (bitstream_tell(&bc) + 7) >> 3; + (*srcbuf) += (get_bits_count(&gb) + 7) >> 3; return pixels_read; } -static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, +static int dvbsub_read_4bit_string(AVCodecContext *avctx, 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) { - BitstreamContext bc; + GetBitContext gb; int bits; int run_length; - int pixels_read = 0; + int pixels_read = x_pos; + + init_get_bits(&gb, *srcbuf, buf_size << 3); - bitstream_init8(&bc, *srcbuf, buf_size); + destbuf += x_pos; - while (bitstream_tell(&bc) < buf_size << 3 && pixels_read < dbuf_len) { - bits = bitstream_read(&bc, 4); + while (get_bits_count(&gb) < buf_size << 3 && pixels_read < dbuf_len) { + bits = get_bits(&gb, 4); if (bits) { if (non_mod != 1 || bits != 1) { @@ -455,12 +500,12 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } pixels_read++; } else { - bits = bitstream_read_bit(&bc); + bits = get_bits1(&gb); if (bits == 0) { - run_length = bitstream_read(&bc, 3); + run_length = get_bits(&gb, 3); if (run_length == 0) { - *srcbuf += (bitstream_tell(&bc) + 7) >> 3; + (*srcbuf) += (get_bits_count(&gb) + 7) >> 3; return pixels_read; } @@ -476,10 +521,10 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, pixels_read++; } } else { - bits = bitstream_read_bit(&bc); + bits = get_bits1(&gb); if (bits == 0) { - run_length = bitstream_read(&bc, 2) + 4; - bits = bitstream_read(&bc, 4); + run_length = get_bits(&gb, 2) + 4; + bits = get_bits(&gb, 4); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -492,10 +537,10 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } } } else { - bits = bitstream_read(&bc, 2); + bits = get_bits(&gb, 2); if (bits == 2) { - run_length = bitstream_read(&bc, 4) + 9; - bits = bitstream_read(&bc, 4); + run_length = get_bits(&gb, 4) + 9; + bits = get_bits(&gb, 4); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -508,8 +553,8 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } } } else if (bits == 3) { - run_length = bitstream_read(&bc, 8) + 25; - bits = bitstream_read(&bc, 4); + run_length = get_bits(&gb, 8) + 25; + bits = get_bits(&gb, 4); if (non_mod == 1 && bits == 1) pixels_read += run_length; @@ -522,14 +567,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) @@ -544,22 +589,25 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } } - if (bitstream_read(&bc, 8)) - av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n"); + if (get_bits(&gb, 8)) + av_log(avctx, AV_LOG_ERROR, "line overflow\n"); - *srcbuf += (bitstream_tell(&bc) + 7) >> 3; + (*srcbuf) += (get_bits_count(&gb) + 7) >> 3; return pixels_read; } -static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len, +static int dvbsub_read_8bit_string(AVCodecContext *avctx, + 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)++; @@ -579,30 +627,243 @@ static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len, if (run_length == 0) { return pixels_read; } + + bits = 0; } else { bits = *(*srcbuf)++; - - if (non_mod == 1 && bits == 1) - pixels_read += run_length; } - if (map_table) - bits = map_table[0]; - else - bits = 0; - while (run_length-- > 0 && pixels_read < dbuf_len) { - *destbuf++ = bits; - pixels_read++; + if (non_mod == 1 && bits == 1) + pixels_read += run_length; + else { + if (map_table) + bits = map_table[bits]; + while (run_length-- > 0 && pixels_read < dbuf_len) { + *destbuf++ = bits; + pixels_read++; + } } } } if (*(*srcbuf)++) - av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n"); + av_log(avctx, AV_LOG_ERROR, "line overflow\n"); return pixels_read; } +static void compute_default_clut(DVBSubContext *ctx, uint8_t *clut, AVSubtitleRect *rect, int w, int h) +{ + uint8_t list[256] = {0}; + uint8_t list_inv[256]; + int counttab[256] = {0}; + int (*counttab2)[256] = ctx->clut_count2; + int count, i, x, y; + ptrdiff_t stride = rect->linesize[0]; + + memset(ctx->clut_count2, 0 , sizeof(ctx->clut_count2)); + +#define V(x,y) rect->data[0][(x) + (y)*stride] + for (y = 0; y<h; y++) { + for (x = 0; x<w; x++) { + int v = V(x,y) + 1; + int vl = x ? V(x-1,y) + 1 : 0; + int vr = x+1<w ? V(x+1,y) + 1 : 0; + int vt = y ? V(x,y-1) + 1 : 0; + int vb = y+1<h ? V(x,y+1) + 1 : 0; + counttab[v-1] += !!((v!=vl) + (v!=vr) + (v!=vt) + (v!=vb)); + counttab2[vl][v-1] ++; + counttab2[vr][v-1] ++; + counttab2[vt][v-1] ++; + counttab2[vb][v-1] ++; + } + } +#define L(x,y) list[d[(x) + (y)*stride]] + + for (i = 0; i<256; i++) { + counttab2[i+1][i] = 0; + } + for (i = 0; i<256; i++) { + int bestscore = 0; + int bestv = 0; + + for (x = 0; x < 256; x++) { + int scorev = 0; + if (list[x]) + continue; + scorev += counttab2[0][x]; + for (y = 0; y < 256; y++) { + scorev += list[y] * counttab2[y+1][x]; + } + + if (scorev) { + int score = 1024LL*scorev / counttab[x]; + if (score > bestscore) { + bestscore = score; + bestv = x; + } + } + } + if (!bestscore) + break; + list [ bestv ] = 1; + list_inv[ i ] = bestv; + } + + count = FFMAX(i - 1, 1); + for (i--; i>=0; i--) { + int v = i*255/count; + AV_WN32(clut + 4*list_inv[i], RGBA(v/2,v,v/2,v)); + } +} + + +static int save_subtitle_set(AVCodecContext *avctx, AVSubtitle *sub, int *got_output) +{ + DVBSubContext *ctx = avctx->priv_data; + DVBSubRegionDisplay *display; + DVBSubDisplayDefinition *display_def = ctx->display_definition; + DVBSubRegion *region; + AVSubtitleRect *rect; + DVBSubCLUT *clut; + uint32_t *clut_table; + int i; + int offset_x=0, offset_y=0; + int ret = 0; + + + if (display_def) { + offset_x = display_def->x; + offset_y = display_def->y; + } + + /* Not touching AVSubtitles again*/ + if(sub->num_rects) { + avpriv_request_sample(ctx, "Different Version of Segment asked Twice"); + return AVERROR_PATCHWELCOME; + } + for (display = ctx->display_list; display; display = display->next) { + region = get_region(ctx, display->region_id); + if (region && region->dirty) + sub->num_rects++; + } + + if(ctx->compute_edt == 0) { + sub->end_display_time = ctx->time_out * 1000; + *got_output = 1; + } else if (ctx->prev_start != AV_NOPTS_VALUE) { + sub->end_display_time = av_rescale_q((sub->pts - ctx->prev_start ), AV_TIME_BASE_Q, (AVRational){ 1, 1000 }) - 1; + *got_output = 1; + } + if (sub->num_rects > 0) { + + sub->rects = av_mallocz_array(sizeof(*sub->rects), sub->num_rects); + if (!sub->rects) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (i = 0; i < sub->num_rects; i++) { + sub->rects[i] = av_mallocz(sizeof(*sub->rects[i])); + if (!sub->rects[i]) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + i = 0; + + for (display = ctx->display_list; display; display = display->next) { + region = get_region(ctx, display->region_id); + + 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 = (1 << region->depth); + rect->type = SUBTITLE_BITMAP; + rect->linesize[0] = region->width; + + clut = get_clut(ctx, region->clut); + + if (!clut) + clut = &default_clut; + + switch (region->depth) { + case 2: + clut_table = clut->clut4; + break; + case 8: + clut_table = clut->clut256; + break; + case 4: + default: + clut_table = clut->clut16; + break; + } + + rect->data[1] = av_mallocz(AVPALETTE_SIZE); + if (!rect->data[1]) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t)); + + rect->data[0] = av_malloc(region->buf_size); + if (!rect->data[0]) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(rect->data[0], region->pbuf, region->buf_size); + + if ((clut == &default_clut && ctx->compute_clut == -1) || ctx->compute_clut == 1) { + if (!region->has_computed_clut) { + compute_default_clut(ctx, region->computed_clut, rect, rect->w, rect->h); + region->has_computed_clut = 1; + } + + memcpy(rect->data[1], region->computed_clut, sizeof(region->computed_clut)); + } + +#if FF_API_AVPICTURE +FF_DISABLE_DEPRECATION_WARNINGS +{ + int j; + for (j = 0; j < 4; j++) { + rect->pict.data[j] = rect->data[j]; + rect->pict.linesize[j] = rect->linesize[j]; + } +} +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + i++; + } + } + + return 0; +fail: + if (sub->rects) { + for(i=0; i<sub->num_rects; i++) { + rect = sub->rects[i]; + if (rect) { + av_freep(&rect->data[0]); + av_freep(&rect->data[1]); + } + av_freep(&sub->rects[i]); + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + return ret; +} static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDisplay *display, const uint8_t *buf, int buf_size, int top_bottom, int non_mod) @@ -621,6 +882,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 ff_dlog(avctx, "DVB pixel block size %d, %s field:\n", buf_size, top_bottom ? "bottom" : "top"); @@ -635,21 +897,22 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis if (i % 16) ff_dlog(avctx, "\n"); +#endif - if (region == 0) + if (!region) 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; } @@ -662,9 +925,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(avctx, pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, map_table, x_pos); break; case 0x11: if (region->depth < 4) { @@ -677,9 +940,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(avctx, pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, map_table, x_pos); break; case 0x12: if (region->depth < 8) { @@ -687,9 +950,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(avctx, pbuf + (y_pos * region->width), + region->width, &buf, buf_end - buf, + non_mod, NULL, x_pos); break; case 0x20: @@ -716,6 +979,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis } } + region->has_computed_clut = 0; } static int dvbsub_parse_object_segment(AVCodecContext *avctx, @@ -724,7 +988,6 @@ static int 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; @@ -750,12 +1013,13 @@ static int dvbsub_parse_object_segment(AVCodecContext *avctx, buf += 2; if (buf + top_field_len + bottom_field_len > buf_end) { - av_log(avctx, AV_LOG_ERROR, "Field data size too large\n"); + av_log(avctx, AV_LOG_ERROR, "Field data size %d+%d too large\n", top_field_len, bottom_field_len); return AVERROR_INVALIDDATA; } 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); @@ -763,9 +1027,9 @@ static int 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); } @@ -785,6 +1049,7 @@ static int dvbsub_parse_clut_segment(AVCodecContext *avctx, 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; @@ -802,6 +1067,7 @@ static int dvbsub_parse_clut_segment(AVCodecContext *avctx, ff_dlog(avctx, "\n"); clut_id = *buf++; + version = ((*buf)>>4)&15; buf += 1; clut = get_clut(ctx, clut_id); @@ -814,11 +1080,16 @@ static int 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++; @@ -826,7 +1097,6 @@ static int dvbsub_parse_clut_segment(AVCodecContext *avctx, if (depth == 0) { av_log(avctx, AV_LOG_ERROR, "Invalid clut depth 0x%x!\n", *buf); - return AVERROR_INVALIDDATA; } full_range = (*buf++) & 1; @@ -852,14 +1122,20 @@ static int dvbsub_parse_clut_segment(AVCodecContext *avctx, YUV_TO_RGB2_CCIR(r, g, b, y); ff_dlog(avctx, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha); + if (!!(depth & 0x80) + !!(depth & 0x40) + !!(depth & 0x20) > 1) { + ff_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) + if (depth & 0x80 && entry_id < 4) clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha); - if (depth & 0x40) + else if (depth & 0x40 && entry_id < 16) 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; } @@ -872,10 +1148,12 @@ static int 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; int fill; + int ret; if (buf_size < 10) return AVERROR_INVALIDDATA; @@ -890,11 +1168,13 @@ static int dvbsub_parse_region_segment(AVCodecContext *avctx, return AVERROR(ENOMEM); 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); @@ -902,16 +1182,31 @@ static int dvbsub_parse_region_segment(AVCodecContext *avctx, region->height = AV_RB16(buf); buf += 2; + ret = av_image_check_size2(region->width, region->height, avctx->max_pixels, AV_PIX_FMT_PAL8, 0, avctx); + if (ret >= 0 && region->width * region->height * 2 > 320 * 1024 * 8) { + ret = AVERROR_INVALIDDATA; + av_log(avctx, AV_LOG_ERROR, "Pixel buffer memory constraint violated\n"); + } + if (ret < 0) { + region->width= region->height= 0; + return ret; + } + if (region->width * region->height != region->buf_size) { av_free(region->pbuf); region->buf_size = region->width * region->height; region->pbuf = av_malloc(region->buf_size); - if (!region->pbuf) + if (!region->pbuf) { + region->buf_size = + region->width = + region->height = 0; return AVERROR(ENOMEM); + } fill = 1; + region->dirty = 0; } region->depth = 1 << (((*buf++) >> 2) & 7); @@ -921,9 +1216,10 @@ static int 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) @@ -971,6 +1267,13 @@ static int dvbsub_parse_region_segment(AVCodecContext *avctx, display->y_pos = AV_RB16(buf) & 0xfff; buf += 2; + if (display->x_pos >= region->width || + display->y_pos >= region->height) { + av_log(avctx, AV_LOG_ERROR, "Object outside region\n"); + av_free(display); + return AVERROR_INVALIDDATA; + } + if ((object->type == 1 || object->type == 2) && buf+1 < buf_end) { display->fgcolor = *buf++; display->bgcolor = *buf++; @@ -987,7 +1290,7 @@ static int dvbsub_parse_region_segment(AVCodecContext *avctx, } static int dvbsub_parse_page_segment(AVCodecContext *avctx, - const uint8_t *buf, int buf_size) + const uint8_t *buf, int buf_size, AVSubtitle *sub, int *got_output) { DVBSubContext *ctx = avctx->priv_data; DVBSubRegionDisplay *display; @@ -996,27 +1299,50 @@ static int 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 AVERROR_INVALIDDATA; - ctx->time_out = *buf++; + timeout = *buf++; + version = ((*buf)>>4) & 15; page_state = ((*buf++) >> 2) & 3; + if (ctx->version == version) { + return 0; + } + + ctx->time_out = timeout; + ctx->version = version; + ff_dlog(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state); - if (page_state == 2) { - delete_state(ctx); + if(ctx->compute_edt == 1) + save_subtitle_set(avctx, sub, got_output); + + 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++; buf += 1; + display = ctx->display_list; + while (display && display->region_id != region_id) { + display = display->next; + } + if (display) { + av_log(avctx, AV_LOG_ERROR, "duplicate region\n"); + break; + } + display = tmp_display_list; tmp_ptr = &tmp_display_list; @@ -1042,7 +1368,6 @@ static int dvbsub_parse_page_segment(AVCodecContext *avctx, display->next = ctx->display_list; ctx->display_list = display; - ctx->display_list_size++; ff_dlog(avctx, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos); } @@ -1052,7 +1377,7 @@ static int dvbsub_parse_page_segment(AVCodecContext *avctx, tmp_display_list = display->next; - av_free(display); + av_freep(&display); } return 0; @@ -1060,7 +1385,7 @@ static int dvbsub_parse_page_segment(AVCodecContext *avctx, #ifdef DEBUG -static void png_save(const char *filename, uint32_t *bitmap, int w, int h) +static void png_save(DVBSubContext *ctx, const char *filename, uint32_t *bitmap, int w, int h) { int x, y, v; FILE *f; @@ -1110,13 +1435,13 @@ static void png_save(const char *filename, uint32_t *bitmap, int w, int h) snprintf(command, sizeof(command), "pnmtopng -alpha %s %s > %s.png 2> /dev/null", fname2, fname, filename); if (system(command) != 0) { - printf("Error running pnmtopng\n"); + av_log(ctx, AV_LOG_ERROR, "Error running pnmtopng\n"); return; } snprintf(command, sizeof(command), "rm %s %s", fname, fname2); if (system(command) != 0) { - printf("Error removing %s and %s\n", fname, fname2); + av_log(ctx, AV_LOG_ERROR, "Error removing %s and %s\n", fname, fname2); return; } } @@ -1141,6 +1466,9 @@ static int save_display_set(DVBSubContext *ctx) for (display = ctx->display_list; display; display = display->next) { region = get_region(ctx, display->region_id); + if (!region) + return -1; + if (x_pos == -1) { x_pos = display->x_pos; y_pos = display->y_pos; @@ -1171,17 +1499,20 @@ static int save_display_set(DVBSubContext *ctx) pbuf = av_malloc(width * height * 4); if (!pbuf) - return AVERROR(ENOMEM); + return -1; for (display = ctx->display_list; display; display = display->next) { region = get_region(ctx, display->region_id); + if (!region) + return -1; + x_off = display->x_pos - x_pos; y_off = display->y_pos - y_pos; clut = get_clut(ctx, region->clut); - if (clut == 0) + if (!clut) clut = &default_clut; switch (region->depth) { @@ -1208,9 +1539,9 @@ static int save_display_set(DVBSubContext *ctx) snprintf(filename, sizeof(filename), "dvbs.%d", fileno_index); - png_save(filename, pbuf, width, height); + png_save(ctx, filename, pbuf, width, height); - av_free(pbuf); + av_freep(&pbuf); } fileno_index++; @@ -1246,14 +1577,18 @@ static int 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 (buf_size < 13) - return AVERROR_INVALIDDATA; + if (!avctx->width || !avctx->height) { + avctx->width = display_def->width; + avctx->height = display_def->height; + } if (info_byte & 1<<3) { // display_window_flag + if (buf_size < 13) + return AVERROR_INVALIDDATA; + 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; } @@ -1261,123 +1596,16 @@ static int dvbsub_parse_display_definition_segment(AVCodecContext *avctx, } static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, - int buf_size, AVSubtitle *sub) + int buf_size, AVSubtitle *sub,int *got_output) { DVBSubContext *ctx = avctx->priv_data; - DVBSubDisplayDefinition *display_def = ctx->display_definition; - - DVBSubRegion *region; - DVBSubRegionDisplay *display; - AVSubtitleRect *rect; - DVBSubCLUT *clut; - uint32_t *clut_table; - 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; - - if (sub->num_rects > 0) { - sub->rects = av_mallocz(sizeof(*sub->rects) * sub->num_rects); - if (!sub->rects) - return AVERROR(ENOMEM); - for (i = 0; i < sub->num_rects; i++) { - sub->rects[i] = av_mallocz(sizeof(*sub->rects[i])); - if (!sub->rects[i]) { - int j; - for (j = 0; j < i; j ++) - av_free(sub->rects[j]); - av_free(sub->rects); - return AVERROR(ENOMEM); - } - } - } - - 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; - - 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->type = SUBTITLE_BITMAP; - rect->linesize[0] = region->width; - - clut = get_clut(ctx, region->clut); - - if (!clut) - clut = &default_clut; - - switch (region->depth) { - case 2: - clut_table = clut->clut4; - break; - case 8: - clut_table = clut->clut256; - break; - case 4: - default: - clut_table = clut->clut16; - break; - } - - rect->data[1] = av_mallocz(AVPALETTE_SIZE); - if (!rect->data[1]) { - for (i = 0; i < sub->num_rects; i++) - av_free(sub->rects[i]); - av_free(sub->rects); - return AVERROR(ENOMEM); - } - memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t)); - - rect->data[0] = av_malloc(region->buf_size); - if (!rect->data[0]) { - av_free(rect->data[1]); - for (i = 0; i < sub->num_rects; i++) - av_free(sub->rects[i]); - av_free(sub->rects); - return AVERROR(ENOMEM); - } - memcpy(rect->data[0], region->pbuf, region->buf_size); - -#if FF_API_AVPICTURE -FF_DISABLE_DEPRECATION_WARNINGS -{ - int j; - for (j = 0; j < 4; j++) { - rect->pict.data[j] = rect->data[j]; - rect->pict.linesize[j] = rect->linesize[j]; - } -} -FF_ENABLE_DEPRECATION_WARNINGS -#endif - - i++; - } - - sub->num_rects = i; + if(ctx->compute_edt == 0) + save_subtitle_set(avctx, sub, got_output); #ifdef DEBUG save_display_set(ctx); #endif - - return 1; + return 0; } static int dvbsub_decode(AVCodecContext *avctx, @@ -1393,6 +1621,9 @@ static int dvbsub_decode(AVCodecContext *avctx, int page_id; int segment_length; int i; + int ret = 0; + int got_segment = 0; + int got_dds = 0; ff_dlog(avctx, "DVB sub packet:\n"); @@ -1421,9 +1652,14 @@ static int dvbsub_decode(AVCodecContext *avctx, segment_length = AV_RB16(p); p += 2; + if (avctx->debug & FF_DEBUG_STARTCODE) { + av_log(avctx, AV_LOG_DEBUG, "segment_type:%d page_id:%d segment_length:%d\n", segment_type, page_id, segment_length); + } + if (p_end - p < segment_length) { ff_dlog(avctx, "incomplete or broken packet"); - return -1; + ret = -1; + goto end; } if (page_id == ctx->composition_id || page_id == ctx->ancillary_id || @@ -1431,24 +1667,35 @@ static int dvbsub_decode(AVCodecContext *avctx, int ret = 0; switch (segment_type) { case DVBSUB_PAGE_SEGMENT: - ret = dvbsub_parse_page_segment(avctx, p, segment_length); + ret = dvbsub_parse_page_segment(avctx, p, segment_length, sub, data_size); + got_segment |= 1; break; case DVBSUB_REGION_SEGMENT: ret = dvbsub_parse_region_segment(avctx, p, segment_length); + got_segment |= 2; break; case DVBSUB_CLUT_SEGMENT: ret = dvbsub_parse_clut_segment(avctx, p, segment_length); + if (ret < 0) goto end; + got_segment |= 4; break; case DVBSUB_OBJECT_SEGMENT: ret = dvbsub_parse_object_segment(avctx, p, segment_length); + got_segment |= 8; break; case DVBSUB_DISPLAYDEFINITION_SEGMENT: ret = dvbsub_parse_display_definition_segment(avctx, p, segment_length); + got_dds = 1; break; case DVBSUB_DISPLAY_SEGMENT: - ret = dvbsub_display_end_segment(avctx, p, segment_length, sub); - *data_size = ret; + ret = dvbsub_display_end_segment(avctx, p, segment_length, sub, data_size); + if (got_segment == 15 && !got_dds && !avctx->width && !avctx->height) { + // Default from ETSI EN 300 743 V1.3.1 (7.2.1) + avctx->width = 720; + avctx->height = 576; + } + got_segment |= 16; break; default: ff_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n", @@ -1456,15 +1703,44 @@ static int dvbsub_decode(AVCodecContext *avctx, break; } if (ret < 0) - return ret; + goto end; } 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) { + av_log(avctx, AV_LOG_DEBUG, "Missing display_end_segment, emulating\n"); + dvbsub_display_end_segment(avctx, p, 0, sub, data_size); + } + +end: + if(ret < 0) { + *data_size = 0; + avsubtitle_free(sub); + return ret; + } else { + if(ctx->compute_edt == 1 ) + FFSWAP(int64_t, ctx->prev_start, sub->pts); + } return p - buf; } +#define DS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM +static const AVOption options[] = { + {"compute_edt", "compute end of time using pts or timeout", offsetof(DVBSubContext, compute_edt), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DS}, + {"compute_clut", "compute clut when not available(-1) or always(1) or never(0)", offsetof(DVBSubContext, compute_clut), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, DS}, + {"dvb_substream", "", offsetof(DVBSubContext, substream), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, DS}, + {NULL} +}; +static const AVClass dvbsubdec_class = { + .class_name = "DVB Sub Decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; AVCodec ff_dvbsub_decoder = { .name = "dvbsub", @@ -1475,4 +1751,5 @@ AVCodec ff_dvbsub_decoder = { .init = dvbsub_init_decoder, .close = dvbsub_close_decoder, .decode = dvbsub_decode, + .priv_class = &dvbsubdec_class, }; |