X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fdvbsubdec.c;h=aef2c08f30df9c9eeac33bfbd42373201439c25f;hb=ff17fc6353c6513316b1132f27ac2a7a8d81b9ec;hp=b810ef9e490bf79ce9b3c7bd94e4b0032d09d230;hpb=29d7eef7dcfaf6cc6c97ac154e54c89f77507d86;p=ffmpeg diff --git a/libavcodec/dvbsubdec.c b/libavcodec/dvbsubdec.c index b810ef9e490..aef2c08f30d 100644 --- a/libavcodec/dvbsubdec.c +++ b/libavcodec/dvbsubdec.c @@ -2,41 +2,40 @@ * DVB subtitle decoding for ffmpeg * Copyright (c) 2005 Ian Caulfield * - * This file is part of FFmpeg. + * This file is part of Libav. * - * FFmpeg is free software; you can redistribute it and/or + * Libav 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. * - * FFmpeg is distributed in the hope that it will be useful, + * Libav 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 FFmpeg; if not, write to the Free Software + * License along with Libav; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avcodec.h" #include "dsputil.h" #include "get_bits.h" -#include "colorspace.h" - -//#define DEBUG -//#define DEBUG_PACKET_CONTENTS -//#define DEBUG_SAVE_IMAGES +#include "bytestream.h" +#include "libavutil/colorspace.h" #define DVBSUB_PAGE_SEGMENT 0x10 #define DVBSUB_REGION_SEGMENT 0x11 #define DVBSUB_CLUT_SEGMENT 0x12 #define DVBSUB_OBJECT_SEGMENT 0x13 +#define DVBSUB_DISPLAYDEFINITION_SEGMENT 0x14 #define DVBSUB_DISPLAY_SEGMENT 0x80 #define cm (ff_cropTbl + MAX_NEG_CROP) -#ifdef DEBUG_SAVE_IMAGES +#ifdef DEBUG #undef fprintf +#undef perror #if 0 static void png_save(const char *filename, uint8_t *bitmap, int w, int h, uint32_t *rgba_palette) @@ -51,7 +50,7 @@ static void png_save(const char *filename, uint8_t *bitmap, int w, int h, f = fopen(fname, "w"); if (!f) { perror(fname); - exit(1); + return; } fprintf(f, "P6\n" "%d %d\n" @@ -73,7 +72,7 @@ static void png_save(const char *filename, uint8_t *bitmap, int w, int h, f = fopen(fname2, "w"); if (!f) { perror(fname2); - exit(1); + return; } fprintf(f, "P5\n" "%d %d\n" @@ -107,7 +106,7 @@ static void png_save2(const char *filename, uint32_t *bitmap, int w, int h) f = fopen(fname, "w"); if (!f) { perror(fname); - exit(1); + return; } fprintf(f, "P6\n" "%d %d\n" @@ -129,7 +128,7 @@ static void png_save2(const char *filename, uint32_t *bitmap, int w, int h) f = fopen(fname2, "w"); if (!f) { perror(fname2); - exit(1); + return; } fprintf(f, "P5\n" "%d %d\n" @@ -216,6 +215,15 @@ typedef struct DVBSubRegion { struct DVBSubRegion *next; } DVBSubRegion; +typedef struct DVBSubDisplayDefinition { + int version; + + int x; + int y; + int width; + int height; +} DVBSubDisplayDefinition; + typedef struct DVBSubContext { int composition_id; int ancillary_id; @@ -227,6 +235,7 @@ typedef struct DVBSubContext { int display_list_size; DVBSubRegionDisplay *display_list; + DVBSubDisplayDefinition *display_definition; } DVBSubContext; @@ -320,9 +329,7 @@ static void delete_state(DVBSubContext *ctx) ctx->region_list = region->next; delete_region_display_list(ctx, region); - if (region->pbuf) - av_free(region->pbuf); - + av_free(region->pbuf); av_free(region); } @@ -334,6 +341,8 @@ static void delete_state(DVBSubContext *ctx) av_free(clut); } + av_freep(&ctx->display_definition); + /* Should already be null */ if (ctx->object_list) av_log(0, AV_LOG_ERROR, "Memory deallocation error!\n"); @@ -342,12 +351,16 @@ static void delete_state(DVBSubContext *ctx) static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) { int i, r, g, b, a = 0; - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; - - memset(avctx->priv_data, 0, sizeof(DVBSubContext)); + DVBSubContext *ctx = avctx->priv_data; - ctx->composition_id = avctx->sub_id & 0xffff; - ctx->ancillary_id = avctx->sub_id >> 16; + if (!avctx->extradata || avctx->extradata_size != 4) { + av_log(avctx, AV_LOG_WARNING, "Invalid extradata, subtitle streams may be combined!\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); + } default_clut.id = -1; default_clut.next = NULL; @@ -414,7 +427,7 @@ static av_cold int dvbsub_init_decoder(AVCodecContext *avctx) static av_cold int dvbsub_close_decoder(AVCodecContext *avctx) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; DVBSubRegionDisplay *display; delete_state(ctx); @@ -717,7 +730,7 @@ static int dvbsub_read_8bit_string(uint8_t *destbuf, int dbuf_len, static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDisplay *display, const uint8_t *buf, int buf_size, int top_bottom, int non_mod) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; DVBSubRegion *region = get_region(ctx, display->region_id); const uint8_t *buf_end = buf + buf_size; @@ -731,23 +744,20 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; uint8_t *map_table; - dprintf(avctx, "DVB pixel block size %d, %s field:\n", buf_size, + av_dlog(avctx, "DVB pixel block size %d, %s field:\n", buf_size, top_bottom ? "bottom" : "top"); -#ifdef DEBUG_PACKET_CONTENTS for (i = 0; i < buf_size; i++) { if (i % 16 == 0) - av_log(avctx, AV_LOG_INFO, "0x%08p: ", buf+i); + av_dlog(avctx, "0x%8p: ", buf+i); - av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]); + av_dlog(avctx, "%02x ", buf[i]); if (i % 16 == 15) - av_log(avctx, AV_LOG_INFO, "\n"); + av_dlog(avctx, "\n"); } if (i % 16) - av_log(avctx, AV_LOG_INFO, "\n"); - -#endif + av_dlog(avctx, "\n"); if (region == 0) return; @@ -776,7 +786,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis map_table = NULL; x_pos += dvbsub_read_2bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_size, + region->width - x_pos, &buf, buf_end - buf, non_mod, map_table); break; case 0x11: @@ -791,7 +801,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis map_table = NULL; x_pos += dvbsub_read_4bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_size, + region->width - x_pos, &buf, buf_end - buf, non_mod, map_table); break; case 0x12: @@ -801,7 +811,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis } x_pos += dvbsub_read_8bit_string(pbuf + (y_pos * region->width) + x_pos, - region->width - x_pos, &buf, buf_size, + region->width - x_pos, &buf, buf_end - buf, non_mod, NULL); break; @@ -834,7 +844,7 @@ static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDis static void dvbsub_parse_object_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; const uint8_t *buf_end = buf + buf_size; const uint8_t *block; @@ -893,30 +903,25 @@ static void dvbsub_parse_object_segment(AVCodecContext *avctx, static void dvbsub_parse_clut_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; const uint8_t *buf_end = buf + buf_size; - int clut_id; + int i, clut_id; DVBSubCLUT *clut; int entry_id, depth , full_range; int y, cr, cb, alpha; int r, g, b, r_add, g_add, b_add; -#ifdef DEBUG_PACKET_CONTENTS - int i; - - av_log(avctx, AV_LOG_INFO, "DVB clut packet:\n"); + av_dlog(avctx, "DVB clut packet:\n"); for (i=0; i < buf_size; i++) { - av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]); + av_dlog(avctx, "%02x ", buf[i]); if (i % 16 == 15) - av_log(avctx, AV_LOG_INFO, "\n"); + av_dlog(avctx, "\n"); } if (i % 16) - av_log(avctx, AV_LOG_INFO, "\n"); - -#endif + av_dlog(avctx, "\n"); clut_id = *buf++; buf += 1; @@ -966,7 +971,7 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, YUV_TO_RGB1_CCIR(cb, cr); YUV_TO_RGB2_CCIR(r, g, b, y); - dprintf(avctx, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha); + av_dlog(avctx, "clut %d := (%d,%d,%d,%d)\n", entry_id, r, g, b, alpha); if (depth & 0x80) clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha); @@ -981,7 +986,7 @@ static void dvbsub_parse_clut_segment(AVCodecContext *avctx, static void dvbsub_parse_region_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; const uint8_t *buf_end = buf + buf_size; int region_id, object_id; @@ -1014,8 +1019,7 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, buf += 2; if (region->width * region->height != region->buf_size) { - if (region->pbuf) - av_free(region->pbuf); + av_free(region->pbuf); region->buf_size = region->width * region->height; @@ -1042,11 +1046,11 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, region->bgcolor = (((*buf++) >> 2) & 3); } - dprintf(avctx, "Region %d, (%dx%d)\n", region_id, region->width, region->height); + av_dlog(avctx, "Region %d, (%dx%d)\n", region_id, region->width, region->height); if (fill) { memset(region->pbuf, region->bgcolor, region->buf_size); - dprintf(avctx, "Fill region (%d)\n", region->bgcolor); + av_dlog(avctx, "Fill region (%d)\n", region->bgcolor); } delete_region_display_list(ctx, region); @@ -1093,7 +1097,7 @@ static void dvbsub_parse_region_segment(AVCodecContext *avctx, static void dvbsub_parse_page_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; DVBSubRegionDisplay *display; DVBSubRegionDisplay *tmp_display_list, **tmp_ptr; @@ -1107,7 +1111,7 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, ctx->time_out = *buf++; page_state = ((*buf++) >> 2) & 3; - dprintf(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state); + av_dlog(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state); if (page_state == 2) { delete_state(ctx); @@ -1145,7 +1149,7 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, ctx->display_list = display; ctx->display_list_size++; - dprintf(avctx, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos); + av_dlog(avctx, "Region %d, (%d,%d)\n", region_id, display->x_pos, display->y_pos); } while (tmp_display_list) { @@ -1159,7 +1163,7 @@ static void dvbsub_parse_page_segment(AVCodecContext *avctx, } -#ifdef DEBUG_SAVE_IMAGES +#ifdef DEBUG static void save_display_set(DVBSubContext *ctx) { DVBSubRegion *region; @@ -1254,10 +1258,51 @@ static void save_display_set(DVBSubContext *ctx) } #endif +static void dvbsub_parse_display_definition_segment(AVCodecContext *avctx, + const uint8_t *buf, + int buf_size) +{ + DVBSubContext *ctx = avctx->priv_data; + DVBSubDisplayDefinition *display_def = ctx->display_definition; + int dds_version, info_byte; + + if (buf_size < 5) + return; + + info_byte = bytestream_get_byte(&buf); + dds_version = info_byte >> 4; + if (display_def && display_def->version == dds_version) + return; // already have this display definition version + + if (!display_def) { + display_def = av_mallocz(sizeof(*display_def)); + ctx->display_definition = display_def; + } + if (!display_def) + return; + + display_def->version = dds_version; + display_def->x = 0; + 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; + + 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->height = bytestream_get_be16(&buf) - display_def->y + 1; + } +} + static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, int buf_size, AVSubtitle *sub) { - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; + DVBSubContext *ctx = avctx->priv_data; + DVBSubDisplayDefinition *display_def = ctx->display_definition; DVBSubRegion *region; DVBSubRegionDisplay *display; @@ -1265,12 +1310,18 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, 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){ @@ -1288,8 +1339,8 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, if (!region) continue; - rect->x = display->x_pos; - rect->y = display->y_pos; + 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; @@ -1325,7 +1376,7 @@ static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf, sub->num_rects = i; -#ifdef DEBUG_SAVE_IMAGES +#ifdef DEBUG save_display_set(ctx); #endif @@ -1338,36 +1389,34 @@ static int dvbsub_decode(AVCodecContext *avctx, { const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; - DVBSubContext *ctx = (DVBSubContext*) avctx->priv_data; - AVSubtitle *sub = (AVSubtitle*) data; + DVBSubContext *ctx = avctx->priv_data; + AVSubtitle *sub = data; const uint8_t *p, *p_end; int segment_type; int page_id; int segment_length; - -#ifdef DEBUG_PACKET_CONTENTS int i; - av_log(avctx, AV_LOG_INFO, "DVB sub packet:\n"); + av_dlog(avctx, "DVB sub packet:\n"); for (i=0; i < buf_size; i++) { - av_log(avctx, AV_LOG_INFO, "%02x ", buf[i]); + av_dlog(avctx, "%02x ", buf[i]); if (i % 16 == 15) - av_log(avctx, AV_LOG_INFO, "\n"); + av_dlog(avctx, "\n"); } if (i % 16) - av_log(avctx, AV_LOG_INFO, "\n"); + av_dlog(avctx, "\n"); -#endif - - if (buf_size <= 2) + if (buf_size <= 6 || *buf != 0x0f) { + av_dlog(avctx, "incomplete or broken packet"); return -1; + } p = buf; p_end = buf + buf_size; - while (p < p_end && *p == 0x0f) { + while (p_end - p >= 6 && *p == 0x0f) { p += 1; segment_type = *p++; page_id = AV_RB16(p); @@ -1375,7 +1424,13 @@ static int dvbsub_decode(AVCodecContext *avctx, segment_length = AV_RB16(p); p += 2; - if (page_id == ctx->composition_id || page_id == ctx->ancillary_id) { + if (p_end - p < segment_length) { + av_dlog(avctx, "incomplete or broken packet"); + return -1; + } + + if (page_id == ctx->composition_id || page_id == ctx->ancillary_id || + ctx->composition_id == -1 || ctx->ancillary_id == -1) { switch (segment_type) { case DVBSUB_PAGE_SEGMENT: dvbsub_parse_page_segment(avctx, p, segment_length); @@ -1389,11 +1444,13 @@ static int dvbsub_decode(AVCodecContext *avctx, case DVBSUB_OBJECT_SEGMENT: dvbsub_parse_object_segment(avctx, p, segment_length); break; + case DVBSUB_DISPLAYDEFINITION_SEGMENT: + dvbsub_parse_display_definition_segment(avctx, p, segment_length); case DVBSUB_DISPLAY_SEGMENT: *data_size = dvbsub_display_end_segment(avctx, p, segment_length, sub); break; default: - dprintf(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n", + av_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n", segment_type, page_id, segment_length); break; } @@ -1402,23 +1459,17 @@ static int dvbsub_decode(AVCodecContext *avctx, p += segment_length; } - if (p != p_end) { - dprintf(avctx, "Junk at end of packet\n"); - return -1; - } - - return buf_size; + return p - buf; } -AVCodec dvbsub_decoder = { - "dvbsub", - CODEC_TYPE_SUBTITLE, - CODEC_ID_DVB_SUBTITLE, - sizeof(DVBSubContext), - dvbsub_init_decoder, - NULL, - dvbsub_close_decoder, - dvbsub_decode, +AVCodec ff_dvbsub_decoder = { + .name = "dvbsub", + .type = AVMEDIA_TYPE_SUBTITLE, + .id = CODEC_ID_DVB_SUBTITLE, + .priv_data_size = sizeof(DVBSubContext), + .init = dvbsub_init_decoder, + .close = dvbsub_close_decoder, + .decode = dvbsub_decode, .long_name = NULL_IF_CONFIG_SMALL("DVB subtitles"), };