[FFmpeg-devel] [PATCH v2 2/9] lavc/ccaption_dec: implement real_time option
Anshul
anshul.ffmpeg at gmail.com
Wed Jan 13 09:48:36 CET 2016
On 13 January 2016 7:12:53 AM IST, Aman Gupta <ffmpeg at tmm1.net> wrote:
>From: Aman Gupta <aman at tmm1.net>
>
>This new mode is useful for realtime decoding of closed captions so
>they
>can be display along with mpeg2 frames.
>
>Closed caption streams contain two major types of captions:
>
>- POPON captions, which are buffered off-screen and displayed
> only after EOC (end of caption, aka display buffer)
>
>- PAINTON/ROLLUP captions, which are written to the display as soon as
> they arrive.
>
>In a typical real-time eia608 decoder, commands like EOC (end of
>caption; display buffer), EDM (erase display memory) and EBM (erase
>buffered memory) perform their expected functions as soon as the
>commands are processed. This is implemented in the real_time branches
>added in this commit.
>
>Before this commit, and in the !real_time branches after this commit,
>the decoder cleverly implements its own version of the decoder which is
>specifically geared towards buffered decoding. It does so by actively
>ignoring commands like EBM (erase buffered memory), and then re-using
>the non-display buffer to hold the previous caption while the new one
>is
>received. This is the opposite of the real-time decoder, which uses the
>non-display buffer to hold the new caption while the display buffer is
>still showing the current caption.
>
>In addition to ignoring EBM, the buffered decoder also has custom
>implementations for EDM and EOC. An EDM (erase display memory) command
>flushes the existing contents before clearing the screen, and EOC
>similarly always flushes the active buffer (the previous subtitle)
>before flipping buffers.
>---
>libavcodec/ccaption_dec.c | 80
>++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 72 insertions(+), 8 deletions(-)
>
>diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c
>index a9dfc94..6bdd754 100644
>--- a/libavcodec/ccaption_dec.c
>+++ b/libavcodec/ccaption_dec.c
>@@ -116,6 +116,7 @@ struct Screen {
>
> typedef struct CCaptionSubContext {
> AVClass *class;
>+ int real_time;
> struct Screen screen[2];
> int active_screen;
> uint8_t cursor_row;
>@@ -130,6 +131,8 @@ typedef struct CCaptionSubContext {
> /* visible screen time */
> int64_t startv_time;
> int64_t end_time;
>+ int screen_touched;
>+ int64_t last_real_time;
> char prev_cmd[2];
> /* buffer to store pkt data */
> AVBufferRef *pktbuf;
>@@ -428,15 +431,33 @@ static void handle_edm(CCaptionSubContext *ctx,
>int64_t pts)
> {
> struct Screen *screen = ctx->screen + ctx->active_screen;
>
>- reap_screen(ctx, pts);
>+ // In buffered mode, keep writing to screen until it is wiped.
>+ // Before wiping the display, capture contents to emit subtitle.
>+ if (!ctx->real_time)
>+ reap_screen(ctx, pts);
>+
> screen->row_used = 0;
>+
>+ // In realtime mode, emit an empty caption so the last one doesn't
>+ // stay on the screen.
>+ if (ctx->real_time)
>+ reap_screen(ctx, pts);
> }
>
> static void handle_eoc(CCaptionSubContext *ctx, int64_t pts)
> {
>- handle_edm(ctx,pts);
>+ // In buffered mode, we wait til the *next* EOC and
>+ // reap what was already on the screen since the last EOC.
>+ if (!ctx->real_time)
>+ handle_edm(ctx,pts);
>+
> ctx->active_screen = !ctx->active_screen;
> ctx->cursor_column = 0;
>+
>+ // In realtime mode, we display the buffered contents (after
>+ // flipping the buffer to active above) as soon as EOC arrives.
>+ if (ctx->real_time)
>+ reap_screen(ctx, pts);
> }
>
>static void handle_delete_end_of_row(CCaptionSubContext *ctx, char hi,
>char lo)
>@@ -458,6 +479,9 @@ static void handle_char(CCaptionSubContext *ctx,
>char hi, char lo, int64_t pts)
> }
> write_char(ctx, screen, 0);
>
>+ if (ctx->mode != CCMODE_POPON)
>+ ctx->screen_touched = 1;
>+
> /* reset prev command since character can repeat */
> ctx->prev_cmd[0] = 0;
> ctx->prev_cmd[1] = 0;
>@@ -507,10 +531,20 @@ static void process_cc608(CCaptionSubContext
>*ctx, int64_t pts, uint8_t hi, uint
> case 0x2d:
> /* carriage return */
> ff_dlog(ctx, "carriage return\n");
>- reap_screen(ctx, pts);
>+ if (!ctx->real_time)
>+ reap_screen(ctx, pts);
> roll_up(ctx);
> ctx->cursor_column = 0;
> break;
>+ case 0x2e:
>+ /* erase buffered (non displayed) memory */
>+ // Only in realtime mode. In buffered mode, we re-use the
>inactive screen
>+ // for our own buffering.
>+ if (ctx->real_time) {
>+ struct Screen *screen = ctx->screen +
>!ctx->active_screen;
>+ screen->row_used = 0;
>+ }
>+ break;
> case 0x2f:
> /* end of caption */
> ff_dlog(ctx, "handle_eoc\n");
>@@ -562,24 +596,54 @@ static int decode(AVCodecContext *avctx, void
>*data, int *got_sub, AVPacket *avp
> continue;
> else
>process_cc608(ctx, avpkt->pts, *(bptr + i + 1) & 0x7f, *(bptr + i + 2)
>& 0x7f);
>- if (ctx->buffer_changed && *ctx->buffer.str)
>+
>+ if (!ctx->buffer_changed)
>+ continue;
>+ ctx->buffer_changed = 0;
>+
>+ if (*ctx->buffer.str || ctx->real_time)
> {
>- int start_time = av_rescale_q(ctx->start_time,
>avctx->time_base, ass_tb);
>- int end_time = av_rescale_q(ctx->end_time,
>avctx->time_base, ass_tb);
>+ int64_t sub_pts = ctx->real_time ? avpkt->pts :
>ctx->start_time;
>+ int start_time = av_rescale_q(sub_pts, avctx->time_base,
>ass_tb);
>+ int duration = 10 * ass_tb.den;
>+ if (!ctx->real_time) {
>+ int end_time = av_rescale_q(ctx->end_time,
>avctx->time_base, ass_tb);
>+ duration = end_time - start_time;
>+ }
> ff_dlog(ctx, "cdp writing data (%s)\n",ctx->buffer.str);
>- ret = ff_ass_add_rect_bprint(sub, &ctx->buffer,
>start_time, end_time - start_time);
>+ ret = ff_ass_add_rect_bprint(sub, &ctx->buffer,
>start_time, duration);
> if (ret < 0)
> return ret;
>- sub->pts = av_rescale_q(ctx->start_time, avctx->time_base,
>AV_TIME_BASE_Q);
>+ sub->pts = av_rescale_q(sub_pts, avctx->time_base,
>AV_TIME_BASE_Q);
> ctx->buffer_changed = 0;
>+ ctx->last_real_time = avpkt->pts;
>+ ctx->screen_touched = 0;
> }
> }
>
>+ if (ctx->real_time && ctx->screen_touched &&
>+ avpkt->pts > ctx->last_real_time + av_rescale_q(20, ass_tb,
>avctx->time_base)) {
>+ ctx->last_real_time = avpkt->pts;
>+ ctx->screen_touched = 0;
>+
>+ capture_screen(ctx);
>+ ctx->buffer_changed = 0;
>+
>+ int start_time = av_rescale_q(avpkt->pts, avctx->time_base,
>ass_tb);
>+ ret = ff_ass_add_rect_bprint(sub, &ctx->buffer, start_time,
>-1);
>+ if (ret < 0)
>+ return ret;
>+ sub->pts = av_rescale_q(avpkt->pts, avctx->time_base,
>AV_TIME_BASE_Q);
>+ }
>+
> *got_sub = sub->num_rects > 0;
> return ret;
> }
>
>+#define OFFSET(x) offsetof(CCaptionSubContext, x)
>+#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
> static const AVOption options[] = {
>+ { "real_time", "emit subtitle events as they are decoded for
>real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 },
>0, 1, SD },
> {NULL}
> };
>
LGTM.
-Anshul
More information about the ffmpeg-devel
mailing list