Mercurial > mplayer.hg
annotate sub/sub_cc.c @ 34756:df3ff52039fe
Add code to support CC subtitles in ASTC and MOV.
Code to actually use these will be added later, since it
needs special code in FFmpeg.
Code for MOV is already read, ASTC might take longer.
author | reimar |
---|---|
date | Sat, 07 Apr 2012 00:10:27 +0000 |
parents | 788175a8c14a |
children | 430e164238d1 |
rev | line source |
---|---|
32458 | 1 /* |
2 * decoder for Closed Captions | |
3 * | |
4 * This decoder relies on MPlayer's OSD to display subtitles. | |
5 * Be warned that decoding is somewhat preliminary, though it basically works. | |
6 * | |
7 * Most notably, only the text information is decoded as of now, discarding | |
8 * color, background and position info (see source below). | |
9 * | |
10 * uses source from the xine closed captions decoder | |
11 * | |
12 * Copyright (C) 2002 Matteo Giani | |
13 * | |
14 * This file is part of MPlayer. | |
15 * | |
16 * MPlayer is free software; you can redistribute it and/or modify | |
17 * it under the terms of the GNU General Public License as published by | |
18 * the Free Software Foundation; either version 2 of the License, or | |
19 * (at your option) any later version. | |
20 * | |
21 * MPlayer is distributed in the hope that it will be useful, | |
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
24 * GNU General Public License for more details. | |
25 * | |
26 * You should have received a copy of the GNU General Public License along | |
27 * with MPlayer; if not, write to the Free Software Foundation, Inc., | |
28 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
29 */ | |
30 | |
31 #include <stdio.h> | |
32 #include <stdlib.h> | |
33 #include <string.h> | |
34 | |
35 #include "config.h" | |
34756 | 36 #include "mp_msg.h" |
32464
22888a8cb312
Do not use a path for including files in the same directory.
reimar
parents:
32458
diff
changeset
|
37 #include "sub_cc.h" |
32458 | 38 |
32464
22888a8cb312
Do not use a path for including files in the same directory.
reimar
parents:
32458
diff
changeset
|
39 #include "subreader.h" |
32458 | 40 |
41 #include "libvo/video_out.h" | |
32467 | 42 #include "sub.h" |
32458 | 43 |
34756 | 44 #include "libavutil/avutil.h" |
45 | |
32458 | 46 |
47 #define CC_MAX_LINE_LENGTH 64 | |
48 | |
49 static char chartbl[128]; | |
50 | |
51 static subtitle buf1,buf2; | |
52 static subtitle *fb,*bb; | |
53 | |
54 static unsigned int cursor_pos=0; | |
55 | |
56 static int initialized=0; | |
57 | |
58 #define CC_ROLLON 1 | |
59 #define CC_ROLLUP 2 | |
60 | |
61 static int cc_mode=CC_ROLLON; | |
62 static int cc_lines=4; ///< number of visible rows in CC roll-up mode, not used in CC roll-on mode | |
63 | |
64 static void build_char_table(void) | |
65 { | |
66 int i; | |
67 /* first the normal ASCII codes */ | |
68 for (i = 0; i < 128; i++) | |
69 chartbl[i] = (char) i; | |
70 /* now the special codes */ | |
34702 | 71 chartbl[0x2a] = 0xe1; /* Latin Small Letter A with acute */ |
72 chartbl[0x5c] = 0xe9; /* Latin Small Letter E with acute */ | |
73 chartbl[0x5e] = 0xed; /* Latin Small Letter I with acute */ | |
74 chartbl[0x5f] = 0xf3; /* Latin Small Letter O with acute */ | |
75 chartbl[0x60] = 0xfa; /* Latin Small Letter U with acute */ | |
76 chartbl[0x7b] = 0xe7; /* Latin Small Letter C with cedilla */ | |
77 chartbl[0x7c] = 0xf7; /* Division sign */ | |
78 chartbl[0x7d] = 0xd1; /* Latin Capital letter N with tilde */ | |
79 chartbl[0x7e] = 0xf1; /* Latin Small Letter N with tilde */ | |
80 chartbl[0x7f] = 0xa4; /* Currency sign FIXME: this should be a solid block */ | |
32458 | 81 } |
82 | |
83 static void clear_buffer(subtitle *buf) | |
84 { | |
85 int i; | |
86 buf->lines=0; | |
32511
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32467
diff
changeset
|
87 for (i = 0; i < SUB_MAX_TEXT; i++) { |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32467
diff
changeset
|
88 free(buf->text[i]); |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32467
diff
changeset
|
89 buf->text[i] = NULL; |
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32467
diff
changeset
|
90 } |
32458 | 91 } |
92 | |
93 | |
94 /** | |
95 \brief scroll buffer one line up | |
96 \param buf buffer to scroll | |
97 */ | |
98 static void scroll_buffer(subtitle* buf) | |
99 { | |
100 int i; | |
101 | |
102 while(buf->lines > cc_lines) | |
103 { | |
32511
b39155e98ac3
Remove some useless NULL pointer checks before invoking free() on the pointer.
diego
parents:
32467
diff
changeset
|
104 free(buf->text[0]); |
32458 | 105 |
32512 | 106 for(i = 0; i < buf->lines - 1; i++) buf->text[i] = buf->text[i+1]; |
32458 | 107 |
108 buf->text[buf->lines-1] = NULL; | |
109 buf->lines--; | |
110 } | |
111 } | |
112 | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
113 static int channel; |
32458 | 114 |
115 void subcc_init(void) | |
116 { | |
117 int i; | |
118 //printf("subcc_init(): initing...\n"); | |
119 build_char_table(); | |
120 for(i=0;i<SUB_MAX_TEXT;i++) {buf1.text[i]=buf2.text[i]=NULL;} | |
121 buf1.lines=buf2.lines=0; | |
122 fb=&buf1; | |
123 bb=&buf2; | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
124 channel = -1; |
32458 | 125 |
126 initialized=1; | |
127 } | |
128 | |
129 | |
130 static void display_buffer(subtitle *buf) | |
131 { | |
132 vo_sub = buf; | |
133 vo_osd_changed(OSDTYPE_SUBTITLE); | |
134 } | |
135 | |
136 | |
137 static void append_char(char c) | |
138 { | |
139 if(!bb->lines) {bb->lines++; cursor_pos=0;} | |
140 if(bb->text[bb->lines - 1]==NULL) | |
141 { | |
32513 | 142 bb->text[bb->lines - 1] = calloc(1, CC_MAX_LINE_LENGTH); |
32458 | 143 cursor_pos=0; |
144 } | |
145 | |
146 if(c=='\n') | |
147 { | |
148 if(cursor_pos>0 && bb->lines < SUB_MAX_TEXT) | |
149 { | |
150 bb->lines++;cursor_pos=0; | |
151 if(cc_mode==CC_ROLLUP){ //Carriage return - scroll buffer one line up | |
152 bb->text[bb->lines - 1]=calloc(1, CC_MAX_LINE_LENGTH); | |
153 scroll_buffer(bb); | |
154 } | |
155 } | |
156 } | |
157 else | |
158 { | |
159 if(cursor_pos==CC_MAX_LINE_LENGTH-1) | |
160 { | |
161 fprintf(stderr,"CC: append_char() reached CC_MAX_LINE_LENGTH!\n"); | |
162 return; | |
163 } | |
164 bb->text[bb->lines - 1][cursor_pos++]=c; | |
165 } | |
166 //In CC roll-up mode data should be shown immediately | |
167 if(cc_mode==CC_ROLLUP) display_buffer(bb); | |
168 } | |
169 | |
170 | |
171 static void swap_buffers(void) | |
172 { | |
173 subtitle *foo; | |
174 foo=fb; | |
175 fb=bb; | |
176 bb=foo; | |
177 } | |
178 | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
179 static int selected_channel(void) |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
180 { |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
181 return subcc_enabled - 1; |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
182 } |
32458 | 183 |
184 static void cc_decode_EIA608(unsigned short int data) | |
185 { | |
186 | |
187 static unsigned short int lastcode=0x0000; | |
32566 | 188 uint8_t c1 = data & 0x7f; |
189 uint8_t c2 = (data >> 8) & 0x7f; | |
32458 | 190 |
191 if (c1 & 0x60) { /* normal character, 0x20 <= c1 <= 0x7f */ | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
192 if (channel != (selected_channel() & 1)) |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
193 return; |
32458 | 194 append_char(chartbl[c1]); |
195 if(c2 & 0x60) /*c2 might not be a normal char even if c1 is*/ | |
196 append_char(chartbl[c2]); | |
197 } | |
198 else if (c1 & 0x10) // control code / special char | |
199 { | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
200 channel = (c1 & 0x08) >> 3; |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
201 if (channel != (selected_channel() & 1)) |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
202 return; |
32458 | 203 c1&=~0x08; |
204 if(data!=lastcode) | |
205 { | |
206 if(c2 & 0x40) { /*PAC, Preamble Address Code */ | |
207 append_char('\n'); /*FIXME properly interpret PACs*/ | |
208 } | |
209 else | |
210 switch(c1) | |
211 { | |
212 case 0x10: break; // ext attribute | |
213 case 0x11: | |
214 if((c2 & 0x30)==0x30) | |
215 { | |
216 //printf("[debug]:Special char (ignored)\n"); | |
217 /*cc_decode_special_char()*/; | |
218 } | |
219 else if (c2 & 0x20) | |
220 { | |
221 //printf("[debug]: midrow_attr (ignored)\n"); | |
222 /*cc_decode_midrow_attr()*/; | |
223 } | |
224 break; | |
225 case 0x14: | |
226 switch(c2) | |
227 { | |
228 case 0x00: //CC roll-on mode | |
229 cc_mode=CC_ROLLON; | |
230 break; | |
231 case 0x25: //CC roll-up, 2 rows | |
232 case 0x26: //CC roll-up, 3 rows | |
233 case 0x27: //CC roll-up, 4 rows | |
234 cc_lines=c2-0x23; | |
235 cc_mode=CC_ROLLUP; | |
236 break; | |
237 case 0x2C: display_buffer(NULL); //EDM | |
238 clear_buffer(fb); break; | |
239 case 0x2d: append_char('\n'); //carriage return | |
240 break; | |
241 case 0x2e: clear_buffer(bb); //ENM | |
242 break; | |
243 case 0x2f: swap_buffers(); //Swap buffers | |
244 display_buffer(fb); | |
245 clear_buffer(bb); | |
246 break; | |
247 } | |
248 break; | |
249 case 0x17: | |
250 if( c2>=0x21 && c2<=0x23) //TAB | |
251 { | |
252 break; | |
253 } | |
254 } | |
255 } | |
256 } | |
257 lastcode=data; | |
258 } | |
259 | |
32566 | 260 static void subcc_decode(const uint8_t *inputbuffer, unsigned int inputlength) |
32458 | 261 { |
262 /* The first number may denote a channel number. I don't have the | |
263 * EIA-708 standard, so it is hard to say. | |
264 * From what I could figure out so far, the general format seems to be: | |
265 * | |
266 * repeat | |
267 * | |
268 * 0xfe starts 2 byte sequence of unknown purpose. It might denote | |
32522 | 269 * field #2 in line 21 of the VBI. |
270 * Treating it identical of 0xff fixes | |
271 * http://samples.mplayerhq.hu/MPEG-VOB/ClosedCaptions/Starship_Troopers.vob | |
32458 | 272 * |
273 * 0xff starts 2 byte EIA-608 sequence, field #1 in line 21 of the VBI. | |
274 * Followed by a 3-code triplet that starts either with 0xff or | |
275 * 0xfe. In either case, the following triplet needs to be ignored | |
276 * for line 21, field 1. | |
277 * | |
278 * 0x00 is padding, followed by 2 more 0x00. | |
279 * | |
280 * 0x01 always seems to appear at the beginning, always seems to | |
281 * be followed by 0xf8, 8-bit number. | |
282 * The lower 7 bits of this 8-bit number seem to denote the | |
283 * number of code triplets that follow. | |
284 * The most significant bit denotes whether the Line 21 field 1 | |
285 * captioning information is at odd or even triplet offsets from this | |
286 * beginning triplet. 1 denotes odd offsets, 0 denotes even offsets. | |
287 * | |
288 * Most captions are encoded with odd offsets, so this is what we | |
289 * will assume. | |
290 * | |
291 * until end of packet | |
292 */ | |
32566 | 293 const uint8_t *current = inputbuffer; |
32458 | 294 unsigned int curbytes = 0; |
32566 | 295 uint8_t data1, data2; |
296 uint8_t cc_code; | |
32458 | 297 int odd_offset = 1; |
298 | |
299 while (curbytes < inputlength) { | |
32512 | 300 cc_code = current[0]; |
32458 | 301 |
302 if (inputlength - curbytes < 2) { | |
303 #ifdef LOG_DEBUG | |
304 fprintf(stderr, "Not enough data for 2-byte CC encoding\n"); | |
305 #endif | |
306 break; | |
307 } | |
308 | |
32512 | 309 data1 = current[1]; |
310 data2 = current[2]; | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
311 current += 3; curbytes += 3; |
32458 | 312 |
313 switch (cc_code) { | |
314 case 0xfe: | |
315 case 0xff: | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
316 odd_offset ^= 1; |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
317 if (odd_offset != selected_channel() >> 1) |
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
318 break; |
32458 | 319 /* expect EIA-608 CC1/CC2 encoding */ |
320 // FIXME check parity! | |
321 // Parity check omitted assuming we are reading from a DVD and therefore | |
322 // we should encounter no "transmission errors". | |
323 cc_decode_EIA608(data1 | (data2 << 8)); | |
324 break; | |
325 | |
326 case 0x00: | |
327 /* This seems to be just padding */ | |
328 break; | |
329 | |
330 case 0x01: | |
32520
5e062dc4a04d
Add code to allow selecting the Close Captioning channel.
reimar
parents:
32513
diff
changeset
|
331 odd_offset = data2 >> 7; |
32458 | 332 break; |
333 | |
334 default: | |
335 //#ifdef LOG_DEBUG | |
336 fprintf(stderr, "Unknown CC encoding: %x\n", cc_code); | |
337 //#endif | |
338 break; | |
339 } | |
340 } | |
341 } | |
342 | |
34756 | 343 static const uint8_t mov_cc_signature_1[] = {0, 0, 0, 0xa, 'c', 'd', 'a', 't'}; |
344 static const uint8_t mov_cc_signature_2[] = {0, 0, 0, 0xa, 'c', 'd', 't', '2'}; | |
345 /** | |
346 * MOV uses a vastly more verbose representation for EIA 608 CC data than DVDs. | |
347 * This function handles that case. | |
348 */ | |
349 static void mov_subcc_decode(const uint8_t *data, unsigned len) | |
350 { | |
351 while (len >= 10) { | |
352 int channel = -1; | |
353 if (memcmp(data, mov_cc_signature_1, sizeof(mov_cc_signature_1)) == 0) { | |
354 channel = 0; | |
355 } else if (memcmp(data, mov_cc_signature_2, sizeof(mov_cc_signature_2)) == 0) { | |
356 channel = 1; | |
357 } else { | |
358 mp_msg(MSGT_OSD, MSGL_V, "Unknown MOV 608 CC formatting\n"); | |
359 data++; | |
360 len--; | |
361 continue; | |
362 } | |
363 if (channel == selected_channel() >> 1) | |
364 cc_decode_EIA608(data[8] | (data[9] << 8)); | |
365 data += 10; | |
366 len -= 10; | |
367 } | |
368 } | |
32458 | 369 |
32566 | 370 void subcc_process_data(const uint8_t *inputdata, unsigned int len) |
32458 | 371 { |
34756 | 372 int mov_mode = len >= 10 && |
373 memcmp(inputdata, mov_cc_signature_1, sizeof(mov_cc_signature_1)) == 0; | |
32458 | 374 if(!subcc_enabled) return; |
375 if(!initialized) subcc_init(); | |
376 | |
34756 | 377 if (mov_mode) { |
378 mov_subcc_decode(inputdata, len); | |
379 return; | |
380 } | |
32458 | 381 subcc_decode(inputdata, len); |
382 } | |
34756 | 383 |
384 /** | |
385 * This processes CC captions in the format as found in ASTC broadcasts. | |
386 * Like DVD CC it is stored inside the MPEG-frame userdata, but with two | |
387 * differences: | |
388 * 1) It starts with "GA" instead of "CC" | |
389 * 2) It _must_ be reordered in the way the decoder reorders the video frames | |
390 * The latter makes things difficult and is the reason why there is no support | |
391 * for this yet beyond this function. | |
392 */ | |
393 void subcc_process_eia708(const uint8_t *data, int len) | |
394 { | |
395 int cc_count; | |
396 if (!subcc_enabled) | |
397 return; | |
398 if (!initialized) | |
399 subcc_init(); | |
400 if (len <= 5) | |
401 return; | |
402 if (data[0] != '9' || data[1] != '4' || data[2] != 3) { | |
403 mp_msg(MSGT_OSD, MSGL_ERR, "Unknown ATSC CC type " | |
404 "0x%"PRIx8" 0x%"PRIx8" 0x%"PRIx8"\n", | |
405 data[0], data[1], data[2]); | |
406 return; | |
407 } | |
408 // process_cc_data_flag | |
409 if (!(data[3] & 0x40)) | |
410 return; | |
411 cc_count = data[3] & 0x1f; | |
412 data += 5; | |
413 len -= 5; | |
414 cc_count = FFMIN(cc_count, len / 3); | |
415 while (cc_count--) { | |
416 // EAI-608 data | |
417 if ((data[0] & 0xfe) == 0xfc && (data[0] & 1) == selected_channel() >> 1) | |
418 cc_decode_EIA608(data[1] | (data[2] << 8)); | |
419 data += 3; | |
420 } | |
421 } |