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