/[tivodecode]/tivodecode/trunk/tivodecoder.c
ViewVC logotype

Contents of /tivodecode/trunk/tivodecoder.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 66 - (show annotations) (download)
Wed Dec 13 00:43:25 2006 UTC (15 years, 6 months ago) by jeremyd2019
File MIME type: text/plain
File size: 11848 byte(s)
make process_frame use a write function.  add target to make libtivodecode.a

1 /*
2 * tivodecode, (c) 2006, Jeremy Drake
3 * See COPYING file for license terms
4 *
5 * derived from mpegcat, copyright 2006 Kees Cook, used with permission
6 */
7 #include <stdio.h>
8 #include <memory.h>
9 #include "happyfile.h"
10 #include "turing_stream.h"
11 #include "tivodecoder.h"
12
13 /* TODO: clean this up */
14 extern int o_verbose;
15 extern int o_no_verify;
16
17 typedef enum
18 {
19 PACK_NONE,
20 PACK_SPECIAL,
21 PACK_PES_SIMPLE, // packet length == data length
22 PACK_PES_COMPLEX, // crazy headers need skipping
23 }
24 packet_type;
25
26 typedef struct
27 {
28 // the byte value match for the packet tags
29 unsigned char code_match_lo; // low end of the range of matches
30 unsigned char code_match_hi; // high end of the range of matches
31
32 // what kind of PES is it?
33 packet_type packet;
34 }
35 packet_tag_info;
36
37 static packet_tag_info packet_tags[] = {
38 {0x00, 0x00, PACK_SPECIAL}, // pic start
39 {0x01, 0xAF, PACK_SPECIAL}, // video slices
40 {0xB0, 0xB1, PACK_SPECIAL}, // reserved
41 {0xB2, 0xB5, PACK_SPECIAL}, // user data, sequences
42 {0xB6, 0xB6, PACK_SPECIAL}, // reserved
43 {0xB7, 0xB9, PACK_SPECIAL}, // sequence, gop, end
44 {0xBA, 0xBA, PACK_SPECIAL}, // pack
45 {0xBB, 0xBB, PACK_PES_SIMPLE}, // system: same len as PES
46 {0xBC, 0xBC, PACK_PES_SIMPLE}, // PES: prog stream map *
47 {0xBD, 0xBD, PACK_PES_COMPLEX}, // PES: priv 1
48 {0xBE, 0xBF, PACK_PES_SIMPLE}, // PES: padding, priv 2 *
49 {0xC0, 0xDF, PACK_PES_COMPLEX}, // PES: Audio
50 {0xE0, 0xEF, PACK_PES_COMPLEX}, // PES: Video
51 {0xF0, 0xF2, PACK_PES_SIMPLE}, // PES: ecm, emm, dsmcc *
52 {0xF3, 0xF7, PACK_PES_COMPLEX}, // PES: iso 13522/h2221a-d
53 {0xF8, 0xF8, PACK_PES_SIMPLE}, // PES: h2221e *
54 {0xF9, 0xF9, PACK_PES_COMPLEX}, // PES: ancillary
55 {0xFA, 0xFE, PACK_PES_SIMPLE}, // PES: reserved
56 {0xFF, 0xFF, PACK_PES_SIMPLE}, // PES: prog stream dir *
57 {0, 0, PACK_NONE} // end of list
58 };
59
60 /**
61 * This is from analyzing the TiVo directshow dll. Most of the parameters I have no idea what they are for.
62 *
63 * @param arg_0 pointer to the 16 byte private data section of the packet header.
64 * @param block_no pointer to an integer to contain the block number used in the turing key
65 * @param arg_8 no clue
66 * @param crypted pointer to an integer to contain 4 bytes of data encrypted
67 * with the same turing cipher as the video. No idea what to do with it once
68 * it is decrypted, tho, but the turing needs to have 4 bytes
69 * consumed in order to line up with the video/audio data. My
70 * guess is it is a checksum of some sort.
71 * @param arg_10 no clue
72 * @param arg_14 no clue
73 *
74 * @return count of particular bits which are zero. They should all be 1, so the return value should be zero.
75 * I would consider a non-zero return an error.
76 */
77 static int do_header(BYTE * arg_0, int * block_no, int * arg_8, int * crypted, int * arg_10, int * arg_14)
78 {
79 int var_4 = 0;
80
81 if (!(arg_0[0] & 0x80))
82 var_4++;
83
84 if (arg_10)
85 {
86 *arg_10 = (arg_0[0x0] & 0x78) >> 3;
87 }
88
89 if (arg_14)
90 {
91 *arg_14 = (arg_0[0x0] & 0x07) << 1;
92 *arg_14 |= (arg_0[0x1] & 0x80) >> 7;
93 }
94
95 if (!(arg_0[1] & 0x40))
96 var_4++;
97
98 if (block_no)
99 {
100 *block_no = (arg_0[0x1] & 0x3f) << 0x12;
101 *block_no |= (arg_0[0x2] & 0xff) << 0xa;
102 *block_no |= (arg_0[0x3] & 0xc0) << 0x2;
103
104 if (!(arg_0[3] & 0x20))
105 var_4++;
106
107 *block_no |= (arg_0[0x3] & 0x1f) << 0x3;
108 *block_no |= (arg_0[0x4] & 0xe0) >> 0x5;
109 }
110
111 if (!(arg_0[4] & 0x10))
112 var_4++;
113
114 if (arg_8)
115 {
116 *arg_8 = (arg_0[0x4] & 0x0f) << 0x14;
117 *arg_8 |= (arg_0[0x5] & 0xff) << 0xc;
118 *arg_8 |= (arg_0[0x6] & 0xf0) << 0x4;
119
120 if (!(arg_0[6] & 0x8))
121 var_4++;
122
123 *arg_8 |= (arg_0[0x6] & 0x07) << 0x5;
124 *arg_8 |= (arg_0[0x7] & 0xf8) >> 0x3;
125 }
126
127 if (crypted)
128 {
129 *crypted = (arg_0[0xb] & 0x03) << 0x1e;
130 *crypted |= (arg_0[0xc] & 0xff) << 0x16;
131 *crypted |= (arg_0[0xd] & 0xfc) << 0xe;
132
133 if (!(arg_0[0xd] & 0x2))
134 var_4++;
135
136 *crypted |= (arg_0[0xd] & 0x01) << 0xf;
137 *crypted |= (arg_0[0xe] & 0xff) << 0x7;
138 *crypted |= (arg_0[0xf] & 0xfe) >> 0x1;
139 }
140
141 if (!(arg_0[0xf] & 0x1))
142 var_4++;
143
144 return var_4;
145 }
146
147 #define LOOK_AHEAD(fh, bytes, n) do {\
148 if (read_handler((bytes) + looked_ahead, (n) - looked_ahead, fh) != (n) - looked_ahead) { \
149 perror ("read"); \
150 return -1; \
151 } else { \
152 looked_ahead = (n); \
153 } \
154 } while (0)
155
156 /*
157 * called for each frame
158 */
159 int process_frame(unsigned char code, turing_state * turing, off_t packet_start, void * packet_stream, read_func_t read_handler, void * ofh, write_func_t write_handler)
160 {
161 static unsigned char packet_buffer[65536 + 3];
162 unsigned char bytes[32];
163 int looked_ahead = 0;
164 int i;
165 int scramble=0;
166 unsigned int header_len = 0;
167 unsigned int length;
168
169 for (i = 0; packet_tags[i].packet != PACK_NONE; i++)
170 {
171 if (code >= packet_tags[i].code_match_lo &&
172 code <= packet_tags[i].code_match_hi)
173 {
174 if (packet_tags[i].packet == PACK_PES_SIMPLE
175 || packet_tags[i].packet == PACK_PES_COMPLEX)
176 {
177 if (packet_tags[i].packet == PACK_PES_COMPLEX)
178 {
179 LOOK_AHEAD (packet_stream, bytes, 5);
180
181 // packet_length is 0 and 1
182 // PES header variables
183 // | 2 | 3 | 4 |
184 // 76 54 3 2 1 0 76 5 4 3 2 1 0 76543210
185 // 10 scramble pts/dts pes_crc
186 // priority escr extension
187 // alignment es_rate header_data_length
188 // copyright dsm_trick
189 // copy addtl copy
190
191 if ((bytes[2]>>6) != 0x2) {
192 fprintf(stderr, "PES (0x%02X) header mark != 0x2: 0x%x (is this an MPEG2-PS file?)\n",code,(bytes[2]>>6));
193 }
194
195 scramble=((bytes[2]>>4)&0x3);
196
197 header_len = 5 + bytes[4];
198
199 if (scramble == 3)
200 {
201 if (bytes[3] & 0x1)
202 {
203 int off = 6;
204 int ext_byte = 5;
205 int goagain = 0;
206 // extension
207 if (header_len > 32)
208 return -1;
209
210 LOOK_AHEAD (packet_stream, bytes, header_len);
211
212 do
213 {
214 goagain = 0;
215
216 //packet seq counter flag
217 if (bytes[ext_byte] & 0x20)
218 {
219 off += 4;
220 }
221
222
223 //private data flag
224 if (bytes[ext_byte] & 0x80)
225 {
226 int block_no, crypted;
227
228 if (do_header (&bytes[off], &block_no, NULL, &crypted, NULL, NULL))
229 {
230 fprintf(stderr, "do_header not returned 0!\n");
231 }
232
233 if (o_verbose)
234 fprintf(stderr, "%10" OFF_T_FORMAT ": stream_no: %x, block_no: %d\n", packet_start, code, block_no);
235
236 prepare_frame(turing, code, block_no);
237 decrypt_buffer(turing, (unsigned char *)&crypted, 4);
238 }
239
240 // STD buffer flag
241 if (bytes[ext_byte] & 0x10)
242 {
243 off += 2;
244 }
245
246 // extension flag 2
247 if (bytes[ext_byte] & 0x1)
248 {
249 ext_byte = off;
250 off++;
251 goagain = 1;
252 continue;
253 }
254 } while (goagain);
255 }
256 }
257 }
258 else
259 {
260 LOOK_AHEAD (packet_stream, bytes, 2);
261 }
262
263 length = bytes[1] | (bytes[0] << 8);
264
265 memcpy (packet_buffer + 1, bytes, looked_ahead);
266
267 LOOK_AHEAD (packet_stream, packet_buffer + 1, length + 2);
268 {
269 unsigned char * packet_ptr = packet_buffer + 1;
270 size_t packet_size;
271
272 packet_buffer[0] = code;
273
274 if (header_len)
275 {
276 packet_ptr += header_len;
277 packet_size = length - header_len + 2;
278 }
279 else
280 {
281 packet_ptr += 2;
282 packet_size = length;
283 }
284
285 if (scramble == 3)
286 {
287 decrypt_buffer (turing, packet_ptr, packet_size);
288 // turn off scramble bits
289 packet_buffer[1+2] &= ~0x30;
290
291 // scan video buffer for Slices. If no slices are
292 // found, the MAK is wrong.
293 if (!o_no_verify && code == 0xe0) {
294 int slice_count=0;
295 size_t offset;
296
297 for (offset=0;offset+4<packet_size;offset++)
298 {
299 if (packet_buffer[offset] == 0x00 &&
300 packet_buffer[offset+1] == 0x00 &&
301 packet_buffer[offset+2] == 0x01 &&
302 packet_buffer[offset+3] >= 0x01 &&
303 packet_buffer[offset+3] <= 0xAF)
304 {
305 slice_count++;
306 }
307 // choose 8 as a good test that if 8 slices
308 // are seen, it's probably not random noise
309 if (slice_count>8)
310 {
311 // disable future verification
312 o_no_verify = 1;
313 }
314 }
315 if (!o_no_verify)
316 {
317 fprintf(stderr, "Invalid MAK -- aborting\n");
318 exit(20);
319 }
320 }
321 }
322 else if (code == 0xbc)
323 {
324 // don't know why, but tivo dll does this.
325 // I can find no good docs on the format of the program_stream_map
326 // but I think this clears a reserved bit. No idea why
327 packet_buffer[1+2] &= ~0x20;
328 }
329
330 if (write_handler(packet_buffer, length + 3, ofh) != length + 3)
331 {
332 perror ("writing buffer");
333 }
334
335 return 1;
336 }
337 }
338
339 return 0;
340 }
341 }
342
343 return -1;
344 }
345

cvs@jdrake.com
ViewVC Help
Powered by ViewVC 1.1.13