#include "get_bits.h"
#include "bytestream.h"
#include "libavutil/colorspace.h"
+#include "libavutil/opt.h"
#define DVBSUB_PAGE_SEGMENT 0x10
#define DVBSUB_REGION_SEGMENT 0x11
} 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 */
+ int64_t prev_start;
DVBSubRegion *region_list;
DVBSubCLUT *clut_list;
DVBSubObject *object_list;
}
ctx->version = -1;
+ ctx->prev_start = AV_NOPTS_VALUE;
default_clut.id = -1;
default_clut.next = NULL;
return pixels_read;
}
+static void 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;
+
+
+ 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\n");
+ return;
+ }
+ 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);
+ 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->pict.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->pict.data[1] = av_mallocz(AVPALETTE_SIZE);
+ memcpy(rect->pict.data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
+
+ rect->pict.data[0] = av_malloc(region->buf_size);
+ memcpy(rect->pict.data[0], region->pbuf, region->buf_size);
+
+ i++;
+ }
+ }
+}
static void dvbsub_parse_pixel_data_block(AVCodecContext *avctx, DVBSubObjectDisplay *display,
const uint8_t *buf, int buf_size, int top_bottom, int non_mod)
}
static void 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;
version = ((*buf)>>4) & 15;
page_state = ((*buf++) >> 2) & 3;
- if (ctx->version != version) {
+ if (ctx->version == version) {
+ return;
+ }
ctx->time_out = timeout;
ctx->version = version;
av_dlog(avctx, "Page time out %ds, state %d\n", ctx->time_out, page_state);
+ 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);
av_free(display);
}
- }
}
}
}
-static int dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf,
- int buf_size, AVSubtitle *sub)
+static void dvbsub_display_end_segment(AVCodecContext *avctx, const uint8_t *buf,
+ 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->end_display_time = ctx->time_out * 1000;
-
- if (display_def) {
- offset_x = display_def->x;
- offset_y = display_def->y;
- }
-
- 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_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);
-
- 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->pict.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->pict.data[1] = av_mallocz(AVPALETTE_SIZE);
- memcpy(rect->pict.data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
-
- rect->pict.data[0] = av_malloc(region->buf_size);
- memcpy(rect->pict.data[0], region->pbuf, region->buf_size);
-
- i++;
- }
- }
+ if(ctx->compute_edt == 0)
+ save_subtitle_set(avctx, sub, got_output);
#ifdef DEBUG
save_display_set(ctx);
#endif
- return 1;
}
static int dvbsub_decode(AVCodecContext *avctx,
int page_id;
int segment_length;
int i;
- int ret;
+ int ret = 0;
int got_segment = 0;
av_dlog(avctx, "DVB sub packet:\n");
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) {
av_dlog(avctx, "incomplete or broken packet");
- return -1;
+ ret = -1;
+ goto end;
}
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);
+ dvbsub_parse_page_segment(avctx, p, segment_length, sub, data_size);
got_segment |= 1;
break;
case DVBSUB_REGION_SEGMENT:
break;
case DVBSUB_CLUT_SEGMENT:
ret = dvbsub_parse_clut_segment(avctx, p, segment_length);
- if (ret < 0) return ret;
+ if (ret < 0) goto end;
got_segment |= 4;
break;
case DVBSUB_OBJECT_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);
+ dvbsub_display_end_segment(avctx, p, segment_length, sub, data_size);
got_segment |= 16;
break;
default:
}
// 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);
+ 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;
}
+static const AVOption options[] = {
+ {"compute_edt", "compute end of time using pts or timeout", offsetof(DVBSubContext, compute_edt), FF_OPT_TYPE_INT, {.i64 = 0}, 0, 1, 0},
+ {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",
.init = dvbsub_init_decoder,
.close = dvbsub_close_decoder,
.decode = dvbsub_decode,
+ .priv_class = &dvbsubdec_class,
};