A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1204 lines 37 kB view raw
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 26struct stream_parser str_parser SHAREDBSS_ATTR; 27 28static 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 */ 41void 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 */ 56void 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 */ 74static 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 80static 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 */ 86uint8_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 */ 120unsigned 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 */ 154uint32_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 181uint32_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 258static 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 275static 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 347static 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. */ 356static 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: %jd\n", (intmax_t) 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 571static 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 */ 610static 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:%jd wr:%jd\n" 737 " p:%p cp:%p cpe:%p\n" 738 " dbs:%p dbe:%p dbt:%p\n", 739 str->id, (intmax_t) str->hdr.win_left, (intmax_t) 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. */ 895static 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 966bool 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; 992try_again: 993 994 if (mpeg_parser_scan_start_code(&sk, MPEG_START_GOP)) 995 { 996 DEBUGF("GOP found at: %jd\n", (intmax_t) 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:%jd len:%ld\n", (intmax_t) 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 */ 1047uint32_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 1058void 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:%jd swr:%jd\n", 1076 (intmax_t) sw.left, (intmax_t) sw.right); 1077 1078 if (sw.right > disk_buf.filesize - 4*MIN_BUFAHEAD) 1079 sw.right = disk_buf.filesize - 4*MIN_BUFAHEAD; 1080 1081 disk_buf_prepare_streaming(sw.left, 1082 sw.right - sw.left + 4*MIN_BUFAHEAD); 1083} 1084 1085int parser_init_stream(void) 1086{ 1087 if (disk_buf.in_file < 0) 1088 return STREAM_ERROR; 1089 1090 /* TODO: Actually find which streams are available */ 1091 audio_str.id = MPEG_STREAM_AUDIO_FIRST; 1092 video_str.id = MPEG_STREAM_VIDEO_FIRST; 1093 1094 /* Try to pull a video PES - if not found, try video init anyway which 1095 * should succeed if it really is a video-only stream */ 1096 video_str.hdr.pos = 0; 1097 video_str.hdr.limit = 256*1024; 1098 1099 if (parse_demux(&video_str, STREAM_PM_RANDOM_ACCESS) == STREAM_OK) 1100 { 1101 /* Found a video packet - assume program stream */ 1102 str_parser.format = STREAM_FMT_MPEG_PS; 1103 str_parser.next_data = parse_demux; 1104 } 1105 else 1106 { 1107 /* No PES element found - assume video elementary stream */ 1108 str_parser.format = STREAM_FMT_MPV; 1109 str_parser.next_data = parse_elementary; 1110 } 1111 1112 if (!init_video_info()) 1113 { 1114 /* Cannot determine video size, etc. */ 1115 parser_init_state(); 1116 return STREAM_UNSUPPORTED; 1117 } 1118 1119 if (str_parser.format == STREAM_FMT_MPEG_PS) 1120 { 1121 /* Initalize start_pts and end_pts with the length (in 45kHz units) of 1122 * the movie. INVALID_TIMESTAMP if the time could not be determined */ 1123 if (!init_times(&video_str) || !check_times(&video_str)) 1124 { 1125 /* Must have video at least */ 1126 parser_init_state(); 1127 return STREAM_UNSUPPORTED; 1128 } 1129 1130 str_parser.flags |= STREAMF_CAN_SEEK; 1131 1132 if (init_times(&audio_str)) 1133 { 1134 /* Audio will be part of playback pool */ 1135 stream_add_stream(&audio_str); 1136 1137 if (check_times(&audio_str)) 1138 { 1139 /* Overall duration is maximum span */ 1140 str_parser.start_pts = MIN(audio_str.start_pts, video_str.start_pts); 1141 str_parser.end_pts = MAX(audio_str.end_pts, video_str.end_pts); 1142 } 1143 else 1144 { 1145 /* Bad times on audio - use video times */ 1146 str_parser.start_pts = video_str.start_pts; 1147 str_parser.end_pts = video_str.end_pts; 1148 1149 /* Questionable: could use bitrate seek and match video to that */ 1150 audio_str.start_pts = video_str.start_pts; 1151 audio_str.end_pts = video_str.end_pts; 1152 } 1153 } 1154 else 1155 { 1156 /* No audio stream - use video only */ 1157 str_parser.start_pts = video_str.start_pts; 1158 str_parser.end_pts = video_str.end_pts; 1159 } 1160 1161 str_parser.last_seek_time = str_parser.start_pts; 1162 } 1163 else 1164 { 1165 /* There's no way to handle times on this without a full file 1166 * scan */ 1167 audio_str.start_pts = INVALID_TIMESTAMP; 1168 audio_str.end_pts = INVALID_TIMESTAMP; 1169 video_str.start_pts = 0; 1170 video_str.end_pts = INVALID_TIMESTAMP; 1171 str_parser.start_pts = 0; 1172 str_parser.end_pts = INVALID_TIMESTAMP; 1173 } 1174 1175 /* Add video to playback pool */ 1176 stream_add_stream(&video_str); 1177 1178 /* Cache duration - it's used very often */ 1179 str_parser.duration = str_parser.end_pts - str_parser.start_pts; 1180 1181 DEBUGF("Movie info:\n" 1182 " size:%dx%d\n" 1183 " start:%u\n" 1184 " end:%u\n" 1185 " duration:%u\n", 1186 str_parser.dims.w, str_parser.dims.h, 1187 (unsigned)str_parser.start_pts, 1188 (unsigned)str_parser.end_pts, 1189 (unsigned)str_parser.duration); 1190 1191 return STREAM_OK; 1192} 1193 1194void parser_close_stream(void) 1195{ 1196 stream_remove_streams(); 1197 parser_init_state(); 1198} 1199 1200bool parser_init(void) 1201{ 1202 parser_init_state(); 1203 return true; 1204}