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
25
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 */
44 };
45
57 };
58
64 };
65
66 static const unsigned char pac2_attribs[32][3] =
// Color, font, ident
67 {
100 /* total 32 entries */
101 };
102
103 /* 0-255 needs 256 spaces */
105 1, 0, 0, 1, 0, 1, 1, 0,
106 1, 0, 0, 1, 0, 1, 1, 0,
107 0, 1, 1, 0, 1, 0, 0, 1,
108 1, 0, 0, 1, 0, 1, 1, 0,
109 0, 1, 1, 0, 1, 0, 0, 1,
110 0, 1, 1, 0, 1, 0, 0, 1,
111 1, 0, 0, 1, 0, 1, 1, 0,
112 1, 0, 0, 1, 0, 1, 1, 0,
113 0, 1, 1, 0, 1, 0, 0, 1,
114 0, 1, 1, 0, 1, 0, 0, 1,
115 1, 0, 0, 1, 0, 1, 1, 0,
116 0, 1, 1, 0, 1, 0, 0, 1,
117 1, 0, 0, 1, 0, 1, 1, 0,
118 1, 0, 0, 1, 0, 1, 1, 0,
119 0, 1, 1, 0, 1, 0, 0, 1,
120 1, 0, 0, 1, 0, 1, 1, 0,
121 0, 1, 1, 0, 1, 0, 0, 1,
122 0, 1, 1, 0, 1, 0, 0, 1,
123 1, 0, 0, 1, 0, 1, 1, 0,
124 0, 1, 1, 0, 1, 0, 0, 1,
125 1, 0, 0, 1, 0, 1, 1, 0,
126 1, 0, 0, 1, 0, 1, 1, 0,
127 0, 1, 1, 0, 1, 0, 0, 1,
128 0, 1, 1, 0, 1, 0, 0, 1,
129 1, 0, 0, 1, 0, 1, 1, 0,
130 1, 0, 0, 1, 0, 1, 1, 0,
131 0, 1, 1, 0, 1, 0, 0, 1,
132 1, 0, 0, 1, 0, 1, 1, 0,
133 0, 1, 1, 0, 1, 0, 0, 1,
134 0, 1, 1, 0, 1, 0, 0, 1,
135 1, 0, 0, 1, 0, 1, 1, 0 };
136
138 /* +1 is used to compensate null character of string */
142 /*
143 * Bitmask of used rows; if a bit is not set, the
144 * corresponding row is not used.
145 * for setting row 1 use row | (1 << 0)
146 * for setting row 15 use row | (1 << 14)
147 */
149 };
150
151
165 /* visible screen time */
169 /* buffer to store pkt data */
172
173
175 {
178
180 /* taking by default roll up to 2 */
184 if(ret < 0) {
185 goto fail;
186 }
187 /* allocate pkt buffer */
191 }
192
193 fail:
195 }
196
198 {
202 return 0;
203 }
204
205 /**
206 * @param ctx closed caption context just to print log
207 */
209 {
211 row[col] = ch;
212 return 0;
213 }
214 /* We have extra space at end only for null character */
216 row[col] = ch;
217 return 0;
218 }
219 else {
222 }
223 }
224
225 /**
226 * This function after validating parity bit, also remove it from data pair.
227 * The first byte doesn't pass parity, we replace it with a solid blank
228 * and process the pair.
229 * If the second byte doesn't pass parity, it returns INVALIDDATA
230 * user can ignore the whole pair and pass the other pair.
231 */
233 {
234 uint8_t cc_valid = (*cc_data_pair & 4) >>2;
235 uint8_t cc_type = *cc_data_pair & 3;
236
237 if (!cc_valid)
239
240 // if EIA-608 data then verify parity.
241 if (cc_type==0 || cc_type==1) {
244 }
246 cc_data_pair[1]=0x7F;
247 }
248 }
249
250 //Skip non-data
251 if( (cc_data_pair[0] == 0xFA || cc_data_pair[0] == 0xFC || cc_data_pair[0] == 0xFD )
252 && (cc_data_pair[1] & 0x7F) == 0 && (cc_data_pair[2] & 0x7F) == 0)
254
255 //skip 708 data
256 if(cc_type == 3 || cc_type == 2 )
258
259 /* remove parity bit */
260 cc_data_pair[1] &= 0x7F;
261 cc_data_pair[2] &= 0x7F;
262
263
264 return 0;
265
266 }
267
269 {
272 // use Inactive screen
279 // use active screen
281 }
282 /* It was never an option */
284 }
285
287 {
289 int i, keep_lines;
290
292 return;
293
295
296 /* +1 signify cursor_row starts from 0
297 * Can't keep lines less then row cursor pos
298 */
300
301 for( i = 0; i < ctx->
cursor_row - keep_lines; i++ )
303
304
305 for( i = 0; i < keep_lines && screen->
row_used; i++ ) {
306 const int i_row = ctx->
cursor_row - keep_lines + i + 1;
307
313
314 }
316
317 }
318
320 {
321 int i;
325
327 {
330 /* skip space */
331 while (*str == ' ')
332 str++;
333
336 if( ret == 0) {
338 break;
339 }
340 }
341
342 }
346 }
347
349 {
350 int i = lo - 0x20;
354
355 if( i >= 32)
356 return;
357
360
363 if(ret == 0)
365 }
366
368 {
369 static const int8_t row_map[] = {
370 11, -1, 1, 2, 3, 4, 12, 13, 14, 15, 5, 6, 7, 8, 9, 10
371 };
372 const int index = ( (hi<<1) & 0x0e) | ( (lo>>5) & 0x01 );
374 char *row;
376
377 if( row_map[index] <= 0 ) {
379 return;
380 }
381
382 lo &= 0x1f;
383
390 for(i = 0;i < indent; i++) {
392 if( ret == 0 )
394 }
395
396 }
397
398 /**
399 * @param pts it is required to set end time
400 */
402 {
405
410 }
411
413 {
419 }
420
422 {
426
427 }
428
430 {
434
436
438 if( ret == 0 )
440
441 if(lo) {
443 if ( ret == 0 )
445 }
447
448 /* reset prev command since character can repeat */
451 if (lo)
452 av_dlog(ctx,
"(%c,%c)\n",hi,lo);
453 else
455 }
456
458 {
460 #define COR3(var, with1, with2, with3) ( (var) == (with1) || (var) == (with2) || (var) == (with3) )
462 /* ignore redundant command */
463 } else if ( (hi == 0x10 && (lo >= 0x40 || lo <= 0x5f)) ||
464 ( (hi >= 0x11 && hi <= 0x17) && (lo >= 0x40 && lo <= 0x7f) ) ) {
466 } else if ( ( hi == 0x11 && lo >= 0x20 && lo <= 0x2f ) ||
467 ( hi == 0x17 && lo >= 0x2e && lo <= 0x2f) ) {
469 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x20 ) {
470 /* resume caption loading */
472 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x24 ) {
474 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x25 ) {
477 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x26 ) {
480 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x27 ) {
483 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x29 ) {
484 /* resume direct captioning */
486 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2B ) {
487 /* resume text display */
489 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2C ) {
490 /* erase display memory */
492 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2D ) {
493 /* carriage return */
494 av_dlog(ctx,
"carriage return\n");
499 }
else if (
COR3(hi, 0x14, 0x15, 0x1C) && lo == 0x2F ) {
500 /* end of caption */
503 } else if (hi>=0x20) {
504 /* Standard characters (always in pairs) */
506 } else {
507 /* Ignoring all other non data code */
508 av_dlog(ctx,
"Unknown command 0x%hhx 0x%hhx\n", hi, lo);
509 }
510
511 /* set prev command */
514
515 #undef COR3
517
518 }
519
521 {
527 int i;
528
531 if(ret < 0) {
534 ret = 0;
535 }
536 }
539
540
541 for (i = 0; i <
len; i += 3) {
542 uint8_t cc_type = *(bptr + i) & 3;
544 continue;
545 /* ignoring data field 1 */
546 if(cc_type == 1)
547 continue;
548 else
549 process_cc608(ctx, avpkt->
pts, *(bptr + i + 1) & 0x7f, *(bptr + i + 2) & 0x7f);
551 {
556 if (ret < 0)
561 }
562 }
563
564 *got_sub = sub->num_rects > 0;
566 }
567
570 };
571
577 };
578
588 .priv_class = &ccaption_dec_class,
589 };