Mercurial > libavformat.hg
annotate mpeg.c @ 2728:e00622d9d3c9 libavformat
Improve Sofdec file detection
author | mru |
---|---|
date | Thu, 08 Nov 2007 21:27:37 +0000 |
parents | a21454bf80b8 |
children | d52c718e83f9 |
rev | line source |
---|---|
0 | 1 /* |
2176
50322a49fa2b
split mpeg ps and variants muxer and demuxer, I'll clean more in a few minutes, lpcm freq tab is left static const in mpeg.h for now until we have more code in common
bcoudurier
parents:
2164
diff
changeset
|
2 * MPEG1/2 demuxer |
0 | 3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard. |
4 * | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
5 * This file is part of FFmpeg. |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
6 * |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
7 * FFmpeg is free software; you can redistribute it and/or |
0 | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
10 * version 2.1 of the License, or (at your option) any later version. |
0 | 11 * |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
12 * FFmpeg is distributed in the hope that it will be useful, |
0 | 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 | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1332
diff
changeset
|
18 * License along with FFmpeg; if not, write to the Free Software |
896
edbe5c3717f9
Update licensing information: The FSF changed postal address.
diego
parents:
885
diff
changeset
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
0 | 20 */ |
2176
50322a49fa2b
split mpeg ps and variants muxer and demuxer, I'll clean more in a few minutes, lpcm freq tab is left static const in mpeg.h for now until we have more code in common
bcoudurier
parents:
2164
diff
changeset
|
21 |
0 | 22 #include "avformat.h" |
2176
50322a49fa2b
split mpeg ps and variants muxer and demuxer, I'll clean more in a few minutes, lpcm freq tab is left static const in mpeg.h for now until we have more code in common
bcoudurier
parents:
2164
diff
changeset
|
23 #include "mpeg.h" |
0 | 24 |
310 | 25 //#define DEBUG_SEEK |
0 | 26 |
346
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
27 #undef NDEBUG |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
28 #include <assert.h> |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
29 |
0 | 30 /*********************************************/ |
31 /* demux code */ | |
32 | |
33 #define MAX_SYNC_SIZE 100000 | |
34 | |
1284 | 35 static int cdxa_probe(AVProbeData *p) |
36 { | |
37 /* check file header */ | |
38 if (p->buf[0] == 'R' && p->buf[1] == 'I' && | |
39 p->buf[2] == 'F' && p->buf[3] == 'F' && | |
40 p->buf[8] == 'C' && p->buf[9] == 'D' && | |
41 p->buf[10] == 'X' && p->buf[11] == 'A') | |
42 return AVPROBE_SCORE_MAX; | |
43 else | |
44 return 0; | |
45 } | |
46 | |
2697
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
47 static int check_pes(uint8_t *p, uint8_t *end){ |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
48 int pes1; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
49 int pes2= (p[3] & 0xC0) == 0x80 |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
50 && (p[4] & 0xC0) != 0x40 |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
51 &&((p[4] & 0xC0) == 0x00 || (p[4]&0xC0)>>2 == (p[6]&0xF0)); |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
52 |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
53 for(p+=3; p<end && *p == 0xFF; p++); |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
54 if((*p&0xC0) == 0x40) p+=2; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
55 if((*p&0xF0) == 0x20){ |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
56 pes1= p[0]&p[2]&p[4]&1; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
57 p+=5; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
58 }else if((*p&0xF0) == 0x30){ |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
59 pes1= p[0]&p[2]&p[4]&p[5]&p[7]&p[9]&1; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
60 p+=10; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
61 }else |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
62 pes1 = *p == 0x0F; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
63 |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
64 return pes1||pes2; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
65 } |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
66 |
0 | 67 static int mpegps_probe(AVProbeData *p) |
68 { | |
936 | 69 uint32_t code= -1; |
1138
36ce24677f96
detect MPEG PES streams as MPEG PS; the PS demuxer will cope
mru
parents:
1126
diff
changeset
|
70 int sys=0, pspack=0, priv1=0, vid=0, audio=0; |
539 | 71 int i; |
1257 | 72 int score=0; |
49 | 73 |
1284 | 74 score = cdxa_probe(p); |
75 if (score > 0) return score; | |
76 | |
77 /* Search for MPEG stream */ | |
936 | 78 for(i=0; i<p->buf_size; i++){ |
79 code = (code<<8) + p->buf[i]; | |
165
e4d2f704bf80
- Looks a tiny bit harder in mpegps_probe() for a valid start code. This is
michaelni
parents:
65
diff
changeset
|
80 if ((code & 0xffffff00) == 0x100) { |
2698 | 81 int pes= check_pes(p->buf+i, p->buf+p->buf_size); |
2697
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
82 |
1140
2cfb5e02b299
detect audio-only program streams and broken files made by VDR
mru
parents:
1138
diff
changeset
|
83 if(code == SYSTEM_HEADER_START_CODE) sys++; |
2cfb5e02b299
detect audio-only program streams and broken files made by VDR
mru
parents:
1138
diff
changeset
|
84 else if(code == PRIVATE_STREAM_1) priv1++; |
2cfb5e02b299
detect audio-only program streams and broken files made by VDR
mru
parents:
1138
diff
changeset
|
85 else if(code == PACK_START_CODE) pspack++; |
2697
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
86 else if((code & 0xf0) == VIDEO_ID && pes) vid++; |
afbdd52324e3
fix misdetection of mp3could_not_find_codec_parameters.mp3
michael
parents:
2695
diff
changeset
|
87 else if((code & 0xe0) == AUDIO_ID && pes) audio++; |
165
e4d2f704bf80
- Looks a tiny bit harder in mpegps_probe() for a valid start code. This is
michaelni
parents:
65
diff
changeset
|
88 } |
0 | 89 } |
1257 | 90 |
91 if(vid || audio) /* invalid VDR files nd short PES streams */ | |
92 score= AVPROBE_SCORE_MAX/4; | |
93 | |
2311 | 94 //av_log(NULL, AV_LOG_ERROR, "%d %d %d %d %d len:%d\n", sys, priv1, pspack,vid, audio, p->buf_size); |
936 | 95 if(sys && sys*9 <= pspack*10) |
96 return AVPROBE_SCORE_MAX/2+2; // +1 for .mpg | |
1140
2cfb5e02b299
detect audio-only program streams and broken files made by VDR
mru
parents:
1138
diff
changeset
|
97 if((priv1 || vid || audio) && (priv1+vid+audio)*9 <= pspack*10) |
940 | 98 return AVPROBE_SCORE_MAX/2+2; // +1 for .mpg |
2312 | 99 if((!!vid ^ !!audio) && (audio+vid > 1) && !sys && !pspack && p->buf_size>2048) /* PES stream */ |
1140
2cfb5e02b299
detect audio-only program streams and broken files made by VDR
mru
parents:
1138
diff
changeset
|
100 return AVPROBE_SCORE_MAX/2+2; |
1257 | 101 |
102 //02-Penguin.flac has sys:0 priv1:0 pspack:0 vid:0 audio:1 | |
103 return score; | |
0 | 104 } |
105 | |
106 | |
107 typedef struct MpegDemuxContext { | |
1332 | 108 int32_t header_state; |
722 | 109 unsigned char psm_es_type[256]; |
2718
1bbb4fa1cdcd
correctly set audio codec when demuxing sofdec files
aurel
parents:
2698
diff
changeset
|
110 int sofdec; |
0 | 111 } MpegDemuxContext; |
112 | |
310 | 113 static int mpegps_read_header(AVFormatContext *s, |
114 AVFormatParameters *ap) | |
115 { | |
116 MpegDemuxContext *m = s->priv_data; | |
2728 | 117 const char *sofdec = "Sofdec"; |
118 int v, i = 0; | |
2718
1bbb4fa1cdcd
correctly set audio codec when demuxing sofdec files
aurel
parents:
2698
diff
changeset
|
119 |
310 | 120 m->header_state = 0xff; |
121 s->ctx_flags |= AVFMTCTX_NOHEADER; | |
122 | |
2728 | 123 m->sofdec = -1; |
124 do { | |
125 v = get_byte(&s->pb); | |
126 m->header_state = m->header_state << 8 | v; | |
127 m->sofdec++; | |
128 } while (v == sofdec[i] && i++ < 6); | |
2718
1bbb4fa1cdcd
correctly set audio codec when demuxing sofdec files
aurel
parents:
2698
diff
changeset
|
129 |
310 | 130 /* no need to do more */ |
131 return 0; | |
132 } | |
133 | |
134 static int64_t get_pts(ByteIOContext *pb, int c) | |
135 { | |
136 int64_t pts; | |
137 int val; | |
138 | |
139 if (c < 0) | |
140 c = get_byte(pb); | |
141 pts = (int64_t)((c >> 1) & 0x07) << 30; | |
142 val = get_be16(pb); | |
143 pts |= (int64_t)(val >> 1) << 15; | |
144 val = get_be16(pb); | |
145 pts |= (int64_t)(val >> 1); | |
146 return pts; | |
147 } | |
148 | |
885 | 149 static int find_next_start_code(ByteIOContext *pb, int *size_ptr, |
1332 | 150 int32_t *header_state) |
0 | 151 { |
152 unsigned int state, v; | |
153 int val, n; | |
154 | |
155 state = *header_state; | |
156 n = *size_ptr; | |
157 while (n > 0) { | |
158 if (url_feof(pb)) | |
159 break; | |
160 v = get_byte(pb); | |
161 n--; | |
162 if (state == 0x000001) { | |
163 state = ((state << 8) | v) & 0xffffff; | |
164 val = state; | |
165 goto found; | |
166 } | |
167 state = ((state << 8) | v) & 0xffffff; | |
168 } | |
169 val = -1; | |
170 found: | |
171 *header_state = state; | |
172 *size_ptr = n; | |
173 return val; | |
174 } | |
175 | |
683
095009fc2f35
kill warnings patch by (M«©ns Rullg«©rd <mru inprovide com>)
michael
parents:
674
diff
changeset
|
176 #if 0 /* unused, remove? */ |
310 | 177 /* XXX: optimize */ |
178 static int find_prev_start_code(ByteIOContext *pb, int *size_ptr) | |
0 | 179 { |
310 | 180 int64_t pos, pos_start; |
181 int max_size, start_code; | |
182 | |
183 max_size = *size_ptr; | |
184 pos_start = url_ftell(pb); | |
185 | |
186 /* in order to go faster, we fill the buffer */ | |
187 pos = pos_start - 16386; | |
188 if (pos < 0) | |
189 pos = 0; | |
190 url_fseek(pb, pos, SEEK_SET); | |
191 get_byte(pb); | |
293
62cec412a186
make AVFMT_NOHEADER flag dynamic - added av_open_input_stream()
bellard
parents:
291
diff
changeset
|
192 |
310 | 193 pos = pos_start; |
194 for(;;) { | |
195 pos--; | |
196 if (pos < 0 || (pos_start - pos) >= max_size) { | |
197 start_code = -1; | |
198 goto the_end; | |
199 } | |
200 url_fseek(pb, pos, SEEK_SET); | |
201 start_code = get_be32(pb); | |
202 if ((start_code & 0xffffff00) == 0x100) | |
203 break; | |
204 } | |
205 the_end: | |
206 *size_ptr = pos_start - pos; | |
207 return start_code; | |
0 | 208 } |
683
095009fc2f35
kill warnings patch by (M«©ns Rullg«©rd <mru inprovide com>)
michael
parents:
674
diff
changeset
|
209 #endif |
0 | 210 |
722 | 211 /** |
212 * Extracts stream types from a program stream map | |
213 * According to ISO/IEC 13818-1 ('MPEG-2 Systems') table 2-35 | |
885 | 214 * |
722 | 215 * @return number of bytes occupied by PSM in the bitstream |
216 */ | |
217 static long mpegps_psm_parse(MpegDemuxContext *m, ByteIOContext *pb) | |
218 { | |
219 int psm_length, ps_info_length, es_map_length; | |
220 | |
221 psm_length = get_be16(pb); | |
222 get_byte(pb); | |
223 get_byte(pb); | |
224 ps_info_length = get_be16(pb); | |
225 | |
226 /* skip program_stream_info */ | |
227 url_fskip(pb, ps_info_length); | |
228 es_map_length = get_be16(pb); | |
229 | |
230 /* at least one es available? */ | |
231 while (es_map_length >= 4){ | |
232 unsigned char type = get_byte(pb); | |
233 unsigned char es_id = get_byte(pb); | |
234 uint16_t es_info_length = get_be16(pb); | |
235 /* remember mapping from stream id to stream type */ | |
236 m->psm_es_type[es_id] = type; | |
237 /* skip program_stream_info */ | |
238 url_fskip(pb, es_info_length); | |
239 es_map_length -= 4 + es_info_length; | |
240 } | |
241 get_be32(pb); /* crc32 */ | |
242 return 2 + psm_length; | |
243 } | |
244 | |
885 | 245 /* read the next PES header. Return its position in ppos |
310 | 246 (if not NULL), and its start code, pts and dts. |
247 */ | |
248 static int mpegps_read_pes_header(AVFormatContext *s, | |
885 | 249 int64_t *ppos, int *pstart_code, |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
250 int64_t *ppts, int64_t *pdts) |
0 | 251 { |
252 MpegDemuxContext *m = s->priv_data; | |
310 | 253 int len, size, startcode, c, flags, header_len; |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
254 int pes_ext, ext2_len, id_ext, skip; |
1664
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
255 int64_t pts, dts; |
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
256 int64_t last_sync= url_ftell(&s->pb); |
0 | 257 |
1664
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
258 error_redo: |
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
259 url_fseek(&s->pb, last_sync, SEEK_SET); |
0 | 260 redo: |
310 | 261 /* next start code (should be immediately after) */ |
262 m->header_state = 0xff; | |
263 size = MAX_SYNC_SIZE; | |
264 startcode = find_next_start_code(&s->pb, &size, &m->header_state); | |
1664
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
265 last_sync = url_ftell(&s->pb); |
1443
404048ea11bc
Replace most of the %lld and %llx by their (cleaner) PRI*64 counterparts.
diego
parents:
1415
diff
changeset
|
266 //printf("startcode=%x pos=0x%"PRIx64"\n", startcode, url_ftell(&s->pb)); |
0 | 267 if (startcode < 0) |
2274
b21c2af60bc9
Replace all occurrences of AVERROR_IO with AVERROR(EIO).
takis
parents:
2177
diff
changeset
|
268 return AVERROR(EIO); |
0 | 269 if (startcode == PACK_START_CODE) |
270 goto redo; | |
271 if (startcode == SYSTEM_HEADER_START_CODE) | |
272 goto redo; | |
2728 | 273 if (startcode == PADDING_STREAM) { |
274 url_fskip(&s->pb, get_be16(&s->pb)); | |
275 goto redo; | |
276 } | |
277 if (startcode == PRIVATE_STREAM_2) { | |
0 | 278 len = get_be16(&s->pb); |
2728 | 279 if (!m->sofdec) { |
280 while (len-- >= 6) { | |
281 if (get_byte(&s->pb) == 'S') { | |
282 uint8_t buf[5]; | |
283 get_buffer(&s->pb, buf, sizeof(buf)); | |
284 m->sofdec = !memcmp(buf, "ofdec", 5); | |
285 len -= sizeof(buf); | |
286 break; | |
287 } | |
288 } | |
289 m->sofdec -= !m->sofdec; | |
290 } | |
0 | 291 url_fskip(&s->pb, len); |
292 goto redo; | |
293 } | |
722 | 294 if (startcode == PROGRAM_STREAM_MAP) { |
295 mpegps_psm_parse(m, &s->pb); | |
296 goto redo; | |
297 } | |
885 | 298 |
0 | 299 /* find matching stream */ |
300 if (!((startcode >= 0x1c0 && startcode <= 0x1df) || | |
301 (startcode >= 0x1e0 && startcode <= 0x1ef) || | |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
302 (startcode == 0x1bd) || (startcode == 0x1fd))) |
0 | 303 goto redo; |
310 | 304 if (ppos) { |
305 *ppos = url_ftell(&s->pb) - 4; | |
306 } | |
0 | 307 len = get_be16(&s->pb); |
1666 | 308 pts = |
0 | 309 dts = AV_NOPTS_VALUE; |
310 /* stuffing */ | |
311 for(;;) { | |
310 | 312 if (len < 1) |
1664
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
313 goto error_redo; |
0 | 314 c = get_byte(&s->pb); |
315 len--; | |
316 /* XXX: for mpeg1, should test only bit 7 */ | |
885 | 317 if (c != 0xff) |
0 | 318 break; |
319 } | |
320 if ((c & 0xc0) == 0x40) { | |
321 /* buffer scale & size */ | |
322 get_byte(&s->pb); | |
323 c = get_byte(&s->pb); | |
324 len -= 2; | |
325 } | |
1667 | 326 if ((c & 0xe0) == 0x20) { |
310 | 327 dts = pts = get_pts(&s->pb, c); |
0 | 328 len -= 4; |
1667 | 329 if (c & 0x10){ |
330 dts = get_pts(&s->pb, -1); | |
331 len -= 5; | |
332 } | |
0 | 333 } else if ((c & 0xc0) == 0x80) { |
334 /* mpeg 2 PES */ | |
1126 | 335 #if 0 /* some streams have this field set for no apparent reason */ |
0 | 336 if ((c & 0x30) != 0) { |
310 | 337 /* Encrypted multiplex not handled */ |
338 goto redo; | |
0 | 339 } |
1126 | 340 #endif |
0 | 341 flags = get_byte(&s->pb); |
342 header_len = get_byte(&s->pb); | |
343 len -= 2; | |
344 if (header_len > len) | |
1664
5e7460a2f209
seperate redo (we dont handle this possibly valid packet) from
michael
parents:
1443
diff
changeset
|
345 goto error_redo; |
1668 | 346 len -= header_len; |
1667 | 347 if (flags & 0x80) { |
310 | 348 dts = pts = get_pts(&s->pb, -1); |
0 | 349 header_len -= 5; |
1667 | 350 if (flags & 0x40) { |
351 dts = get_pts(&s->pb, -1); | |
352 header_len -= 5; | |
353 } | |
0 | 354 } |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
355 if (flags & 0x01) { /* PES extension */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
356 pes_ext = get_byte(&s->pb); |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
357 header_len--; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
358 if (pes_ext & 0x40) { /* pack header - should be zero in PS */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
359 goto error_redo; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
360 } |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
361 /* Skip PES private data, program packet sequence counter and P-STD buffer */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
362 skip = (pes_ext >> 4) & 0xb; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
363 skip += skip & 0x9; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
364 url_fskip(&s->pb, skip); |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
365 header_len -= skip; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
366 |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
367 if (pes_ext & 0x01) { /* PES extension 2 */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
368 ext2_len = get_byte(&s->pb); |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
369 header_len--; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
370 if ((ext2_len & 0x7f) > 0) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
371 id_ext = get_byte(&s->pb); |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
372 if ((id_ext & 0x80) == 0) |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
373 startcode = ((startcode & 0xff) << 8) | id_ext; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
374 header_len--; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
375 } |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
376 } |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
377 } |
1668 | 378 if(header_len < 0) |
379 goto error_redo; | |
380 url_fskip(&s->pb, header_len); | |
0 | 381 } |
447
94aa265c18b9
Mpeg start codes patch by ("Dmitry Borisov" <jbors at mail dot ru>)
michael
parents:
437
diff
changeset
|
382 else if( c!= 0xf ) |
94aa265c18b9
Mpeg start codes patch by ("Dmitry Borisov" <jbors at mail dot ru>)
michael
parents:
437
diff
changeset
|
383 goto redo; |
94aa265c18b9
Mpeg start codes patch by ("Dmitry Borisov" <jbors at mail dot ru>)
michael
parents:
437
diff
changeset
|
384 |
722 | 385 if (startcode == PRIVATE_STREAM_1 && !m->psm_es_type[startcode & 0xff]) { |
0 | 386 startcode = get_byte(&s->pb); |
387 len--; | |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
388 if (startcode >= 0x80 && startcode <= 0xcf) { |
0 | 389 /* audio: skip header */ |
390 get_byte(&s->pb); | |
391 get_byte(&s->pb); | |
392 get_byte(&s->pb); | |
393 len -= 3; | |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
394 if (startcode >= 0xb0 && startcode <= 0xbf) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
395 /* MLP/TrueHD audio has a 4-byte header */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
396 get_byte(&s->pb); |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
397 len--; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
398 } |
0 | 399 } |
400 } | |
1665 | 401 if(len<0) |
402 goto error_redo; | |
346
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
403 if(dts != AV_NOPTS_VALUE && ppos){ |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
404 int i; |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
405 for(i=0; i<s->nb_streams; i++){ |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
406 if(startcode == s->streams[i]->id) { |
979 | 407 av_add_index_entry(s->streams[i], *ppos, dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); |
346
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
408 } |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
409 } |
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
410 } |
885 | 411 |
310 | 412 *pstart_code = startcode; |
413 *ppts = pts; | |
414 *pdts = dts; | |
415 return len; | |
416 } | |
417 | |
418 static int mpegps_read_packet(AVFormatContext *s, | |
419 AVPacket *pkt) | |
420 { | |
722 | 421 MpegDemuxContext *m = s->priv_data; |
310 | 422 AVStream *st; |
722 | 423 int len, startcode, i, type, codec_id = 0, es_type; |
346
e154eb1b7149
caching of timestamps for mpeg-ps so seeking is faster
michael
parents:
337
diff
changeset
|
424 int64_t pts, dts, dummy_pos; //dummy_pos is needed for the index building to work |
310 | 425 |
426 redo: | |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
427 len = mpegps_read_pes_header(s, &dummy_pos, &startcode, &pts, &dts); |
310 | 428 if (len < 0) |
429 return len; | |
885 | 430 |
0 | 431 /* now find stream */ |
432 for(i=0;i<s->nb_streams;i++) { | |
433 st = s->streams[i]; | |
434 if (st->id == startcode) | |
435 goto found; | |
436 } | |
722 | 437 |
438 es_type = m->psm_es_type[startcode & 0xff]; | |
439 if(es_type > 0){ | |
440 if(es_type == STREAM_TYPE_VIDEO_MPEG1){ | |
441 codec_id = CODEC_ID_MPEG2VIDEO; | |
442 type = CODEC_TYPE_VIDEO; | |
443 } else if(es_type == STREAM_TYPE_VIDEO_MPEG2){ | |
444 codec_id = CODEC_ID_MPEG2VIDEO; | |
445 type = CODEC_TYPE_VIDEO; | |
446 } else if(es_type == STREAM_TYPE_AUDIO_MPEG1 || | |
447 es_type == STREAM_TYPE_AUDIO_MPEG2){ | |
448 codec_id = CODEC_ID_MP3; | |
449 type = CODEC_TYPE_AUDIO; | |
450 } else if(es_type == STREAM_TYPE_AUDIO_AAC){ | |
451 codec_id = CODEC_ID_AAC; | |
452 type = CODEC_TYPE_AUDIO; | |
453 } else if(es_type == STREAM_TYPE_VIDEO_MPEG4){ | |
454 codec_id = CODEC_ID_MPEG4; | |
455 type = CODEC_TYPE_VIDEO; | |
456 } else if(es_type == STREAM_TYPE_VIDEO_H264){ | |
457 codec_id = CODEC_ID_H264; | |
458 type = CODEC_TYPE_VIDEO; | |
459 } else if(es_type == STREAM_TYPE_AUDIO_AC3){ | |
460 codec_id = CODEC_ID_AC3; | |
461 type = CODEC_TYPE_AUDIO; | |
462 } else { | |
463 goto skip; | |
464 } | |
465 } else if (startcode >= 0x1e0 && startcode <= 0x1ef) { | |
1146 | 466 static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 }; |
467 unsigned char buf[8]; | |
468 get_buffer(&s->pb, buf, 8); | |
469 url_fseek(&s->pb, -8, SEEK_CUR); | |
470 if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1)) | |
471 codec_id = CODEC_ID_CAVS; | |
472 else | |
473 codec_id = CODEC_ID_MPEG2VIDEO; | |
0 | 474 type = CODEC_TYPE_VIDEO; |
475 } else if (startcode >= 0x1c0 && startcode <= 0x1df) { | |
476 type = CODEC_TYPE_AUDIO; | |
2728 | 477 codec_id = m->sofdec > 0 ? CODEC_ID_ADPCM_ADX : CODEC_ID_MP2; |
767
cbfea73709bd
fix ac3 and dts detection (patch by Joakim Plate <joakim.plate at ecce.se>)
mru
parents:
722
diff
changeset
|
478 } else if (startcode >= 0x80 && startcode <= 0x87) { |
0 | 479 type = CODEC_TYPE_AUDIO; |
480 codec_id = CODEC_ID_AC3; | |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
481 } else if ((startcode >= 0x88 && startcode <= 0x8f) |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
482 ||( startcode >= 0x98 && startcode <= 0x9f)) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
483 /* 0x90 - 0x97 is reserved for SDDS in DVD specs */ |
496
112057e05179
libdts support by (Benjamin Zores <ben at geexbox dot org>)
michael
parents:
483
diff
changeset
|
484 type = CODEC_TYPE_AUDIO; |
112057e05179
libdts support by (Benjamin Zores <ben at geexbox dot org>)
michael
parents:
483
diff
changeset
|
485 codec_id = CODEC_ID_DTS; |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
486 } else if (startcode >= 0xa0 && startcode <= 0xaf) { |
41 | 487 type = CODEC_TYPE_AUDIO; |
488 codec_id = CODEC_ID_PCM_S16BE; | |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
489 } else if (startcode >= 0xb0 && startcode <= 0xbf) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
490 type = CODEC_TYPE_AUDIO; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
491 codec_id = CODEC_ID_MLP; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
492 } else if (startcode >= 0xc0 && startcode <= 0xcf) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
493 /* Used for both AC-3 and E-AC-3 in EVOB files */ |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
494 type = CODEC_TYPE_AUDIO; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
495 codec_id = CODEC_ID_AC3; |
783
2e8b5a7d7e02
DVD subtitle parsing - show mpeg component IDs by default
bellard
parents:
767
diff
changeset
|
496 } else if (startcode >= 0x20 && startcode <= 0x3f) { |
2e8b5a7d7e02
DVD subtitle parsing - show mpeg component IDs by default
bellard
parents:
767
diff
changeset
|
497 type = CODEC_TYPE_SUBTITLE; |
2e8b5a7d7e02
DVD subtitle parsing - show mpeg component IDs by default
bellard
parents:
767
diff
changeset
|
498 codec_id = CODEC_ID_DVD_SUBTITLE; |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
499 } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
500 type = CODEC_TYPE_VIDEO; |
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
501 codec_id = CODEC_ID_VC1; |
0 | 502 } else { |
503 skip: | |
504 /* skip packet */ | |
505 url_fskip(&s->pb, len); | |
506 goto redo; | |
507 } | |
508 /* no stream found: add a new stream */ | |
509 st = av_new_stream(s, startcode); | |
885 | 510 if (!st) |
0 | 511 goto skip; |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
789
diff
changeset
|
512 st->codec->codec_type = type; |
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
789
diff
changeset
|
513 st->codec->codec_id = codec_id; |
310 | 514 if (codec_id != CODEC_ID_PCM_S16BE) |
2023 | 515 st->need_parsing = AVSTREAM_PARSE_FULL; |
0 | 516 found: |
708 | 517 if(st->discard >= AVDISCARD_ALL) |
652 | 518 goto skip; |
1759
9eaf71a70ac1
Revised patch for HD DVD .EVO demuxing by (Ian Caulfield <lowercase name seperated by . place at here gmail place a dot here com>)
michael
parents:
1668
diff
changeset
|
519 if (startcode >= 0xa0 && startcode <= 0xaf) { |
41 | 520 int b1, freq; |
521 | |
522 /* for LPCM, we just skip the header and consider it is raw | |
523 audio data */ | |
524 if (len <= 3) | |
525 goto skip; | |
526 get_byte(&s->pb); /* emphasis (1), muse(1), reserved(1), frame number(5) */ | |
527 b1 = get_byte(&s->pb); /* quant (2), freq(2), reserved(1), channels(3) */ | |
528 get_byte(&s->pb); /* dynamic range control (0x80 = off) */ | |
529 len -= 3; | |
530 freq = (b1 >> 4) & 3; | |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
789
diff
changeset
|
531 st->codec->sample_rate = lpcm_freq_tab[freq]; |
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
789
diff
changeset
|
532 st->codec->channels = 1 + (b1 & 7); |
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
789
diff
changeset
|
533 st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * 2; |
41 | 534 } |
0 | 535 av_new_packet(pkt, len); |
536 get_buffer(&s->pb, pkt->data, pkt->size); | |
537 pkt->pts = pts; | |
310 | 538 pkt->dts = dts; |
0 | 539 pkt->stream_index = st->index; |
331
4530681af424
suppress PTS in packets when not needed (slightly smaller files), fixed PTS generation in some cases, added provision for DTS generation, slightly better SCR generation (initial patch by Michel Bardiaux)
bellard
parents:
310
diff
changeset
|
540 #if 0 |
652 | 541 av_log(s, AV_LOG_DEBUG, "%d: pts=%0.3f dts=%0.3f size=%d\n", |
542 pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0, pkt->size); | |
331
4530681af424
suppress PTS in packets when not needed (slightly smaller files), fixed PTS generation in some cases, added provision for DTS generation, slightly better SCR generation (initial patch by Michel Bardiaux)
bellard
parents:
310
diff
changeset
|
543 #endif |
482 | 544 |
0 | 545 return 0; |
546 } | |
547 | |
548 static int mpegps_read_close(AVFormatContext *s) | |
549 { | |
550 return 0; | |
551 } | |
552 | |
885 | 553 static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
554 int64_t *ppos, int64_t pos_limit) |
310 | 555 { |
556 int len, startcode; | |
557 int64_t pos, pts, dts; | |
558 | |
559 pos = *ppos; | |
560 #ifdef DEBUG_SEEK | |
1443
404048ea11bc
Replace most of the %lld and %llx by their (cleaner) PRI*64 counterparts.
diego
parents:
1415
diff
changeset
|
561 printf("read_dts: pos=0x%"PRIx64" next=%d -> ", pos, find_next); |
310 | 562 #endif |
563 url_fseek(&s->pb, pos, SEEK_SET); | |
564 for(;;) { | |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
565 len = mpegps_read_pes_header(s, &pos, &startcode, &pts, &dts); |
310 | 566 if (len < 0) { |
567 #ifdef DEBUG_SEEK | |
568 printf("none (ret=%d)\n", len); | |
569 #endif | |
570 return AV_NOPTS_VALUE; | |
571 } | |
885 | 572 if (startcode == s->streams[stream_index]->id && |
310 | 573 dts != AV_NOPTS_VALUE) { |
574 break; | |
575 } | |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
576 url_fskip(&s->pb, len); |
310 | 577 } |
578 #ifdef DEBUG_SEEK | |
1443
404048ea11bc
Replace most of the %lld and %llx by their (cleaner) PRI*64 counterparts.
diego
parents:
1415
diff
changeset
|
579 printf("pos=0x%"PRIx64" dts=0x%"PRIx64" %0.3f\n", pos, dts, dts / 90000.0); |
310 | 580 #endif |
581 *ppos = pos; | |
463
696f41bc8784
store index for seeking in the native timebase of each stream
michael
parents:
452
diff
changeset
|
582 return dts; |
310 | 583 } |
584 | |
1167 | 585 AVInputFormat mpegps_demuxer = { |
0 | 586 "mpeg", |
587 "MPEG PS format", | |
588 sizeof(MpegDemuxContext), | |
589 mpegps_probe, | |
590 mpegps_read_header, | |
591 mpegps_read_packet, | |
592 mpegps_read_close, | |
437
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
593 NULL, //mpegps_read_seek, |
50bae308f71e
moving nearly identical binary search code from nut/mpeg/asf to utils.c
michael
parents:
396
diff
changeset
|
594 mpegps_read_dts, |
783
2e8b5a7d7e02
DVD subtitle parsing - show mpeg component IDs by default
bellard
parents:
767
diff
changeset
|
595 .flags = AVFMT_SHOW_IDS, |
0 | 596 }; |