Browse code

Adding Closed caption Support

Signed-off-by: Anshul Maheshwari <anshul.ffmpeg@gmail.com>

To test Closed caption use following command
ffmpeg -f lavfi -i "movie=input.ts[out0+subcc]" -map s output.srt
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

Anshul Maheshwari authored on 2015/01/06 15:53:34
Showing 3 changed files
... ...
@@ -173,6 +173,7 @@ OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
173 173
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
174 174
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
175 175
                                           cavsdata.o mpeg12data.o
176
+OBJS-$(CONFIG_CCAPTION_DECODER)        += ccaption_dec.o
176 177
 OBJS-$(CONFIG_CDGRAPHICS_DECODER)      += cdgraphics.o
177 178
 OBJS-$(CONFIG_CDXL_DECODER)            += cdxl.o
178 179
 OBJS-$(CONFIG_CINEPAK_DECODER)         += cinepak.o
... ...
@@ -481,6 +481,7 @@ void avcodec_register_all(void)
481 481
     /* subtitles */
482 482
     REGISTER_ENCDEC (SSA,               ssa);
483 483
     REGISTER_ENCDEC (ASS,               ass);
484
+    REGISTER_DECODER(CCAPTION,          ccaption);
484 485
     REGISTER_ENCDEC (DVBSUB,            dvbsub);
485 486
     REGISTER_ENCDEC (DVDSUB,            dvdsub);
486 487
     REGISTER_DECODER(JACOSUB,           jacosub);
487 488
new file mode 100644
... ...
@@ -0,0 +1,529 @@
0
+/*
1
+ * Closed Caption Decoding
2
+ * Copyright (c) 2015 Anshul Maheshwari
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+#include "avcodec.h"
22
+#include "ass.h"
23
+#include "libavutil/opt.h"
24
+
25
+#define CHAR_DEBUG
26
+#define SCREEN_ROWS 15
27
+#define SCREEN_COLUMNS 32
28
+
29
+#define SET_FLAG(var, val) ( var |= ( 1 << (val) ) )
30
+#define UNSET_FLAG(var, val) ( var &=  ~( 1 << (val)) )
31
+#define CHECK_FLAG(var, val) ( (var) & (1 << (val) ) )
32
+
33
+/*
34
+ * TODO list
35
+ * 1) handle font and color completely
36
+ */
37
+enum cc_mode {
38
+    CCMODE_POPON,
39
+    CCMODE_PAINTON,
40
+    CCMODE_ROLLUP_2,
41
+    CCMODE_ROLLUP_3,
42
+    CCMODE_ROLLUP_4,
43
+    CCMODE_TEXT,
44
+};
45
+
46
+enum cc_color_code
47
+{
48
+    CCCOL_WHITE,
49
+    CCCOL_GREEN,
50
+    CCCOL_BLUE,
51
+    CCCOL_CYAN,
52
+    CCCOL_RED,
53
+    CCCOL_YELLOW,
54
+    CCCOL_MAGENTA,
55
+    CCCOL_USERDEFINED,
56
+    CCCOL_BLACK,
57
+    CCCOL_TRANSPARENT
58
+};
59
+
60
+enum cc_font
61
+{
62
+    CCFONT_REGULAR,
63
+    CCFONT_ITALICS,
64
+    CCFONT_UNDERLINED,
65
+    CCFONT_UNDERLINED_ITALICS
66
+};
67
+
68
+static const unsigned char pac2_attribs[][3] = // Color, font, ident
69
+{
70
+    { CCCOL_WHITE, CCFONT_REGULAR, 0 },  // 0x40 || 0x60
71
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 0 },  // 0x41 || 0x61
72
+    { CCCOL_GREEN, CCFONT_REGULAR, 0 },  // 0x42 || 0x62
73
+    { CCCOL_GREEN, CCFONT_UNDERLINED, 0 },  // 0x43 || 0x63
74
+    { CCCOL_BLUE, CCFONT_REGULAR, 0 },  // 0x44 || 0x64
75
+    { CCCOL_BLUE, CCFONT_UNDERLINED, 0 },  // 0x45 || 0x65
76
+    { CCCOL_CYAN, CCFONT_REGULAR, 0 },  // 0x46 || 0x66
77
+    { CCCOL_CYAN, CCFONT_UNDERLINED, 0 },  // 0x47 || 0x67
78
+    { CCCOL_RED, CCFONT_REGULAR, 0 },  // 0x48 || 0x68
79
+    { CCCOL_RED, CCFONT_UNDERLINED, 0 },  // 0x49 || 0x69
80
+    { CCCOL_YELLOW, CCFONT_REGULAR, 0 },  // 0x4a || 0x6a
81
+    { CCCOL_YELLOW, CCFONT_UNDERLINED, 0 },  // 0x4b || 0x6b
82
+    { CCCOL_MAGENTA, CCFONT_REGULAR, 0 },  // 0x4c || 0x6c
83
+    { CCCOL_MAGENTA, CCFONT_UNDERLINED, 0 },  // 0x4d || 0x6d
84
+    { CCCOL_WHITE, CCFONT_ITALICS, 0 },  // 0x4e || 0x6e
85
+    { CCCOL_WHITE, CCFONT_UNDERLINED_ITALICS, 0 },  // 0x4f || 0x6f
86
+    { CCCOL_WHITE, CCFONT_REGULAR, 0 },  // 0x50 || 0x70
87
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 0 },  // 0x51 || 0x71
88
+    { CCCOL_WHITE, CCFONT_REGULAR, 4 },  // 0x52 || 0x72
89
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 4 },  // 0x53 || 0x73
90
+    { CCCOL_WHITE, CCFONT_REGULAR, 8 },  // 0x54 || 0x74
91
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 8 },  // 0x55 || 0x75
92
+    { CCCOL_WHITE, CCFONT_REGULAR, 12 }, // 0x56 || 0x76
93
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 12 }, // 0x57 || 0x77
94
+    { CCCOL_WHITE, CCFONT_REGULAR, 16 }, // 0x58 || 0x78
95
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 16 }, // 0x59 || 0x79
96
+    { CCCOL_WHITE, CCFONT_REGULAR, 20 }, // 0x5a || 0x7a
97
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 20 }, // 0x5b || 0x7b
98
+    { CCCOL_WHITE, CCFONT_REGULAR, 24 }, // 0x5c || 0x7c
99
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 24 }, // 0x5d || 0x7d
100
+    { CCCOL_WHITE, CCFONT_REGULAR, 28 }, // 0x5e || 0x7e
101
+    { CCCOL_WHITE, CCFONT_UNDERLINED, 28 }  // 0x5f || 0x7f
102
+    /* total 32 entry */
103
+};
104
+/* 0-255 needs 256 space */
105
+static const uint8_t parity_table[256] = { 0, 1, 1, 0, 1, 0, 0, 1,
106
+                                           1, 0, 0, 1, 0, 1, 1, 0,
107
+                                           1, 0, 0, 1, 0, 1, 1, 0,
108
+                                           0, 1, 1, 0, 1, 0, 0, 1,
109
+                                           1, 0, 0, 1, 0, 1, 1, 0,
110
+                                           0, 1, 1, 0, 1, 0, 0, 1,
111
+                                           0, 1, 1, 0, 1, 0, 0, 1,
112
+                                           1, 0, 0, 1, 0, 1, 1, 0,
113
+                                           1, 0, 0, 1, 0, 1, 1, 0,
114
+                                           0, 1, 1, 0, 1, 0, 0, 1,
115
+                                           0, 1, 1, 0, 1, 0, 0, 1,
116
+                                           1, 0, 0, 1, 0, 1, 1, 0,
117
+                                           0, 1, 1, 0, 1, 0, 0, 1,
118
+                                           1, 0, 0, 1, 0, 1, 1, 0,
119
+                                           1, 0, 0, 1, 0, 1, 1, 0,
120
+                                           0, 1, 1, 0, 1, 0, 0, 1,
121
+                                           1, 0, 0, 1, 0, 1, 1, 0,
122
+                                           0, 1, 1, 0, 1, 0, 0, 1,
123
+                                           0, 1, 1, 0, 1, 0, 0, 1,
124
+                                           1, 0, 0, 1, 0, 1, 1, 0,
125
+                                           0, 1, 1, 0, 1, 0, 0, 1,
126
+                                           1, 0, 0, 1, 0, 1, 1, 0,
127
+                                           1, 0, 0, 1, 0, 1, 1, 0,
128
+                                           0, 1, 1, 0, 1, 0, 0, 1,
129
+                                           0, 1, 1, 0, 1, 0, 0, 1,
130
+                                           1, 0, 0, 1, 0, 1, 1, 0,
131
+                                           1, 0, 0, 1, 0, 1, 1, 0,
132
+                                           0, 1, 1, 0, 1, 0, 0, 1,
133
+                                           1, 0, 0, 1, 0, 1, 1, 0,
134
+                                           0, 1, 1, 0, 1, 0, 0, 1,
135
+                                           0, 1, 1, 0, 1, 0, 0, 1,
136
+                                           1, 0, 0, 1, 0, 1, 1, 0 };
137
+struct Screen {
138
+    /* +1 is used to compensate null character of string */
139
+    uint8_t characters[SCREEN_ROWS][SCREEN_COLUMNS+1];
140
+    /*
141
+     * Bitmask of used rows; if a bit is not set, the
142
+     * corresponding row is not used.
143
+     * for setting row 1  use row | (0 << 1)
144
+     * for setting row 15 use row | (1 << 14)
145
+     */
146
+    int16_t  row_used;
147
+};
148
+
149
+
150
+typedef struct CCaptionSubContext {
151
+    AVClass *class;
152
+    int row_cnt;
153
+    struct Screen screen[2];
154
+    int active_screen;
155
+    uint8_t cursor_row;
156
+    uint8_t cursor_column;
157
+    uint8_t cursor_color;
158
+    uint8_t cursor_font;
159
+    AVBPrint buffer;
160
+    int erase_display_memory;
161
+    int rollup;
162
+    enum  cc_mode mode;
163
+    int64_t start_time;
164
+    /* visible screen time */
165
+    int64_t startv_time;
166
+    int64_t end_time;
167
+    char prev_cmd[2];
168
+    /* buffer to store pkt data */
169
+    AVBufferRef *pktbuf;
170
+}CCaptionSubContext;
171
+
172
+
173
+static av_cold int init_decoder(AVCodecContext *avctx)
174
+{
175
+    int ret;
176
+    CCaptionSubContext *ctx = avctx->priv_data;
177
+
178
+    av_bprint_init(&ctx->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
179
+    /* taking by default roll up to 2 */
180
+    ctx->rollup = 2;
181
+    ret = ff_ass_subtitle_header_default(avctx);
182
+    /* allocate pkt buffer */
183
+    ctx->pktbuf = av_buffer_alloc(128);
184
+    if( !ctx->pktbuf) {
185
+        ret = AVERROR(ENOMEM);
186
+    }
187
+
188
+
189
+    return ret;
190
+}
191
+
192
+static av_cold int close_decoder(AVCodecContext *avctx)
193
+{
194
+    CCaptionSubContext *ctx = avctx->priv_data;
195
+    av_bprint_finalize( &ctx->buffer, NULL);
196
+    av_buffer_unref(&ctx->pktbuf);
197
+    return 0;
198
+}
199
+/**
200
+ * @param ctx closed caption context just to print log
201
+ */
202
+static int write_char (CCaptionSubContext *ctx, char *row,uint8_t col, char ch)
203
+{
204
+    if(col < SCREEN_COLUMNS) {
205
+        row[col] = ch;
206
+        return 0;
207
+    }
208
+    /* We have extra space at end only for null character */
209
+    else if ( col == SCREEN_COLUMNS && ch == 0) {
210
+        row[col] = ch;
211
+        return 0;
212
+    }
213
+    else {
214
+        av_log(ctx, AV_LOG_WARNING,"Data Ignored since exciding screen width\n");
215
+        return AVERROR_INVALIDDATA;
216
+    }
217
+}
218
+/**
219
+ * This function after validating parity bit, also remove it from data pair.
220
+ * The first byte doesn't pass parity, we replace it with a solid blank
221
+ * and process the pair.
222
+ * If the second byte doesn't pass parity, it returns INVALIDDATA
223
+ * user can ignore the whole pair and pass the other pair.
224
+ */
225
+static int validate_cc_data_pair (uint8_t *cc_data_pair)
226
+{
227
+    uint8_t cc_valid = (*cc_data_pair & 4) >>2;
228
+    uint8_t cc_type = *cc_data_pair & 3;
229
+
230
+    if (!cc_valid)
231
+        return AVERROR_INVALIDDATA;
232
+
233
+    // if EIA-608 data then verify parity.
234
+    if (cc_type==0 || cc_type==1) {
235
+        if (!parity_table[cc_data_pair[2]]) {
236
+            return AVERROR_INVALIDDATA;
237
+        }
238
+        if (!parity_table[cc_data_pair[1]]) {
239
+            cc_data_pair[1]=0x7F;
240
+        }
241
+    }
242
+
243
+    //Skip non-data
244
+    if( (cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD )
245
+         && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
246
+        return AVERROR_PATCHWELCOME;
247
+
248
+    //skip 708 data
249
+    if(cc_type == 3 || cc_type == 2 )
250
+        return AVERROR_PATCHWELCOME;
251
+
252
+    /* remove parity bit */
253
+    cc_data_pair[1] &= 0x7F;
254
+    cc_data_pair[2] &= 0x7F;
255
+
256
+
257
+    return 0;
258
+
259
+}
260
+
261
+static struct Screen *get_writing_screen(CCaptionSubContext *ctx)
262
+{
263
+    switch (ctx->mode) {
264
+    case CCMODE_POPON:
265
+        // use Inactive screen
266
+        return ctx->screen + !ctx->active_screen;
267
+    case CCMODE_PAINTON:
268
+    case CCMODE_ROLLUP_2:
269
+    case CCMODE_ROLLUP_3:
270
+    case CCMODE_ROLLUP_4:
271
+    case CCMODE_TEXT:
272
+        // use active screen
273
+        return ctx->screen + ctx->active_screen;
274
+    }
275
+    /* It was never an option */
276
+    return NULL;
277
+}
278
+
279
+static void handle_textattr( CCaptionSubContext *ctx, uint8_t hi, uint8_t lo )
280
+{
281
+    int i = lo - 0x20;
282
+    int ret;
283
+    struct Screen *screen = get_writing_screen(ctx);
284
+    char *row = screen->characters[ctx->cursor_row];
285
+
286
+    if( i >= 32)
287
+        return;
288
+
289
+    ctx->cursor_color =  pac2_attribs[i][0];
290
+    ctx->cursor_font = pac2_attribs[i][1];
291
+
292
+    SET_FLAG(screen->row_used,ctx->cursor_row);
293
+    ret = write_char(ctx, row, ctx->cursor_column, ' ');
294
+    if(ret == 0)
295
+        ctx->cursor_column++;
296
+}
297
+static void handle_pac( CCaptionSubContext *ctx, uint8_t hi, uint8_t lo )
298
+{
299
+    static const int8_t row_map[] = {
300
+        11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10
301
+    };
302
+    const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 );
303
+    struct Screen *screen = get_writing_screen(ctx);
304
+    char *row;
305
+    int indent,i,ret;
306
+
307
+    if( row_map[index] <= 0 )
308
+        return;
309
+
310
+    lo &= 0x1f;
311
+
312
+    ctx->cursor_row = row_map[index] - 1;
313
+    ctx->cursor_color =  pac2_attribs[lo][0];
314
+    ctx->cursor_font = pac2_attribs[lo][1];
315
+    ctx->cursor_column = 0;
316
+    indent = pac2_attribs[lo][2];
317
+    row = screen->characters[ctx->cursor_row];
318
+    for(i = 0;i < indent; i++) {
319
+        ret = write_char(ctx, row, ctx->cursor_column, ' ');
320
+        if(  ret == 0 )
321
+            ctx->cursor_column++;
322
+    }
323
+
324
+}
325
+
326
+/**
327
+ * @param pts it is required to set end time
328
+ */
329
+static int handle_edm(CCaptionSubContext *ctx,int64_t pts)
330
+{
331
+    int i;
332
+    int ret = 0;
333
+    struct Screen *screen = ctx->screen + ctx->active_screen;
334
+
335
+    ctx->start_time = ctx->startv_time;
336
+    for( i = 0; screen->row_used && i < SCREEN_ROWS; i++)
337
+    {
338
+        if(CHECK_FLAG(screen->row_used,i)) {
339
+            char *str = screen->characters[i];
340
+            /* skip space */
341
+            while (*str == ' ')
342
+                str++;
343
+            av_bprint_append_data(&ctx->buffer, str, strlen(str));
344
+            av_bprint_append_data(&ctx->buffer, "\\N",2);
345
+            UNSET_FLAG(screen->row_used, i);
346
+            ret = av_bprint_is_complete(&ctx->buffer);
347
+            if( ret == 0) {
348
+                ret = AVERROR(ENOMEM);
349
+                break;
350
+            }
351
+        }
352
+
353
+    }
354
+    ctx->startv_time = pts;
355
+    ctx->erase_display_memory = 1;
356
+    ctx->end_time = pts;
357
+    return ret;
358
+}
359
+static int handle_eoc(CCaptionSubContext *ctx, int64_t pts)
360
+{
361
+    int ret;
362
+    ret = handle_edm(ctx,pts);
363
+    ctx->active_screen = !ctx->active_screen;
364
+    ctx->cursor_column = 0;
365
+    return ret;
366
+}
367
+static void handle_delete_end_of_row( CCaptionSubContext *ctx, char hi, char lo)
368
+{
369
+    struct Screen *screen = get_writing_screen(ctx);
370
+    char *row = screen->characters[ctx->cursor_row];
371
+    write_char(ctx, row, ctx->cursor_column, 0);
372
+
373
+}
374
+static void handle_char(CCaptionSubContext *ctx, char hi, char lo, int64_t pts)
375
+{
376
+    struct Screen *screen = get_writing_screen(ctx);
377
+    char *row = screen->characters[ctx->cursor_row];
378
+    int ret;
379
+
380
+    SET_FLAG(screen->row_used,ctx->cursor_row);
381
+
382
+    ret = write_char(ctx, row, ctx->cursor_column, hi);
383
+    if( ret == 0 )
384
+        ctx->cursor_column++;
385
+
386
+    if(lo) {
387
+        ret = write_char(ctx, row, ctx->cursor_column, lo);
388
+        if ( ret == 0 )
389
+            ctx->cursor_column++;
390
+    }
391
+    write_char(ctx, row, ctx->cursor_column, 0);
392
+
393
+    /* reset prev command since character can repeat */
394
+    ctx->prev_cmd[0] = 0;
395
+    ctx->prev_cmd[1] = 0;
396
+#ifdef CHAR_DEBUG
397
+    av_log(ctx, AV_LOG_DEBUG,"(%c,%c)\n",hi,lo);
398
+#endif
399
+}
400
+static int process_cc608(CCaptionSubContext *ctx, int64_t pts, uint8_t hi, uint8_t lo)
401
+{
402
+    int ret = 0;
403
+#define COR3(var, with1, with2, with3)  ( (var) == (with1) ||  (var) == (with2) || (var) == (with3) )
404
+    if ( hi == ctx->prev_cmd[0] && lo == ctx->prev_cmd[1]) {
405
+    /* ignore redundant command */
406
+    } else if ( (hi == 0x10 && (lo >= 0x40 || lo <= 0x5f)) ||
407
+              ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) {
408
+        handle_pac(ctx, hi, lo);
409
+    } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) ||
410
+                ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) {
411
+        handle_textattr(ctx, hi, lo);
412
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x20 ) {
413
+    /* resume caption loading */
414
+        ctx->mode = CCMODE_POPON;
415
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x24 ) {
416
+        handle_delete_end_of_row(ctx, hi, lo);
417
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x25 ) {
418
+        ctx->rollup = 2;
419
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x26 ) {
420
+        ctx->rollup = 3;
421
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x27 ) {
422
+        ctx->rollup = 4;
423
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x29 ) {
424
+    /* resume direct captioning */
425
+        ctx->mode = CCMODE_PAINTON;
426
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2B ) {
427
+    /* resume text display */
428
+        ctx->mode = CCMODE_TEXT;
429
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2C ) {
430
+    /* erase display memory */
431
+        ret = handle_edm(ctx, pts);
432
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2D ) {
433
+    /* carriage return */
434
+        ctx->row_cnt++;
435
+        if(ctx->row_cnt >= ctx->rollup) {
436
+            ctx->row_cnt = 0;
437
+            ret = handle_edm(ctx, pts);
438
+            ctx->active_screen = !ctx->active_screen;
439
+        }
440
+        ctx->cursor_column = 0;
441
+    } else if ( COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2F ) {
442
+    /* end of caption */
443
+        ret = handle_eoc(ctx, pts);
444
+    } else if (hi>=0x20) {
445
+    /* Standard characters (always in pairs) */
446
+        handle_char(ctx, hi, lo, pts);
447
+    } else {
448
+    /* Ignoring all other non data code */
449
+    }
450
+
451
+    /* set prev command */
452
+     ctx->prev_cmd[0] = hi;
453
+     ctx->prev_cmd[1] = lo;
454
+
455
+#undef COR3
456
+    return ret;
457
+
458
+}
459
+static int decode(AVCodecContext *avctx, void *data, int *got_sub, AVPacket *avpkt)
460
+{
461
+    CCaptionSubContext *ctx = avctx->priv_data;
462
+    AVSubtitle *sub = data;
463
+    uint8_t *bptr = NULL;
464
+    int len = avpkt->size;
465
+    int ret = 0;
466
+    int i;
467
+
468
+    if ( ctx->pktbuf->size < len) {
469
+        ret = av_buffer_realloc(&ctx->pktbuf, len);
470
+         if(ret < 0) {
471
+            av_log(ctx, AV_LOG_WARNING, "Insufficient Memory of %d truncated to %d\n",len, ctx->pktbuf->size);
472
+            len = ctx->pktbuf->size;
473
+            ret = 0;
474
+        }
475
+    }
476
+    memcpy(ctx->pktbuf->data, avpkt->data, len);
477
+    bptr = ctx->pktbuf->data;
478
+
479
+
480
+    for (i  = 0; i < len; i += 3) {
481
+        uint8_t cc_type = *(bptr + i) & 3;
482
+        if (validate_cc_data_pair( bptr + i) )
483
+            continue;
484
+        /* ignoring data field 1 */
485
+        if(cc_type == 1)
486
+            continue;
487
+        else
488
+            process_cc608(ctx, avpkt->pts, *(bptr + i + 1) & 0x7f, *(bptr + i + 2) & 0x7f);
489
+        if(ctx->erase_display_memory && *ctx->buffer.str)
490
+        {
491
+            int start_time = av_rescale_q(ctx->start_time, avctx->time_base, (AVRational){ 1, 100 });
492
+            int end_time = av_rescale_q(ctx->end_time, avctx->time_base, (AVRational){ 1, 100 });
493
+#ifdef CHAR_DEBUG
494
+            av_log(ctx, AV_LOG_DEBUG,"cdp writing data (%s)\n",ctx->buffer.str);
495
+#endif
496
+            ret = ff_ass_add_rect(sub, ctx->buffer.str, start_time, end_time - start_time , 0);
497
+            if (ret < 0)
498
+                return ret;
499
+            sub->pts = av_rescale_q(ctx->start_time, avctx->time_base, AV_TIME_BASE_Q);
500
+            ctx->erase_display_memory = 0;
501
+            av_bprint_clear(&ctx->buffer);
502
+        }
503
+    }
504
+
505
+    *got_sub = sub->num_rects > 0;
506
+    return ret;
507
+}
508
+static const AVOption options[] = {
509
+    {NULL}
510
+};
511
+static const AVClass ccaption_dec_class = {
512
+    .class_name = "Closed caption Decoder",
513
+    .item_name  = av_default_item_name,
514
+    .option     = options,
515
+    .version    = LIBAVUTIL_VERSION_INT,
516
+};
517
+
518
+AVCodec ff_ccaption_decoder = {
519
+    .name           = "cc_dec",
520
+    .long_name      = NULL_IF_CONFIG_SMALL("Closed Caption (EIA-608 / CEA-708) Decoder"),
521
+    .type           = AVMEDIA_TYPE_SUBTITLE,
522
+    .id             = AV_CODEC_ID_EIA_608,
523
+    .priv_data_size = sizeof(CCaptionSubContext),
524
+    .init           = init_decoder,
525
+    .close          = close_decoder,
526
+    .decode         = decode,
527
+    .priv_class     = &ccaption_dec_class,
528
+};