aboutsummaryrefslogtreecommitdiffhomepage
path: root/sub_cc.c
blob: 2acebf01bcca9dbc0fef96c95899a9676f90d7fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
 * sub_cc.c - Decoder for Closed Captions
 *
 * This decoder relies on MPlayer's OSD to display subtitles.
 * Be warned that the decoding is somewhat preliminary, though it basically works.
 * 
 * Most notably, only the text information is decoded as of now, discarding color,
 * background and position info (see source below).
 * 
 * by Matteo Giani
 *
 * uses source from the xine closed captions decoder
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include "sub_cc.h"

#include "subreader.h"

#include "libvo/video_out.h"
#include "libvo/sub.h"


#define CC_MAX_LINE_LENGTH 64

static char chartbl[128];

static subtitle buf1,buf2;
static subtitle *fb,*bb;

static unsigned int cursor_pos=0;

static int initialized=0;

#define CC_ROLLON 1
#define CC_ROLLUP 2

static int cc_mode=CC_ROLLON;
static int cc_lines=4; ///< number of visible rows in CC roll-up mode, not used in CC roll-on mode

static void display_buffer(subtitle * buf);

static void build_char_table(void)
{
  int i;
  /* first the normal ASCII codes */
  for (i = 0; i < 128; i++)
    chartbl[i] = (char) i;
  /* now the special codes */
  chartbl[0x2a] = 'á';
  chartbl[0x5c] = 'é';
  chartbl[0x5e] = 'í';
  chartbl[0x5f] = 'ó';
  chartbl[0x60] = 'ú';
  chartbl[0x7b] = 'ç';
  chartbl[0x7c] = '÷';
  chartbl[0x7d] = 'Ñ';
  chartbl[0x7e] = 'ñ';
  chartbl[0x7f] = '¤';    /* FIXME: this should be a solid block */
}

static void clear_buffer(subtitle *buf)
{
	int i;
	buf->lines=0;
	for(i=0;i<SUB_MAX_TEXT;i++) if(buf->text[i]) {free(buf->text[i]);buf->text[i]=NULL;}
}


/**
 \brief scroll buffer one line up
 \param buf buffer to scroll
*/
static void scroll_buffer(subtitle* buf)
{
	int i;

	while(buf->lines > cc_lines)
	{
		if(buf->text[0]) free(buf->text[0]);

		for(i = 0; i < (buf->lines - 1); i++) buf->text[i] = buf->text[i+1];

		buf->text[buf->lines-1] = NULL;
		buf->lines--;
	}
}
 
 
void subcc_init(void)
{
	int i;
	//printf("subcc_init(): initing...\n");
	build_char_table();
	for(i=0;i<SUB_MAX_TEXT;i++) {buf1.text[i]=buf2.text[i]=NULL;}
	buf1.lines=buf2.lines=0;
	fb=&buf1;
	bb=&buf2;
	
	initialized=1;
}

static void append_char(char c)
{
	if(!bb->lines) {bb->lines++; cursor_pos=0;}
	if(bb->text[bb->lines - 1]==NULL) 
	{
		bb->text[bb->lines - 1]=malloc(CC_MAX_LINE_LENGTH);
		memset(bb->text[bb->lines - 1],0,CC_MAX_LINE_LENGTH);
		cursor_pos=0;
	}
	
	if(c=='\n')
	{
		if(cursor_pos>0 && bb->lines < SUB_MAX_TEXT)
		{
			bb->lines++;cursor_pos=0;
			if(cc_mode==CC_ROLLUP){ //Carriage return - scroll buffer one line up
				bb->text[bb->lines - 1]=calloc(1, CC_MAX_LINE_LENGTH);
				scroll_buffer(bb);
			}
		}
	}
	else 
	{
		if(cursor_pos==CC_MAX_LINE_LENGTH-1)
		{
			fprintf(stderr,"sub_cc.c: append_char() reached CC_MAX_LINE_LENGTH!\n");
			return;
		}
		bb->text[bb->lines - 1][cursor_pos++]=c;
	}
	//In CC roll-up mode data should be shown immediately
	if(cc_mode==CC_ROLLUP) display_buffer(bb);
}


static void swap_buffers(void)
{
	subtitle *foo;
	foo=fb;
	fb=bb;
	bb=foo;
}

static void display_buffer(subtitle * buf)
{
	vo_sub=buf;
	vo_osd_changed(OSDTYPE_SUBTITLE);
}


static void cc_decode_EIA608(unsigned short int data)
{
  
  static unsigned short int lastcode=0x0000;	
  unsigned char c1 = data & 0x7f;
  unsigned char c2 = (data >> 8) & 0x7f;

  if (c1 & 0x60) {		/* normal character, 0x20 <= c1 <= 0x7f */
	   append_char(chartbl[c1]);
	   if(c2 & 0x60)	/*c2 might not be a normal char even if c1 is*/
		   append_char(chartbl[c2]);
  }
  else if (c1 & 0x10)		// control code / special char
  {
//	  int channel= (c1 & 0x08) >> 3;
	  c1&=~0x08;
	  if(data!=lastcode)
	  {
	  	if(c2 & 0x40) {	/*PAC, Preamble Address Code */
			append_char('\n'); /*FIXME properly interpret PACs*/
		}
		else
			switch(c1)
			{
				case 0x10:	break; // ext attribute
				case 0x11: 
					if((c2 & 0x30)==0x30) 
					{
						//printf("[debug]:Special char (ignored)\n");
						/*cc_decode_special_char()*/;
					}
					else if (c2 & 0x20) 
					{
						//printf("[debug]: midrow_attr (ignored)\n");
						/*cc_decode_midrow_attr()*/;
					}
					break;
				case 0x14:
					switch(c2)
					{
						case 0x00: //CC roll-on mode
							   cc_mode=CC_ROLLON;
							   break;
						case 0x25: //CC roll-up, 2 rows
						case 0x26: //CC roll-up, 3 rows
						case 0x27: //CC roll-up, 4 rows
							   cc_lines=c2-0x23;
							   cc_mode=CC_ROLLUP;
							   break;
						case 0x2C: display_buffer(NULL); //EDM
							   clear_buffer(fb); break;
						case 0x2d: append_char('\n');	//carriage return
							   break;
						case 0x2e: clear_buffer(bb);	//ENM
							   break;
						case 0x2f: swap_buffers();	//Swap buffers
							   display_buffer(fb);
							   clear_buffer(bb);
							   break;
					}
					break;
				case 0x17:
					if( c2>=0x21 && c2<=0x23) //TAB
					{
						break;
					}
			}
	  }
  } 
  lastcode=data;  
}

static void subcc_decode(unsigned char *inputbuffer, unsigned int inputlength)
{
  /* The first number may denote a channel number. I don't have the
   * EIA-708 standard, so it is hard to say.
   * From what I could figure out so far, the general format seems to be:
   *
   * repeat
   *
   *   0xfe starts 2 byte sequence of unknown purpose. It might denote
   *        field #2 in line 21 of the VBI. We'll ignore it for the
   *        time being.
   *
   *   0xff starts 2 byte EIA-608 sequence, field #1 in line 21 of the VBI.
   *        Followed by a 3-code triplet that starts either with 0xff or
   *        0xfe. In either case, the following triplet needs to be ignored
   *        for line 21, field 1.
   *
   *   0x00 is padding, followed by 2 more 0x00.
   *
   *   0x01 always seems to appear at the beginning, always seems to
   *        be followed by 0xf8, 8-bit number. 
   *        The lower 7 bits of this 8-bit number seem to denote the
   *        number of code triplets that follow.
   *        The most significant bit denotes whether the Line 21 field 1 
   *        captioning information is at odd or even triplet offsets from this
   *        beginning triplet. 1 denotes odd offsets, 0 denotes even offsets.
   *      
   *        Most captions are encoded with odd offsets, so this is what we
   *        will assume.
   *
   * until end of packet
   */
  unsigned char *current = inputbuffer;
  unsigned int curbytes = 0;
  unsigned char data1, data2;
  unsigned char cc_code;
  int odd_offset = 1;

  while (curbytes < inputlength) {
    int skip = 2;

    cc_code = *(current);

    if (inputlength - curbytes < 2) {
#ifdef LOG_DEBUG
      fprintf(stderr, "Not enough data for 2-byte CC encoding\n");
#endif
      break;
    }
    
    data1 = *(current+1);
    data2 = *(current + 2);
    current++; curbytes++;
    
    switch (cc_code) {
    case 0xfe:
      /* expect 2 byte encoding (perhaps CC3, CC4?) */
      /* ignore for time being */
      skip = 2;
      break;
      
    case 0xff:
      /* expect EIA-608 CC1/CC2 encoding */
      // FIXME check parity!
      // Parity check omitted assuming we are reading from a DVD and therefore
      // we should encounter no "transmission errors".
      cc_decode_EIA608(data1 | (data2 << 8));
      skip = 5;
      break;
      
    case 0x00:
      /* This seems to be just padding */
      skip = 2;
      break;
      
    case 0x01:
      odd_offset = data2 & 0x80;
      if (odd_offset)
	skip = 2;
      else
	skip = 5;
      break;
      
    default:
//#ifdef LOG_DEBUG
      fprintf(stderr, "Unknown CC encoding: %x\n", cc_code);
//#endif
      skip = 2;
      break;
    }
    current += skip;
    curbytes += skip;
  }
}


void subcc_process_data(unsigned char *inputdata,unsigned int len)
{
	if(!subcc_enabled) return;
	if(!initialized) subcc_init();
	
	subcc_decode(inputdata, len);
}