1
mirror of https://git.videolan.org/git/ffmpeg.git synced 2024-08-19 07:45:05 +02:00

libzvbi-teletextdec: support multiple teletext pages in a single packet

After this patch, if a packet contains multiple teletext pages, the teletext
decoder can return the fist page and store the remaining pages in memory, and
return them to the user on the next calls to avcodec_decode_subtitle2.

Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
Marton Balint 2013-07-28 22:07:01 +02:00
parent 78e150c5e9
commit a494757b34

View File

@ -31,6 +31,15 @@
#define VBI_G(rgba) (((rgba) >> 8) & 0xFF)
#define VBI_B(rgba) (((rgba) >> 16) & 0xFF)
#define VBI_A(rgba) (((rgba) >> 24) & 0xFF)
#define MAX_BUFFERED_PAGES 25
typedef struct TeletextPage
{
AVSubtitleRect *sub_rect;
int pgno;
int subno;
int64_t pts;
} TeletextPage;
/* main data structure */
typedef struct TeletextContext
@ -47,7 +56,9 @@ typedef struct TeletextContext
int chop_spaces;
int lines_processed;
AVSubtitleRect *sub_rect;
TeletextPage *pages;
int nb_pages;
int64_t pts;
vbi_decoder * vbi;
vbi_dvb_demux * dx;
@ -72,11 +83,19 @@ chop_spaces_utf8(const unsigned char* t, int len)
return len;
}
static void
subtitle_rect_free(AVSubtitleRect **sub_rect)
{
av_freep(&(*sub_rect)->pict.data[0]);
av_freep(&(*sub_rect)->pict.data[1]);
av_freep(&(*sub_rect)->text);
av_freep(sub_rect);
}
// draw a page as text
static int
gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
gen_sub_text(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top)
{
AVSubtitleRect *sub_rect = ctx->sub_rect;
char *text;
const char *in;
char *out;
@ -140,9 +159,8 @@ gen_sub_text(TeletextContext *ctx, vbi_page *page, int chop_top)
}
static void
fix_transparency(TeletextContext *ctx, vbi_page *page, int chop_top, uint8_t transparent_color, int resx, int resy)
fix_transparency(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top, uint8_t transparent_color, int resx, int resy)
{
AVSubtitleRect *sub_rect = ctx->sub_rect;
int iy;
// Hack for transparency, inspired by VLC code...
@ -173,9 +191,8 @@ fix_transparency(TeletextContext *ctx, vbi_page *page, int chop_top, uint8_t tra
// draw a page as bitmap
static int
gen_sub_bitmap(TeletextContext *ctx, vbi_page *page, int chop_top)
gen_sub_bitmap(TeletextContext *ctx, AVSubtitleRect *sub_rect, vbi_page *page, int chop_top)
{
AVSubtitleRect *sub_rect = ctx->sub_rect;
int resx = page->columns * 12;
int resy = (page->rows - chop_top) * 10;
uint8_t ci, cmax = 0;
@ -206,7 +223,7 @@ gen_sub_bitmap(TeletextContext *ctx, vbi_page *page, int chop_top)
0, chop_top, page->columns, page->rows - chop_top,
/*reveal*/ 1, /*flash*/ 1);
fix_transparency(ctx, page, chop_top, cmax, resx, resy);
fix_transparency(ctx, sub_rect, page, chop_top, cmax, resx, resy);
sub_rect->x = ctx->x_offset;
sub_rect->y = ctx->y_offset;
sub_rect->w = resx;
@ -239,6 +256,7 @@ static void
handler(vbi_event *ev, void *user_data)
{
TeletextContext *ctx = user_data;
TeletextPage *new_pages;
vbi_page page;
int res;
char pgno_str[12];
@ -279,18 +297,29 @@ handler(vbi_event *ev, void *user_data)
av_log(ctx, AV_LOG_DEBUG, "%d x %d page chop:%d\n",
page.columns, page.rows, chop_top);
if (!ctx->sub_rect) {
ctx->sub_rect = av_mallocz(sizeof(*ctx->sub_rect));
if (ctx->sub_rect) {
res = (ctx->format_id == 0) ?
gen_sub_bitmap(ctx, &page, chop_top) :
gen_sub_text (ctx, &page, chop_top);
if (res)
av_freep(&ctx->sub_rect);
if (ctx->nb_pages < MAX_BUFFERED_PAGES) {
if ((new_pages = av_realloc_array(ctx->pages, ctx->nb_pages + 1, sizeof(TeletextPage)))) {
TeletextPage *cur_page = new_pages + ctx->nb_pages;
ctx->pages = new_pages;
cur_page->sub_rect = av_mallocz(sizeof(*cur_page->sub_rect));
cur_page->pts = ctx->pts;
cur_page->pgno = ev->ev.ttx_page.pgno;
cur_page->subno = ev->ev.ttx_page.subno;
if (cur_page->sub_rect) {
res = (ctx->format_id == 0) ?
gen_sub_bitmap(ctx, cur_page->sub_rect, &page, chop_top) :
gen_sub_text (ctx, cur_page->sub_rect, &page, chop_top);
if (res)
av_freep(&cur_page->sub_rect);
else
ctx->pages[ctx->nb_pages++] = *cur_page;
}
} else {
av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory to to buffer pages\n");
}
} else {
// FIXME: Multiple teletext pages in a single packet, some kind of buffering should be done instead of dropping the page...
av_log(ctx, AV_LOG_WARNING, "Missed page %s.%02x.\n", pgno_str, ev->ev.ttx_page.subno & 0xFF);
//TODO: If multiple packets contain more than one page, pages may got queued up, and this may happen...
av_log(ctx, AV_LOG_ERROR, "Buffered too many pages, dropping page %s.\n", pgno_str);
}
vbi_unref_page(&page);
@ -304,10 +333,11 @@ teletext_decode_frame(AVCodecContext *avctx,
TeletextContext *ctx = avctx->priv_data;
AVSubtitle *sub = data;
const uint8_t *buf = pkt->data;
unsigned int left = pkt->size;
int left = pkt->size;
uint8_t pesheader[45] = {0x00, 0x00, 0x01, 0xbd, 0x00, 0x00, 0x85, 0x80, 0x24, 0x21, 0x00, 0x01, 0x00, 0x01};
int pesheader_size = sizeof(pesheader);
const uint8_t *pesheader_buf = pesheader;
int ret = 0;
if (!ctx->vbi) {
if (!(ctx->vbi = vbi_decoder_new()))
@ -321,6 +351,10 @@ teletext_decode_frame(AVCodecContext *avctx,
if (!ctx->dx && (!(ctx->dx = vbi_dvb_pes_demux_new (/* callback */ NULL, NULL))))
return AVERROR(ENOMEM);
if (avctx->pkt_timebase.den && pkt->pts != AV_NOPTS_VALUE)
ctx->pts = av_rescale_q(pkt->pts, avctx->pkt_timebase, AV_TIME_BASE_Q);
if (left) {
// We allow unreasonably big packets, even if the standard only allows a max size of 1472
if ((pesheader_size + left) < 184 || (pesheader_size + left) > 65504 || (pesheader_size + left) % 184 != 0)
return AVERROR_INVALIDDATA;
@ -352,33 +386,41 @@ teletext_decode_frame(AVCodecContext *avctx,
ctx->lines_processed += lines;
}
}
ctx->pts = AV_NOPTS_VALUE;
ret = pkt->size;
}
// is there a subtitle to pass?
if (ctx->sub_rect) {
sub->format = (ctx->sub_rect->type == SUBTITLE_TEXT ? 1: 0);
if (ctx->nb_pages) {
int i;
sub->format = (ctx->pages->sub_rect->type == SUBTITLE_TEXT ? 1: 0);
sub->start_display_time = 0;
sub->end_display_time = ctx->sub_duration;
sub->num_rects = 0;
sub->pts = ctx->pages->pts;
if (ctx->sub_rect->type != SUBTITLE_NONE) {
if (ctx->pages->sub_rect->type != SUBTITLE_NONE) {
sub->rects = av_malloc(sizeof(*sub->rects) * 1);
if (sub->rects) {
sub->num_rects = 1;
sub->rects[0] = ctx->sub_rect;
sub->rects[0] = ctx->pages->sub_rect;
}
} else {
av_log(avctx, AV_LOG_DEBUG, "sending empty sub\n");
sub->rects = NULL;
}
if (!sub->rects) // no rect was passed
av_free(ctx->sub_rect);
ctx->sub_rect = NULL;
subtitle_rect_free(&ctx->pages->sub_rect);
for (i = 0; i < ctx->nb_pages - 1; i++)
ctx->pages[i] = ctx->pages[i + 1];
ctx->nb_pages--;
*data_size = 1;
} else
*data_size = 0;
return pkt->size;
return ret;
}
static int teletext_init_decoder(AVCodecContext *avctx)
@ -394,7 +436,7 @@ static int teletext_init_decoder(AVCodecContext *avctx)
ctx->dx = NULL;
ctx->vbi = NULL;
ctx->sub_rect = NULL;
ctx->pts = AV_NOPTS_VALUE;
if (!strcmp(ctx->format, "bitmap")) {
ctx->format_id = 0;
} else if (!strcmp(ctx->format, "text")) {
@ -421,11 +463,15 @@ static int teletext_close_decoder(AVCodecContext *avctx)
#ifdef DEBUG
av_log(avctx, AV_LOG_DEBUG, "lines_total=%u\n", ctx->lines_processed);
#endif
while (ctx->nb_pages)
subtitle_rect_free(&ctx->pages[--ctx->nb_pages].sub_rect);
av_freep(&ctx->pages);
vbi_dvb_demux_delete(ctx->dx);
vbi_decoder_delete(ctx->vbi);
ctx->dx = NULL;
ctx->vbi = NULL;
ctx->pts = AV_NOPTS_VALUE;
return 0;
}
@ -464,6 +510,7 @@ AVCodec ff_libzvbi_teletext_decoder = {
.init = teletext_init_decoder,
.close = teletext_close_decoder,
.decode = teletext_decode_frame,
.capabilities = CODEC_CAP_DELAY,
.flush = teletext_flush,
.priv_class= &teletext_class,
};