diff options
author | Solomon Peachy <pizza@shaftnet.org> | 2021-08-04 09:49:56 -0400 |
---|---|---|
committer | Solomon Peachy <pizza@shaftnet.org> | 2022-10-02 11:57:20 -0400 |
commit | d25d24812e8120c0eb133a412287ac030eb185c9 (patch) | |
tree | d1c336d5d81c39366672c86f30c484f96e24a469 /apps/plugins/mpegplayer/mpeg_parser.c | |
parent | 9ce5b2a2eda5c7101898476adaa3533e0ba5db82 (diff) | |
download | rockbox-d25d24812e8120c0eb133a412287ac030eb185c9.tar.gz rockbox-d25d24812e8120c0eb133a412287ac030eb185c9.zip |
RFC: Get rid of mpegplayer plugin
It might have made sense once upon a time, but in today's world...
Change-Id: I5d638e6f7a2308c50ab12bd901338f02cf426aae
Diffstat (limited to 'apps/plugins/mpegplayer/mpeg_parser.c')
-rw-r--r-- | apps/plugins/mpegplayer/mpeg_parser.c | 1203 |
1 files changed, 0 insertions, 1203 deletions
diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c deleted file mode 100644 index cc57b0c43c..0000000000 --- a/apps/plugins/mpegplayer/mpeg_parser.c +++ /dev/null | |||
@@ -1,1203 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Parser for MPEG streams | ||
11 | * | ||
12 | * Copyright (c) 2007 Michael Sevakis | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version 2 | ||
17 | * of the License, or (at your option) any later version. | ||
18 | * | ||
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
20 | * KIND, either express or implied. | ||
21 | * | ||
22 | ****************************************************************************/ | ||
23 | #include "plugin.h" | ||
24 | #include "mpegplayer.h" | ||
25 | |||
26 | struct stream_parser str_parser SHAREDBSS_ATTR; | ||
27 | |||
28 | static void parser_init_state(void) | ||
29 | { | ||
30 | str_parser.last_seek_time = 0; | ||
31 | str_parser.format = STREAM_FMT_UNKNOWN; | ||
32 | str_parser.start_pts = INVALID_TIMESTAMP; | ||
33 | str_parser.end_pts = INVALID_TIMESTAMP; | ||
34 | str_parser.flags = 0; | ||
35 | str_parser.dims.w = 0; | ||
36 | str_parser.dims.h = 0; | ||
37 | } | ||
38 | |||
39 | /* Place the stream in a state to begin parsing - sync will be performed | ||
40 | * first */ | ||
41 | void str_initialize(struct stream *str, off_t pos) | ||
42 | { | ||
43 | /* Initial positions start here */ | ||
44 | str->hdr.win_left = str->hdr.win_right = pos; | ||
45 | /* No packet */ | ||
46 | str->curr_packet = NULL; | ||
47 | /* Pick up parsing from this point in the buffer */ | ||
48 | str->curr_packet_end = disk_buf_offset2ptr(pos); | ||
49 | /* No flags */ | ||
50 | str->pkt_flags = 0; | ||
51 | /* Sync first */ | ||
52 | str->state = SSTATE_SYNC; | ||
53 | } | ||
54 | |||
55 | /* Place the stream in an end of data state */ | ||
56 | void str_end_of_stream(struct stream *str) | ||
57 | { | ||
58 | /* Offsets that prevent this stream from being included in the | ||
59 | * min left/max right window so that no buffering is triggered on | ||
60 | * its behalf. Set right to the min first so a thread reading the | ||
61 | * overall window gets doesn't see this as valid no matter what the | ||
62 | * file length. */ | ||
63 | str->hdr.win_right = OFF_T_MIN; | ||
64 | str->hdr.win_left = OFF_T_MAX; | ||
65 | /* No packets */ | ||
66 | str->curr_packet = str->curr_packet_end = NULL; | ||
67 | /* No flags */ | ||
68 | str->pkt_flags = 0; | ||
69 | /* Fin */ | ||
70 | str->state = SSTATE_END; | ||
71 | } | ||
72 | |||
73 | /* Return a timestamp at address p+offset if the marker bits are in tact */ | ||
74 | static inline uint32_t read_pts(uint8_t *p, off_t offset) | ||
75 | { | ||
76 | return TS_CHECK_MARKERS(p, offset) ? | ||
77 | TS_FROM_HEADER(p, offset) : INVALID_TIMESTAMP; | ||
78 | } | ||
79 | |||
80 | static inline bool validate_timestamp(uint32_t ts) | ||
81 | { | ||
82 | return ts >= str_parser.start_pts && ts <= str_parser.end_pts; | ||
83 | } | ||
84 | |||
85 | /* Find a start code before or after a given position */ | ||
86 | uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code) | ||
87 | { | ||
88 | stream_scan_normalize(sk); | ||
89 | |||
90 | if (sk->dir < 0) | ||
91 | { | ||
92 | /* Reverse scan - start with at least the min needed */ | ||
93 | stream_scan_offset(sk, 4); | ||
94 | } | ||
95 | |||
96 | code &= 0xff; /* Only the low byte matters */ | ||
97 | |||
98 | while (sk->len >= 0 && sk->margin >= 4) | ||
99 | { | ||
100 | uint8_t *p; | ||
101 | off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); | ||
102 | ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p); | ||
103 | |||
104 | if (pos < 0 || len < 4) | ||
105 | break; | ||
106 | |||
107 | if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == code) | ||
108 | { | ||
109 | return p; | ||
110 | } | ||
111 | |||
112 | stream_scan_offset(sk, 1); | ||
113 | } | ||
114 | |||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | /* Find a PES packet header for any stream - return stream to which it | ||
119 | * belongs */ | ||
120 | unsigned mpeg_parser_scan_pes(struct stream_scan *sk) | ||
121 | { | ||
122 | stream_scan_normalize(sk); | ||
123 | |||
124 | if (sk->dir < 0) | ||
125 | { | ||
126 | /* Reverse scan - start with at least the min needed */ | ||
127 | stream_scan_offset(sk, 4); | ||
128 | } | ||
129 | |||
130 | while (sk->len >= 0 && sk->margin >= 4) | ||
131 | { | ||
132 | uint8_t *p; | ||
133 | off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); | ||
134 | ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, &p); | ||
135 | |||
136 | if (pos < 0 || len < 4) | ||
137 | break; | ||
138 | |||
139 | if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) | ||
140 | { | ||
141 | unsigned id = p[3]; | ||
142 | if (id >= 0xb9) | ||
143 | return id; /* PES header */ | ||
144 | /* else some video stream element */ | ||
145 | } | ||
146 | |||
147 | stream_scan_offset(sk, 1); | ||
148 | } | ||
149 | |||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | /* Return the first SCR found from the scan direction */ | ||
154 | uint32_t mpeg_parser_scan_scr(struct stream_scan *sk) | ||
155 | { | ||
156 | uint8_t *p = mpeg_parser_scan_start_code(sk, MPEG_STREAM_PACK_HEADER); | ||
157 | |||
158 | if (p != NULL && sk->margin >= 9) /* 9 bytes total required */ | ||
159 | { | ||
160 | sk->data = 9; | ||
161 | |||
162 | if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */ | ||
163 | { | ||
164 | /* Lookhead p+8 */ | ||
165 | if (MPEG2_CHECK_PACK_SCR_MARKERS(p, 4)) | ||
166 | return MPEG2_PACK_HEADER_SCR(p, 4); | ||
167 | } | ||
168 | else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */ | ||
169 | { | ||
170 | /* Lookahead p+8 */ | ||
171 | if (TS_CHECK_MARKERS(p, 4)) | ||
172 | return TS_FROM_HEADER(p, 4); | ||
173 | } | ||
174 | /* Weird pack header */ | ||
175 | sk->data = 5; | ||
176 | } | ||
177 | |||
178 | return INVALID_TIMESTAMP; | ||
179 | } | ||
180 | |||
181 | uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id) | ||
182 | { | ||
183 | stream_scan_normalize(sk); | ||
184 | |||
185 | if (sk->dir < 0) | ||
186 | { | ||
187 | /* Reverse scan - start with at least the min needed */ | ||
188 | stream_scan_offset(sk, 4); | ||
189 | } | ||
190 | |||
191 | while (sk->len >= 0 && sk->margin >= 4) | ||
192 | { | ||
193 | uint8_t *p; | ||
194 | off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); | ||
195 | ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, &p); | ||
196 | |||
197 | if (pos < 0 || len < 4) | ||
198 | break; | ||
199 | |||
200 | if (CMP_3_CONST(p, PACKET_START_CODE_PREFIX) && p[3] == id) | ||
201 | { | ||
202 | uint8_t *h = p; | ||
203 | |||
204 | if (sk->margin < 7) | ||
205 | { | ||
206 | /* Insufficient data */ | ||
207 | } | ||
208 | else if ((h[6] & 0xc0) == 0x80) /* mpeg2 */ | ||
209 | { | ||
210 | if (sk->margin >= 14 && (h[7] & 0x80) != 0x00) | ||
211 | { | ||
212 | sk->data = 14; | ||
213 | return read_pts(h, 9); | ||
214 | } | ||
215 | } | ||
216 | else /* mpeg1 */ | ||
217 | { | ||
218 | ssize_t l = 6; | ||
219 | ssize_t margin = sk->margin; | ||
220 | |||
221 | /* Skip stuffing_byte */ | ||
222 | while (margin > 7 && h[l] == 0xff && ++l <= 22) | ||
223 | --margin; | ||
224 | |||
225 | if (margin >= 7) | ||
226 | { | ||
227 | if ((h[l] & 0xc0) == 0x40) | ||
228 | { | ||
229 | /* Skip STD_buffer_scale and STD_buffer_size */ | ||
230 | margin -= 2; | ||
231 | l += 2; | ||
232 | } | ||
233 | |||
234 | if (margin >= 5) | ||
235 | { | ||
236 | /* Header points to the mpeg1 pes header */ | ||
237 | h += l; | ||
238 | |||
239 | if ((h[0] & 0xe0) == 0x20) | ||
240 | { | ||
241 | /* PTS or PTS_DTS indicated */ | ||
242 | sk->data = (h + 5) - p; | ||
243 | return read_pts(h, 0); | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | /* No PTS present - keep searching for a matching PES header with | ||
249 | * one */ | ||
250 | } | ||
251 | |||
252 | stream_scan_offset(sk, 1); | ||
253 | } | ||
254 | |||
255 | return INVALID_TIMESTAMP; | ||
256 | } | ||
257 | |||
258 | static bool init_video_info(void) | ||
259 | { | ||
260 | DEBUGF("Getting movie size\n"); | ||
261 | |||
262 | /* The decoder handles this in order to initialize its knowledge of the | ||
263 | * movie parameters making seeking easier */ | ||
264 | str_send_msg(&video_str, STREAM_RESET, 0); | ||
265 | if (str_send_msg(&video_str, VIDEO_GET_SIZE, | ||
266 | (intptr_t)&str_parser.dims) != 0) | ||
267 | { | ||
268 | return true; | ||
269 | } | ||
270 | |||
271 | DEBUGF(" failed\n"); | ||
272 | return false; | ||
273 | } | ||
274 | |||
275 | static bool init_times(struct stream *str) | ||
276 | { | ||
277 | struct stream tmp_str; | ||
278 | const ssize_t filesize = disk_buf_filesize(); | ||
279 | const ssize_t max_probe = MIN(512*1024, filesize); | ||
280 | bool found_stream; | ||
281 | |||
282 | /* Simply find the first earliest timestamp - this will be the one | ||
283 | * used when streaming anyway */ | ||
284 | DEBUGF("Finding start_pts: 0x%02x\n", str->id); | ||
285 | |||
286 | found_stream = false; | ||
287 | str->start_pts = INVALID_TIMESTAMP; | ||
288 | str->end_pts = INVALID_TIMESTAMP; | ||
289 | |||
290 | tmp_str.id = str->id; | ||
291 | tmp_str.hdr.pos = 0; | ||
292 | tmp_str.hdr.limit = max_probe; | ||
293 | |||
294 | /* Probe for many for the start because some stamps could be anomalous. | ||
295 | * Video also can also have things out of order. Just see what it's got. | ||
296 | */ | ||
297 | while (1) | ||
298 | { | ||
299 | switch (parser_get_next_data(&tmp_str, STREAM_PM_RANDOM_ACCESS)) | ||
300 | { | ||
301 | case STREAM_DATA_END: | ||
302 | break; | ||
303 | case STREAM_OK: | ||
304 | found_stream = true; | ||
305 | if (tmp_str.pkt_flags & PKT_HAS_TS) | ||
306 | { | ||
307 | if (tmp_str.pts < str->start_pts) | ||
308 | str->start_pts = tmp_str.pts; | ||
309 | } | ||
310 | continue; | ||
311 | } | ||
312 | |||
313 | break; | ||
314 | } | ||
315 | |||
316 | if (!found_stream) | ||
317 | { | ||
318 | DEBUGF(" stream not found:0x%02x\n", str->id); | ||
319 | return false; | ||
320 | } | ||
321 | |||
322 | DEBUGF(" start:%u\n", (unsigned)str->start_pts); | ||
323 | |||
324 | /* Use the decoder thread to perform a synchronized search - no | ||
325 | * decoding should take place but just a simple run through timestamps | ||
326 | * and durations as the decoder would see them. This should give the | ||
327 | * precise time at the end of the last frame for the stream. */ | ||
328 | DEBUGF("Finding end_pts: 0x%02x\n", str->id); | ||
329 | |||
330 | str_parser.parms.sd.time = MAX_TIMESTAMP; | ||
331 | str_parser.parms.sd.sk.pos = filesize - max_probe; | ||
332 | str_parser.parms.sd.sk.len = max_probe; | ||
333 | str_parser.parms.sd.sk.dir = SSCAN_FORWARD; | ||
334 | |||
335 | str_send_msg(str, STREAM_RESET, 0); | ||
336 | |||
337 | if (str_send_msg(str, STREAM_FIND_END_TIME, | ||
338 | (intptr_t)&str_parser.parms.sd) == STREAM_PERFECT_MATCH) | ||
339 | { | ||
340 | str->end_pts = str_parser.parms.sd.time; | ||
341 | DEBUGF(" end:%u\n", (unsigned)str->end_pts); | ||
342 | } | ||
343 | |||
344 | return true; | ||
345 | } | ||
346 | |||
347 | static bool check_times(const struct stream *str) | ||
348 | { | ||
349 | return str->start_pts < str->end_pts && | ||
350 | str->end_pts != INVALID_TIMESTAMP; | ||
351 | } | ||
352 | |||
353 | /* Return the best-fit file offset of a timestamp in the PES where | ||
354 | * timstamp <= time < next timestamp. Will try to return something reasonably | ||
355 | * valid if best-fit could not be made. */ | ||
356 | static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id) | ||
357 | { | ||
358 | ssize_t pos_left = 0; | ||
359 | ssize_t pos_right = disk_buf.filesize; | ||
360 | ssize_t pos, pos_new; | ||
361 | uint32_t time_left = str_parser.start_pts; | ||
362 | uint32_t time_right = str_parser.end_pts; | ||
363 | uint32_t pts = 0; | ||
364 | uint32_t prevpts = 0; | ||
365 | enum state_enum state = STATE0; | ||
366 | struct stream_scan sk; | ||
367 | |||
368 | stream_scan_init(&sk); | ||
369 | |||
370 | /* Initial estimate taken from average bitrate - later interpolations are | ||
371 | * taken similarly based on the remaining file interval */ | ||
372 | pos_new = muldiv_uint32(time - time_left, pos_right - pos_left, | ||
373 | time_right - time_left) + pos_left; | ||
374 | |||
375 | /* return this estimated position if nothing better comes up */ | ||
376 | pos = pos_new; | ||
377 | |||
378 | DEBUGF("Seeking stream 0x%02x\n", id); | ||
379 | DEBUGF("$$ tl:%u t:%u ct:?? tr:%u\n pl:%ld pn:%ld pr:%ld\n", | ||
380 | (unsigned)time_left, (unsigned)time, (unsigned)time_right, | ||
381 | (long)pos_left, (long)pos_new, (long)pos_right); | ||
382 | |||
383 | sk.dir = SSCAN_REVERSE; | ||
384 | |||
385 | while (state < STATE9) | ||
386 | { | ||
387 | uint32_t currpts; | ||
388 | sk.pos = pos_new; | ||
389 | sk.len = (sk.dir < 0) ? pos_new - pos_left : pos_right - pos_new; | ||
390 | |||
391 | currpts = mpeg_parser_scan_pts(&sk, id); | ||
392 | |||
393 | if (currpts != INVALID_TIMESTAMP) | ||
394 | { | ||
395 | ssize_t pos_adj; /* Adjustment to over or under-estimate */ | ||
396 | |||
397 | /* Found a valid timestamp - see were it lies in relation to | ||
398 | * target */ | ||
399 | if (currpts < time) | ||
400 | { | ||
401 | /* Time at current position is before seek time - move | ||
402 | * forward */ | ||
403 | if (currpts > pts) | ||
404 | { | ||
405 | /* This is less than the desired time but greater than | ||
406 | * the currently seeked one; move the position up */ | ||
407 | pts = currpts; | ||
408 | pos = sk.pos; | ||
409 | } | ||
410 | |||
411 | /* No next timestamp can be sooner */ | ||
412 | pos_left = sk.pos + sk.data; | ||
413 | time_left = currpts; | ||
414 | |||
415 | if (pos_right <= pos_left) | ||
416 | break; /* If the window disappeared - we're done */ | ||
417 | |||
418 | pos_new = muldiv_uint32(time - time_left, | ||
419 | pos_right - pos_left, | ||
420 | time_right - time_left); | ||
421 | /* Point is ahead of us - fudge estimate a bit high */ | ||
422 | pos_adj = pos_new / 10; | ||
423 | |||
424 | if (pos_adj > 512*1024) | ||
425 | pos_adj = 512*1024; | ||
426 | |||
427 | pos_new += pos_left + pos_adj; | ||
428 | |||
429 | if (pos_new >= pos_right) | ||
430 | { | ||
431 | /* Estimate could push too far */ | ||
432 | pos_new = pos_right; | ||
433 | } | ||
434 | |||
435 | state = STATE2; /* Last scan was early */ | ||
436 | sk.dir = SSCAN_REVERSE; | ||
437 | |||
438 | DEBUGF(">> tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", | ||
439 | (unsigned)time_left, (unsigned)time, (unsigned)currpts, | ||
440 | (unsigned)time_right, (long)pos_left, (long)pos_new, | ||
441 | (long)pos_right); | ||
442 | } | ||
443 | else if (currpts > time) | ||
444 | { | ||
445 | /* Time at current position is past seek time - move | ||
446 | backward */ | ||
447 | pos_right = sk.pos; | ||
448 | time_right = currpts; | ||
449 | |||
450 | if (pos_right <= pos_left) | ||
451 | break; /* If the window disappeared - we're done */ | ||
452 | |||
453 | pos_new = muldiv_uint32(time - time_left, | ||
454 | pos_right - pos_left, | ||
455 | time_right - time_left); | ||
456 | /* Overshot the seek point - fudge estimate a bit low */ | ||
457 | pos_adj = pos_new / 10; | ||
458 | |||
459 | if (pos_adj > 512*1024) | ||
460 | pos_adj = 512*1024; | ||
461 | |||
462 | pos_new += pos_left - pos_adj; | ||
463 | |||
464 | state = STATE3; /* Last scan was late */ | ||
465 | sk.dir = SSCAN_REVERSE; | ||
466 | |||
467 | DEBUGF("<< tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", | ||
468 | (unsigned)time_left, (unsigned)time, (unsigned)currpts, | ||
469 | (unsigned)time_right, (long)pos_left, (long)pos_new, | ||
470 | (long)pos_right); | ||
471 | } | ||
472 | else | ||
473 | { | ||
474 | /* Exact match - it happens */ | ||
475 | DEBUGF("|| tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", | ||
476 | (unsigned)time_left, (unsigned)time, (unsigned)currpts, | ||
477 | (unsigned)time_right, (long)pos_left, (long)pos_new, | ||
478 | (long)pos_right); | ||
479 | pts = currpts; | ||
480 | pos = sk.pos; | ||
481 | state = STATE9; | ||
482 | } | ||
483 | } | ||
484 | else | ||
485 | { | ||
486 | /* Nothing found */ | ||
487 | |||
488 | switch (state) | ||
489 | { | ||
490 | case STATE1: | ||
491 | /* We already tried the bruteforce scan and failed again - no | ||
492 | * more stamps could possibly exist in the interval */ | ||
493 | DEBUGF("!! no timestamp 2x\n"); | ||
494 | break; | ||
495 | case STATE0: | ||
496 | /* Hardly likely except at very beginning - just do L->R scan | ||
497 | * to find something */ | ||
498 | DEBUGF("!! no timestamp on first probe: %ld\n", sk.pos); | ||
499 | case STATE2: | ||
500 | case STATE3: | ||
501 | /* Could just be missing timestamps because the interval is | ||
502 | * narrowing down. A large block of data from another stream | ||
503 | * may also be in the midst of our chosen points which could | ||
504 | * cluster at either extreme end. If anything is there, this | ||
505 | * will find it. */ | ||
506 | pos_new = pos_left; | ||
507 | sk.dir = SSCAN_FORWARD; | ||
508 | DEBUGF("?? tl:%u t:%u ct:%u tr:%u\n pl:%ld pn:%ld pr:%ld\n", | ||
509 | (unsigned)time_left, (unsigned)time, (unsigned)currpts, | ||
510 | (unsigned)time_right, (long)pos_left, (long)pos_new, | ||
511 | (long)pos_right); | ||
512 | state = STATE1; | ||
513 | break; | ||
514 | default: | ||
515 | DEBUGF("?? Invalid state: %d\n", state); | ||
516 | } | ||
517 | } | ||
518 | |||
519 | /* Same timestamp twice = quit */ | ||
520 | if (currpts == prevpts) | ||
521 | { | ||
522 | DEBUGF("!! currpts == prevpts (stop)\n"); | ||
523 | state = STATE9; | ||
524 | } | ||
525 | |||
526 | prevpts = currpts; | ||
527 | } | ||
528 | |||
529 | #if defined(DEBUG) || defined(SIMULATOR) | ||
530 | /* The next pts after the seeked-to position should be greater - | ||
531 | * most of the time - frames out of presentation order may muck it | ||
532 | * up a slight bit */ | ||
533 | sk.pos = pos + 1; | ||
534 | sk.len = disk_buf.filesize; | ||
535 | sk.dir = SSCAN_FORWARD; | ||
536 | |||
537 | uint32_t nextpts = mpeg_parser_scan_pts(&sk, id); | ||
538 | DEBUGF("Seek pos:%ld pts:%u t:%u next pts:%u \n", | ||
539 | (long)pos, (unsigned)pts, (unsigned)time, (unsigned)nextpts); | ||
540 | |||
541 | if (pts <= time && time < nextpts) | ||
542 | { | ||
543 | /* Smile - it worked */ | ||
544 | DEBUGF(" :) pts<=time<next pts\n"); | ||
545 | } | ||
546 | else | ||
547 | { | ||
548 | /* See where things ended up */ | ||
549 | if (pts > time) | ||
550 | { | ||
551 | /* Hmm */ | ||
552 | DEBUGF(" :\\ pts>time\n"); | ||
553 | } | ||
554 | if (pts >= nextpts) | ||
555 | { | ||
556 | /* Weird - probably because of encoded order & tends to be right | ||
557 | * anyway if other criteria are met */ | ||
558 | DEBUGF(" :p pts>=next pts\n"); | ||
559 | } | ||
560 | if (time >= nextpts) | ||
561 | { | ||
562 | /* Ugh */ | ||
563 | DEBUGF(" :( time>=nextpts\n"); | ||
564 | } | ||
565 | } | ||
566 | #endif | ||
567 | |||
568 | return pos; | ||
569 | } | ||
570 | |||
571 | static void prepare_audio(uint32_t time) | ||
572 | { | ||
573 | off_t pos; | ||
574 | |||
575 | if (!str_send_msg(&audio_str, STREAM_NEEDS_SYNC, time)) | ||
576 | { | ||
577 | DEBUGF("Audio was ready\n"); | ||
578 | return; | ||
579 | } | ||
580 | |||
581 | pos = mpeg_parser_seek_PTS(time, audio_str.id); | ||
582 | str_send_msg(&audio_str, STREAM_RESET, 0); | ||
583 | |||
584 | str_parser.parms.sd.time = time; | ||
585 | str_parser.parms.sd.sk.pos = pos; | ||
586 | str_parser.parms.sd.sk.len = 1024*1024; | ||
587 | str_parser.parms.sd.sk.dir = SSCAN_FORWARD; | ||
588 | |||
589 | str_send_msg(&audio_str, STREAM_SYNC, (intptr_t)&str_parser.parms.sd); | ||
590 | } | ||
591 | |||
592 | /* This function demuxes the streams and gives the next stream data | ||
593 | * pointer. | ||
594 | * | ||
595 | * STREAM_PM_STREAMING is for operation during playback. If the nescessary | ||
596 | * data and worst-case lookahead margin is not available, the stream is | ||
597 | * registered for notification when the data becomes available. If parsing | ||
598 | * extends beyond the end of the file or the end of stream marker is reached, | ||
599 | * STREAM_DATA_END is returned and the stream state changed to SSTATE_EOS. | ||
600 | * | ||
601 | * STREAM_PM_RANDOM_ACCESS is for operation when not playing such as seeking. | ||
602 | * If the file cache misses for the current position + lookahead, it will be | ||
603 | * loaded from disk. When the specified limit is reached, STREAM_DATA_END is | ||
604 | * returned. | ||
605 | * | ||
606 | * The results from one mode may be used as input to the other. Random access | ||
607 | * requires cooperation amongst threads to avoid evicting another stream's | ||
608 | * data. | ||
609 | */ | ||
610 | static int parse_demux(struct stream *str, enum stream_parse_mode type) | ||
611 | { | ||
612 | #define INC_BUF(offset) \ | ||
613 | ({ off_t _o = (offset); \ | ||
614 | str->hdr.win_right += _o; \ | ||
615 | if ((p += _o) >= disk_buf.end) \ | ||
616 | p -= disk_buf.size; }) | ||
617 | |||
618 | static const int mpeg1_skip_table[16] = | ||
619 | { 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; | ||
620 | |||
621 | uint8_t *p = str->curr_packet_end; | ||
622 | |||
623 | str->pkt_flags = 0; | ||
624 | |||
625 | while (1) | ||
626 | { | ||
627 | uint8_t *header; | ||
628 | unsigned id; | ||
629 | ssize_t length, bytes; | ||
630 | |||
631 | switch (type) | ||
632 | { | ||
633 | case STREAM_PM_STREAMING: | ||
634 | /* Has the end been reached already? */ | ||
635 | switch (str->state) | ||
636 | { | ||
637 | case SSTATE_PARSE: /* Expected case first if no jumptable */ | ||
638 | /* Are we at the end of file? */ | ||
639 | if (str->hdr.win_left < disk_buf.filesize) | ||
640 | break; | ||
641 | str_end_of_stream(str); | ||
642 | return STREAM_DATA_END; | ||
643 | |||
644 | case SSTATE_SYNC: | ||
645 | /* Is sync at the end of file? */ | ||
646 | if (str->hdr.win_right < disk_buf.filesize) | ||
647 | break; | ||
648 | str_end_of_stream(str); | ||
649 | /* Fall-through */ | ||
650 | case SSTATE_END: | ||
651 | return STREAM_DATA_END; | ||
652 | } | ||
653 | |||
654 | if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD)) | ||
655 | { | ||
656 | /* This data range is not buffered yet - register stream to | ||
657 | * be notified when it becomes available. Stream is obliged | ||
658 | * to enter a TSTATE_DATA state if it must wait. */ | ||
659 | int res = str_next_data_not_ready(str); | ||
660 | |||
661 | if (res != STREAM_OK) | ||
662 | return res; | ||
663 | } | ||
664 | break; | ||
665 | /* STREAM_PM_STREAMING: */ | ||
666 | |||
667 | case STREAM_PM_RANDOM_ACCESS: | ||
668 | str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); | ||
669 | |||
670 | if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit || | ||
671 | disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0) | ||
672 | { | ||
673 | str_end_of_stream(str); | ||
674 | return STREAM_DATA_END; | ||
675 | } | ||
676 | |||
677 | str->state = SSTATE_SYNC; | ||
678 | str->hdr.win_left = str->hdr.pos; | ||
679 | str->curr_packet = NULL; | ||
680 | str->curr_packet_end = p; | ||
681 | break; | ||
682 | /* STREAM_PM_RANDOM_ACCESS: */ | ||
683 | } | ||
684 | |||
685 | if (str->state == SSTATE_SYNC) | ||
686 | { | ||
687 | /* Scanning for start code */ | ||
688 | if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) | ||
689 | { | ||
690 | INC_BUF(1); | ||
691 | continue; | ||
692 | } | ||
693 | } | ||
694 | |||
695 | /* Found a start code - enter parse state */ | ||
696 | str->state = SSTATE_PARSE; | ||
697 | |||
698 | /* Pack header, skip it */ | ||
699 | if (CMP_4_CONST(p, PACK_START_CODE)) | ||
700 | { | ||
701 | /* Max lookahead: 14 */ | ||
702 | if ((p[4] & 0xc0) == 0x40) /* mpeg-2 */ | ||
703 | { | ||
704 | /* Max delta: 14 + 7 = 21 */ | ||
705 | /* Skip pack header and any stuffing bytes*/ | ||
706 | bytes = 14 + (p[13] & 7); | ||
707 | } | ||
708 | else if ((p[4] & 0xf0) == 0x20) /* mpeg-1 */ | ||
709 | { | ||
710 | bytes = 12; | ||
711 | } | ||
712 | else /* unknown - skip it */ | ||
713 | { | ||
714 | DEBUGF("weird pack header!\n"); | ||
715 | bytes = 5; | ||
716 | } | ||
717 | |||
718 | INC_BUF(bytes); | ||
719 | } | ||
720 | |||
721 | /* System header, parse and skip it - 6 bytes + size */ | ||
722 | if (CMP_4_CONST(p, SYSTEM_HEADER_START_CODE)) | ||
723 | { | ||
724 | /* Skip start code */ | ||
725 | /* Max Delta = 65535 + 6 = 65541 */ | ||
726 | bytes = 6 + ((p[4] << 8) | p[5]); | ||
727 | INC_BUF(bytes); | ||
728 | } | ||
729 | |||
730 | /* Packet header, parse it */ | ||
731 | if (!CMP_3_CONST(p, PACKET_START_CODE_PREFIX)) | ||
732 | { | ||
733 | /* Problem? Meh...probably not but just a corrupted section. | ||
734 | * Try to resync the parser which will probably succeed. */ | ||
735 | DEBUGF("packet start code prefix not found: 0x%02x\n" | ||
736 | " wl:%lu wr:%lu\n" | ||
737 | " p:%p cp:%p cpe:%p\n" | ||
738 | " dbs:%p dbe:%p dbt:%p\n", | ||
739 | str->id, str->hdr.win_left, str->hdr.win_right, | ||
740 | p, str->curr_packet, str->curr_packet_end, | ||
741 | disk_buf.start, disk_buf.end, disk_buf.tail); | ||
742 | str->state = SSTATE_SYNC; | ||
743 | INC_BUF(1); /* Next byte - this one's no good */ | ||
744 | continue; | ||
745 | } | ||
746 | |||
747 | /* We retrieve basic infos */ | ||
748 | /* Maximum packet length: 6 + 65535 = 65541 */ | ||
749 | id = p[3]; | ||
750 | length = ((p[4] << 8) | p[5]) + 6; | ||
751 | |||
752 | if (id != str->id) | ||
753 | { | ||
754 | switch (id) | ||
755 | { | ||
756 | case MPEG_STREAM_PROGRAM_END: | ||
757 | /* end of stream */ | ||
758 | str_end_of_stream(str); | ||
759 | DEBUGF("MPEG program end: 0x%02x\n", str->id); | ||
760 | return STREAM_DATA_END; | ||
761 | case MPEG_STREAM_PACK_HEADER: | ||
762 | case MPEG_STREAM_SYSTEM_HEADER: | ||
763 | /* These shouldn't be here - no increment or resync | ||
764 | * since we'll pick it up above. */ | ||
765 | continue; | ||
766 | default: | ||
767 | /* It's not the packet we're looking for, skip it */ | ||
768 | INC_BUF(length); | ||
769 | continue; | ||
770 | } | ||
771 | } | ||
772 | |||
773 | /* Ok, it's our packet */ | ||
774 | header = p; | ||
775 | |||
776 | if ((header[6] & 0xc0) == 0x80) /* mpeg2 */ | ||
777 | { | ||
778 | /* Max Lookahead: 18 */ | ||
779 | /* Min length: 9 */ | ||
780 | /* Max length: 9 + 255 = 264 */ | ||
781 | length = 9 + header[8]; | ||
782 | |||
783 | /* header points to the mpeg2 pes header */ | ||
784 | if ((header[7] & 0x80) != 0) | ||
785 | { | ||
786 | /* header has a pts */ | ||
787 | uint32_t pts = read_pts(header, 9); | ||
788 | |||
789 | if (pts != INVALID_TIMESTAMP) | ||
790 | { | ||
791 | str->pts = pts; | ||
792 | #if 0 | ||
793 | /* DTS isn't used for anything since things just get | ||
794 | decoded ASAP but keep the code around */ | ||
795 | if (STREAM_IS_VIDEO(id)) | ||
796 | { | ||
797 | /* Video stream - header may have a dts as well */ | ||
798 | str->dts = pts; | ||
799 | |||
800 | if (header[7] & 0x40) != 0x00) | ||
801 | { | ||
802 | pts = read_pts(header, 14); | ||
803 | if (pts != INVALID_TIMESTAMP) | ||
804 | str->dts = pts; | ||
805 | } | ||
806 | } | ||
807 | #endif | ||
808 | str->pkt_flags |= PKT_HAS_TS; | ||
809 | } | ||
810 | } | ||
811 | } | ||
812 | else /* mpeg1 */ | ||
813 | { | ||
814 | /* Max lookahead: 24 + 2 + 9 = 35 */ | ||
815 | /* Max len_skip: 24 + 2 = 26 */ | ||
816 | /* Min length: 7 */ | ||
817 | /* Max length: 24 + 2 + 9 = 35 */ | ||
818 | off_t len_skip; | ||
819 | uint8_t * ptsbuf; | ||
820 | |||
821 | length = 7; | ||
822 | |||
823 | while (header[length - 1] == 0xff) | ||
824 | { | ||
825 | if (++length > 23) | ||
826 | { | ||
827 | DEBUGF("Too much stuffing" ); | ||
828 | break; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | if ((header[length - 1] & 0xc0) == 0x40) | ||
833 | length += 2; | ||
834 | |||
835 | len_skip = length; | ||
836 | length += mpeg1_skip_table[header[length - 1] >> 4]; | ||
837 | |||
838 | /* Header points to the mpeg1 pes header */ | ||
839 | ptsbuf = header + len_skip; | ||
840 | |||
841 | if ((ptsbuf[-1] & 0xe0) == 0x20 && TS_CHECK_MARKERS(ptsbuf, -1)) | ||
842 | { | ||
843 | /* header has a pts */ | ||
844 | uint32_t pts = read_pts(ptsbuf, -1); | ||
845 | |||
846 | if (pts != INVALID_TIMESTAMP) | ||
847 | { | ||
848 | str->pts = pts; | ||
849 | #if 0 | ||
850 | /* DTS isn't used for anything since things just get | ||
851 | decoded ASAP but keep the code around */ | ||
852 | if (STREAM_IS_VIDEO(id)) | ||
853 | { | ||
854 | /* Video stream - header may have a dts as well */ | ||
855 | str->dts = pts; | ||
856 | |||
857 | if (ptsbuf[-1] & 0xf0) == 0x30) | ||
858 | { | ||
859 | pts = read_pts(ptsbuf, 4); | ||
860 | |||
861 | if (pts != INVALID_TIMESTAMP) | ||
862 | str->dts = pts; | ||
863 | } | ||
864 | } | ||
865 | #endif | ||
866 | str->pkt_flags |= PKT_HAS_TS; | ||
867 | } | ||
868 | } | ||
869 | } | ||
870 | |||
871 | p += length; | ||
872 | /* Max bytes: 6 + 65535 - 7 = 65534 */ | ||
873 | bytes = 6 + (header[4] << 8) + header[5] - length; | ||
874 | |||
875 | str->curr_packet = p; | ||
876 | str->curr_packet_end = p + bytes; | ||
877 | str->hdr.win_left = str->hdr.win_right + length; | ||
878 | str->hdr.win_right = str->hdr.win_left + bytes; | ||
879 | |||
880 | if (str->hdr.win_right > disk_buf.filesize) | ||
881 | { | ||
882 | /* No packet that exceeds end of file can be valid */ | ||
883 | str_end_of_stream(str); | ||
884 | return STREAM_DATA_END; | ||
885 | } | ||
886 | |||
887 | return STREAM_OK; | ||
888 | } /* end while */ | ||
889 | |||
890 | #undef INC_BUF | ||
891 | } | ||
892 | |||
893 | /* This simply reads data from the file one page at a time and returns a | ||
894 | * pointer to it in the buffer. */ | ||
895 | static int parse_elementary(struct stream *str, enum stream_parse_mode type) | ||
896 | { | ||
897 | uint8_t *p; | ||
898 | ssize_t len = 0; | ||
899 | |||
900 | str->pkt_flags = 0; | ||
901 | |||
902 | switch (type) | ||
903 | { | ||
904 | case STREAM_PM_STREAMING: | ||
905 | /* Has the end been reached already? */ | ||
906 | if (str->state == SSTATE_END) | ||
907 | return STREAM_DATA_END; | ||
908 | |||
909 | /* Are we at the end of file? */ | ||
910 | if (str->hdr.win_left >= disk_buf.filesize) | ||
911 | { | ||
912 | str_end_of_stream(str); | ||
913 | return STREAM_DATA_END; | ||
914 | } | ||
915 | |||
916 | if (!disk_buf_is_data_ready(&str->hdr, MIN_BUFAHEAD)) | ||
917 | { | ||
918 | /* This data range is not buffered yet - register stream to | ||
919 | * be notified when it becomes available. Stream is obliged | ||
920 | * to enter a TSTATE_DATA state if it must wait. */ | ||
921 | int res = str_next_data_not_ready(str); | ||
922 | |||
923 | if (res != STREAM_OK) | ||
924 | return res; | ||
925 | } | ||
926 | |||
927 | len = DISK_BUF_PAGE_SIZE; | ||
928 | |||
929 | if ((size_t)(str->hdr.win_right + len) > (size_t)disk_buf.filesize) | ||
930 | len = disk_buf.filesize - str->hdr.win_right; | ||
931 | |||
932 | if (len <= 0) | ||
933 | { | ||
934 | str_end_of_stream(str); | ||
935 | return STREAM_DATA_END; | ||
936 | } | ||
937 | |||
938 | p = str->curr_packet_end; | ||
939 | if (p >= disk_buf.end) | ||
940 | p -= disk_buf.size; | ||
941 | break; | ||
942 | /* STREAM_PM_STREAMING: */ | ||
943 | |||
944 | case STREAM_PM_RANDOM_ACCESS: | ||
945 | str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); | ||
946 | len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL); | ||
947 | |||
948 | if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit) | ||
949 | { | ||
950 | str_end_of_stream(str); | ||
951 | return STREAM_DATA_END; | ||
952 | } | ||
953 | break; | ||
954 | /* STREAM_PM_RANDOM_ACCESS: */ | ||
955 | } | ||
956 | |||
957 | str->state = SSTATE_PARSE; | ||
958 | str->curr_packet = p; | ||
959 | str->curr_packet_end = p + len; | ||
960 | str->hdr.win_left = str->hdr.win_right; | ||
961 | str->hdr.win_right = str->hdr.win_left + len; | ||
962 | |||
963 | return STREAM_OK; | ||
964 | } | ||
965 | |||
966 | bool parser_prepare_image(uint32_t time) | ||
967 | { | ||
968 | struct stream_scan sk; | ||
969 | int tries; | ||
970 | int result; | ||
971 | |||
972 | stream_scan_init(&sk); | ||
973 | |||
974 | if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time)) | ||
975 | { | ||
976 | DEBUGF("Image was ready\n"); | ||
977 | return true; /* Should already have the image */ | ||
978 | } | ||
979 | |||
980 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
981 | rb->cpu_boost(true); /* No interference with trigger_cpu_boost */ | ||
982 | #endif | ||
983 | |||
984 | str_send_msg(&video_str, STREAM_RESET, 0); | ||
985 | |||
986 | sk.pos = parser_can_seek() ? | ||
987 | mpeg_parser_seek_PTS(time, video_str.id) : 0; | ||
988 | sk.len = sk.pos; | ||
989 | sk.dir = SSCAN_REVERSE; | ||
990 | |||
991 | tries = 1; | ||
992 | try_again: | ||
993 | |||
994 | if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP)) | ||
995 | { | ||
996 | DEBUGF("GOP found at: %ld\n", sk.pos); | ||
997 | |||
998 | unsigned id = mpeg_parser_scan_pes(&sk); | ||
999 | |||
1000 | if (id != video_str.id && sk.pos > 0) | ||
1001 | { | ||
1002 | /* Not part of our stream */ | ||
1003 | DEBUGF(" wrong stream: 0x%02x\n", id); | ||
1004 | goto try_again; | ||
1005 | } | ||
1006 | |||
1007 | /* This will hit the PES header since it's known to be there */ | ||
1008 | uint32_t pts = mpeg_parser_scan_pts(&sk, id); | ||
1009 | |||
1010 | if (pts == INVALID_TIMESTAMP || pts > time) | ||
1011 | { | ||
1012 | DEBUGF(" wrong timestamp: %u\n", (unsigned)pts); | ||
1013 | goto try_again; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | str_parser.parms.sd.time = time; | ||
1018 | str_parser.parms.sd.sk.pos = MAX(sk.pos, 0); | ||
1019 | str_parser.parms.sd.sk.len = 1024*1024; | ||
1020 | str_parser.parms.sd.sk.dir = SSCAN_FORWARD; | ||
1021 | |||
1022 | DEBUGF("thumb pos:%ld len:%ld\n", str_parser.parms.sd.sk.pos, | ||
1023 | (long)str_parser.parms.sd.sk.len); | ||
1024 | |||
1025 | result = str_send_msg(&video_str, STREAM_SYNC, | ||
1026 | (intptr_t)&str_parser.parms.sd); | ||
1027 | |||
1028 | if (result != STREAM_PERFECT_MATCH) | ||
1029 | { | ||
1030 | /* Two tries should be all that is nescessary to find the exact frame | ||
1031 | * if the first GOP actually started later than the timestamp - the | ||
1032 | * GOP just prior must then start on or earlier. */ | ||
1033 | if (++tries <= 2) | ||
1034 | goto try_again; | ||
1035 | } | ||
1036 | |||
1037 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
1038 | rb->cpu_boost(false); | ||
1039 | #endif | ||
1040 | |||
1041 | return result > STREAM_OK; | ||
1042 | } | ||
1043 | |||
1044 | /* Seek parser to the specified time and return absolute time. | ||
1045 | * No actual hard stuff is performed here. That's done when streaming is | ||
1046 | * about to begin or something from the current position is requested */ | ||
1047 | uint32_t parser_seek_time(uint32_t time) | ||
1048 | { | ||
1049 | if (!parser_can_seek()) | ||
1050 | time = 0; | ||
1051 | else if (time > str_parser.duration) | ||
1052 | time = str_parser.duration; | ||
1053 | |||
1054 | str_parser.last_seek_time = time + str_parser.start_pts; | ||
1055 | return str_parser.last_seek_time; | ||
1056 | } | ||
1057 | |||
1058 | void parser_prepare_streaming(void) | ||
1059 | { | ||
1060 | struct stream_window sw; | ||
1061 | |||
1062 | DEBUGF("parser_prepare_streaming\n"); | ||
1063 | |||
1064 | /* Prepare initial video frame */ | ||
1065 | parser_prepare_image(str_parser.last_seek_time); | ||
1066 | |||
1067 | /* Sync audio stream */ | ||
1068 | if (audio_str.start_pts != INVALID_TIMESTAMP) | ||
1069 | prepare_audio(str_parser.last_seek_time); | ||
1070 | |||
1071 | /* Prequeue some data and set buffer window */ | ||
1072 | if (!stream_get_window(&sw)) | ||
1073 | sw.left = sw.right = disk_buf.filesize; | ||
1074 | |||
1075 | DEBUGF(" swl:%ld swr:%ld\n", sw.left, sw.right); | ||
1076 | |||
1077 | if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD) | ||
1078 | sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD; | ||
1079 | |||
1080 | disk_buf_prepare_streaming(sw.left, | ||
1081 | sw.right - sw.left + 4*MIN_BUFAHEAD); | ||
1082 | } | ||
1083 | |||
1084 | int parser_init_stream(void) | ||
1085 | { | ||
1086 | if (disk_buf.in_file < 0) | ||
1087 | return STREAM_ERROR; | ||
1088 | |||
1089 | /* TODO: Actually find which streams are available */ | ||
1090 | audio_str.id = MPEG_STREAM_AUDIO_FIRST; | ||
1091 | video_str.id = MPEG_STREAM_VIDEO_FIRST; | ||
1092 | |||
1093 | /* Try to pull a video PES - if not found, try video init anyway which | ||
1094 | * should succeed if it really is a video-only stream */ | ||
1095 | video_str.hdr.pos = 0; | ||
1096 | video_str.hdr.limit = 256*1024; | ||
1097 | |||
1098 | if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK) | ||
1099 | { | ||
1100 | /* Found a video packet - assume program stream */ | ||
1101 | str_parser.format = STREAM_FMT_MPEG_PS; | ||
1102 | str_parser.next_data = parse_demux; | ||
1103 | } | ||
1104 | else | ||
1105 | { | ||
1106 | /* No PES element found - assume video elementary stream */ | ||
1107 | str_parser.format = STREAM_FMT_MPV; | ||
1108 | str_parser.next_data = parse_elementary; | ||
1109 | } | ||
1110 | |||
1111 | if (!init_video_info()) | ||
1112 | { | ||
1113 | /* Cannot determine video size, etc. */ | ||
1114 | parser_init_state(); | ||
1115 | return STREAM_UNSUPPORTED; | ||
1116 | } | ||
1117 | |||
1118 | if (str_parser.format == STREAM_FMT_MPEG_PS) | ||
1119 | { | ||
1120 | /* Initalize start_pts and end_pts with the length (in 45kHz units) of | ||
1121 | * the movie. INVALID_TIMESTAMP if the time could not be determined */ | ||
1122 | if (!init_times(&video_str) || !check_times(&video_str)) | ||
1123 | { | ||
1124 | /* Must have video at least */ | ||
1125 | parser_init_state(); | ||
1126 | return STREAM_UNSUPPORTED; | ||
1127 | } | ||
1128 | |||
1129 | str_parser.flags |= STREAMF_CAN_SEEK; | ||
1130 | |||
1131 | if (init_times(&audio_str)) | ||
1132 | { | ||
1133 | /* Audio will be part of playback pool */ | ||
1134 | stream_add_stream(&audio_str); | ||
1135 | |||
1136 | if (check_times(&audio_str)) | ||
1137 | { | ||
1138 | /* Overall duration is maximum span */ | ||
1139 | str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts); | ||
1140 | str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts); | ||
1141 | } | ||
1142 | else | ||
1143 | { | ||
1144 | /* Bad times on audio - use video times */ | ||
1145 | str_parser.start_pts = video_str.start_pts; | ||
1146 | str_parser.end_pts = video_str.end_pts; | ||
1147 | |||
1148 | /* Questionable: could use bitrate seek and match video to that */ | ||
1149 | audio_str.start_pts = video_str.start_pts; | ||
1150 | audio_str.end_pts = video_str.end_pts; | ||
1151 | } | ||
1152 | } | ||
1153 | else | ||
1154 | { | ||
1155 | /* No audio stream - use video only */ | ||
1156 | str_parser.start_pts = video_str.start_pts; | ||
1157 | str_parser.end_pts = video_str.end_pts; | ||
1158 | } | ||
1159 | |||
1160 | str_parser.last_seek_time = str_parser.start_pts; | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | /* There's no way to handle times on this without a full file | ||
1165 | * scan */ | ||
1166 | audio_str.start_pts = INVALID_TIMESTAMP; | ||
1167 | audio_str.end_pts = INVALID_TIMESTAMP; | ||
1168 | video_str.start_pts = 0; | ||
1169 | video_str.end_pts = INVALID_TIMESTAMP; | ||
1170 | str_parser.start_pts = 0; | ||
1171 | str_parser.end_pts = INVALID_TIMESTAMP; | ||
1172 | } | ||
1173 | |||
1174 | /* Add video to playback pool */ | ||
1175 | stream_add_stream(&video_str); | ||
1176 | |||
1177 | /* Cache duration - it's used very often */ | ||
1178 | str_parser.duration = str_parser.end_pts - str_parser.start_pts; | ||
1179 | |||
1180 | DEBUGF("Movie info:\n" | ||
1181 | " size:%dx%d\n" | ||
1182 | " start:%u\n" | ||
1183 | " end:%u\n" | ||
1184 | " duration:%u\n", | ||
1185 | str_parser.dims.w, str_parser.dims.h, | ||
1186 | (unsigned)str_parser.start_pts, | ||
1187 | (unsigned)str_parser.end_pts, | ||
1188 | (unsigned)str_parser.duration); | ||
1189 | |||
1190 | return STREAM_OK; | ||
1191 | } | ||
1192 | |||
1193 | void parser_close_stream(void) | ||
1194 | { | ||
1195 | stream_remove_streams(); | ||
1196 | parser_init_state(); | ||
1197 | } | ||
1198 | |||
1199 | bool parser_init(void) | ||
1200 | { | ||
1201 | parser_init_state(); | ||
1202 | return true; | ||
1203 | } | ||