diff options
Diffstat (limited to 'libavcodec/dvbsubdec.c')
-rw-r--r-- | libavcodec/dvbsubdec.c | 645 |
1 files changed, 441 insertions, 204 deletions
diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index c3248c9919..b22c7ca233 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 */ @@ -24,6 +24,7 @@ #include "bytestream.h" #include "internal.h" #include "libavutil/colorspace.h" +#include "libavutil/opt.h" #define DVBSUB_PAGE_SEGMENT 0x10 #define DVBSUB_REGION_SEGMENT 0x11 @@ -38,6 +39,7 @@ typedef struct DVBSubCLUT { int id; + int version; uint32_t clut4[4]; uint32_t clut16[16]; @@ -64,6 +66,7 @@ typedef struct DVBSubObjectDisplay { typedef struct DVBSubObject { int id; + int version; int type; @@ -83,6 +86,7 @@ typedef struct DVBSubRegionDisplay { typedef struct DVBSubRegion { int id; + int version; int width; int height; @@ -93,6 +97,7 @@ typedef struct DVBSubRegion { uint8_t *pbuf; int buf_size; + int dirty; DVBSubObjectDisplay *display_list; @@ -109,15 +114,21 @@ 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 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 +194,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; - /* Should already be null */ - if (ctx->object_list) - av_log(NULL, AV_LOG_ERROR, "Memory deallocation error!\n"); + delete_region_display_list(ctx, region); + + av_freep(®ion->pbuf); + av_freep(®ion); + } } static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) @@ -237,15 +254,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,30 +343,39 @@ 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) { 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); @@ -398,14 +436,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; @@ -424,25 +462,27 @@ static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, } if (get_bits(&gb, 6)) - av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n"); + av_log(avctx, AV_LOG_ERROR, "line overflow\n"); (*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) { 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); @@ -522,14 +562,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) @@ -545,21 +585,24 @@ static int dvbsub_read_4bit_string(uint8_t *destbuf, int dbuf_len, } if (get_bits(&gb, 8)) - av_log(NULL, AV_LOG_ERROR, "DVBSub error: line overflow\n"); + av_log(avctx, AV_LOG_ERROR, "line overflow\n"); (*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 +622,223 @@ 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(AVSubtitleRect *rect, int w, int h) +{ + uint8_t list[256] = {0}; + uint8_t list_inv[256]; + int counttab[256] = {0}; + int count, i, x, y; + +#define V(x,y) rect->data[0][(x) + (y)*rect->linesize[0]] + 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)); + } + } +#define L(x,y) list[ rect->data[0][(x) + (y)*rect->linesize[0]] ] + + for (i = 0; i<256; i++) { + int scoretab[256] = {0}; + int bestscore = 0; + int bestv = 0; + for (y = 0; y<h; y++) { + for (x = 0; x<w; x++) { + int v = rect->data[0][x + y*rect->linesize[0]]; + int l_m = list[v]; + int l_l = x ? L(x-1, y) : 1; + int l_r = x+1<w ? L(x+1, y) : 1; + int l_t = y ? L(x, y-1) : 1; + int l_b = y+1<h ? L(x, y+1) : 1; + int score; + if (l_m) + continue; + scoretab[v] += l_l + l_r + l_t + l_b; + score = 1024LL*scoretab[v] / counttab[v]; + if (score > bestscore) { + bestscore = score; + bestv = v; + } + } + } + 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(rect->data[1] + 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])); + + 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) + compute_default_clut(rect, rect->w, rect->h); + +#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 +857,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 +872,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 +900,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 +915,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 +925,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: @@ -724,7 +962,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 +987,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 +1001,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 +1023,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 +1041,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 +1054,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 +1071,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 +1096,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) 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; } @@ -872,6 +1122,7 @@ 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; @@ -890,11 +1141,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); @@ -908,10 +1161,15 @@ static int dvbsub_parse_region_segment(AVCodecContext *avctx, 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 +1179,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) @@ -987,7 +1246,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,22 +1255,36 @@ 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++; @@ -1042,7 +1315,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 +1324,7 @@ static int dvbsub_parse_page_segment(AVCodecContext *avctx, tmp_display_list = display->next; - av_free(display); + av_freep(&display); } return 0; @@ -1110,13 +1382,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"); + fprintf(stderr, "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); + fprintf(stderr, "Error removing %s and %s\n", fname, fname2); return; } } @@ -1141,6 +1413,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 +1446,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) { @@ -1210,7 +1488,7 @@ static int save_display_set(DVBSubContext *ctx) png_save(filename, pbuf, width, height); - av_free(pbuf); + av_freep(&pbuf); } fileno_index++; @@ -1246,14 +1524,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,110 +1543,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) - return AVERROR_INVALIDDATA; - - sub->rects = av_mallocz_array(sub->num_rects * sub->num_rects, - sizeof(*sub->rects)); - if (!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]) { - 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]); - 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, @@ -1380,6 +1568,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"); @@ -1408,9 +1599,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 || @@ -1418,24 +1614,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", @@ -1443,15 +1650,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", @@ -1462,4 +1698,5 @@ AVCodec ff_dvbsub_decoder = { .init = dvbsub_init_decoder, .close = dvbsub_close_decoder, .decode = dvbsub_decode, + .priv_class = &dvbsubdec_class, }; |