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