From 37a9a6457d3335162f39d1dcf99d53a69a17a67d Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 29 Dec 2004 21:05:03 +0000 Subject: [PATCH] Support for parsing MPEG-2 PS streams. --- src/input/r_mpeg.cpp | 211 ++++++++++++++++++++++++++++++++++--------- src/input/r_mpeg.h | 12 ++- 2 files changed, 177 insertions(+), 46 deletions(-) diff --git a/src/input/r_mpeg.cpp b/src/input/r_mpeg.cpp index 7854b3050..048e8b25e 100644 --- a/src/input/r_mpeg.cpp +++ b/src/input/r_mpeg.cpp @@ -28,6 +28,7 @@ #include "r_mpeg.h" #include "smart_pointers.h" #include "p_ac3.h" +#include "p_dts.h" #include "p_mp3.h" #include "p_video.h" @@ -265,16 +266,16 @@ mpeg_ps_reader_c::mpeg_ps_reader_c(track_info_c *nti) try { uint32_t header; uint8_t byte; - bool streams_found[256], done; + bool done; int i; mm_io = new mm_file_io_c(ti->fname); size = mm_io->get_size(); + file_done = false; bytes_processed = 0; - memset(streams_found, 0, sizeof(bool) * 256); - for (i = 0; i < 256; i++) + for (i = 0; i < 512; i++) id2idx[i] = -1; header = mm_io->read_uint32_be(); done = mm_io->eof(); @@ -334,9 +335,9 @@ mpeg_ps_reader_c::mpeg_ps_reader_c(track_info_c *nti) } stream_id = header & 0xff; - if (!streams_found[stream_id]) - found_new_stream(stream_id); - streams_found[stream_id] = true; + mm_io->save_pos(); + found_new_stream(stream_id); + mm_io->restore_pos(); pes_packet_length = mm_io->read_uint16_be(); mxverb(3, "mpeg_ps: id 0x%02x len %u at %lld\n", stream_id, pes_packet_length, mm_io->getFilePointer() - 4 - 2); @@ -351,11 +352,35 @@ mpeg_ps_reader_c::mpeg_ps_reader_c(track_info_c *nti) done |= mm_io->eof() || (mm_io->getFilePointer() >= PS_PROBE_SIZE); } // while (!done) - mxverb(3, "mpeg_ps: Streams found: "); + mxverb(2, "mpeg_ps: Streams found: "); for (i = 0; i < 256; i++) - if (streams_found[i]) - mxverb(3, "%02x ", i); - mxverb(3, "\n"); + if (id2idx[i] != -1) + mxverb(2, "%02x ", i); + for (i = 256 ; i < 512; i++) + if (id2idx[i] != -1) + mxverb(2, "bd(%02x) ", i - 256); + mxverb(2, "\n"); + + // Calculate by how much the timecodes have to be offset + if (tracks.size() > 0) { + int64_t min_timecode; + + min_timecode = tracks[0]->timecode_offset; + for (i = 1; i < tracks.size(); i++) + if (tracks[i]->timecode_offset < min_timecode) + min_timecode = tracks[i]->timecode_offset; + for (i = 0; i < tracks.size(); i++) + tracks[i]->timecode_offset -= min_timecode; + + mxverb(2, "mpeg_ps: Timecode offset: min was %lld ", min_timecode); + for (i = 0; i < tracks.size(); i++) + if (tracks[i]->id > 0xff) + mxverb(2, "bd(%d)=%lld ", tracks[i]->id - 256, + tracks[i]->timecode_offset); + else + mxverb(2, "%d=%lld ", tracks[i]->id, tracks[i]->timecode_offset); + mxverb(2, "\n"); + } mm_io->setFilePointer(0, seek_beginning); @@ -390,7 +415,8 @@ mpeg_ps_reader_c::read_timestamp(int c, bool mpeg_ps_reader_c::parse_packet(int id, int64_t ×tamp, - int &length) { + int &length, + int &aid) { uint8_t c; length = mm_io->read_uint16_be(); @@ -404,6 +430,8 @@ mpeg_ps_reader_c::parse_packet(int id, if (length == 0) return false; + aid = -1; + c = 0; // Skip stuFFing bytes while (length > 0) { @@ -436,7 +464,59 @@ mpeg_ps_reader_c::parse_packet(int id, length -= 5; } else if ((c & 0xc0) == 0x80) { - mxerror("mpeg_ps_reader: System-2 (VOB) streams are not supported yet.\n"); + int pts_flags; + int hdrlen; + + if ((c & 0x30) != 0x00) + mxerror(FMT_FN "Reading encrypted VOBs is not supported.\n", + ti->fname.c_str()); + pts_flags = mm_io->read_uint8() >> 6; + hdrlen = mm_io->read_uint8(); + length -= 2; + if (hdrlen > length) + return false; + + if ((pts_flags & 2) == 2) { + if (hdrlen < 5) + return false; + c = mm_io->read_uint8(); + if (!read_timestamp(c, timestamp)) + return false; + length -= 5; + hdrlen -= 5; + + } + if (pts_flags == 3) { + if (hdrlen < 5) + return false; + mm_io->skip(5); + length -= 5; + hdrlen -= 5; + } + + if (hdrlen > 0) { + length -= hdrlen; + mm_io->skip(hdrlen); + } + + if (id == 0xbd) { // DVD audio substream + if (length < 4) + return false; + aid = mm_io->read_uint8(); + length--; + + if ((aid >= 0x80) && (aid <= 0xbf)) { + mm_io->skip(3); // number of frames, startpos + length -= 3; + + if (aid >= 0xa0) { // LPCM + if (length < 3) + return false; + mm_io->skip(3); + length -= 3; + } + } + } } else if (c != 0x0f) return false; @@ -449,23 +529,44 @@ mpeg_ps_reader_c::parse_packet(int id, void mpeg_ps_reader_c::found_new_stream(int id) { - if ((id < 0xc0) || (id > 0xef)) + if (((id < 0xc0) || (id > 0xef)) && (id != 0xbd)) return; - mm_io->save_pos(); - try { int64_t timecode; - int length; + int length, aid; unsigned char *buf; - if (!parse_packet(id, timecode, length)) + if (!parse_packet(id, timecode, length, aid)) throw false; - mpeg_ps_track_ptr track(new mpeg_ps_track_t); - track->first_timecode = timecode; + if ((id == 0xbd) && (aid == -1)) + return; - if (id < 0xe0) { + if (id == 0xbd) // DVD audio substream + id = 256 + aid; + + if (id2idx[id] != -1) + return; + + mpeg_ps_track_ptr track(new mpeg_ps_track_t); + track->timecode_offset = timecode; + + if (id > 0xff) { + if ((aid >= 0x20) && (aid <= 0x3f)) { + track->type = 's'; + track->fourcc = FOURCC('V', 'S', 'U', 'B'); + } else if ((aid >= 0x80) && (aid <= 0x87)) { + track->type = 'a'; + track->fourcc = FOURCC('A', 'C', '3', ' '); + } else if ((aid >= 0x88) && (aid <= 0x90)) { + track->type = 'a'; + track->fourcc = FOURCC('D', 'T', 'S', ' '); + } else if ((aid >= 0xa0) && (aid <= 0xa8)) { + track->type = 'a'; + track->fourcc = FOURCC('P', 'C', 'M', ' '); + } + } else if (id < 0xe0) { track->type = 'a'; track->fourcc = FOURCC('M', 'P', '2', ' '); } else { @@ -473,6 +574,9 @@ mpeg_ps_reader_c::found_new_stream(int id) { track->fourcc = FOURCC('m', 'p', 'g', '0' + version); } + if (track->type == '?') + return; + autofree_ptr af_buf(safemalloc(length)); buf = af_buf; if (mm_io->read(buf, length) != length) @@ -493,7 +597,7 @@ mpeg_ps_reader_c::found_new_stream(int id) { if (!find_next_packet_for_id(id)) throw false; - if (!parse_packet(id, timecode, length)) + if (!parse_packet(id, timecode, length, aid)) throw false; autofree_ptr new_buf(safemalloc(length)); if (mm_io->read(new_buf, length) != length) @@ -527,14 +631,11 @@ mpeg_ps_reader_c::found_new_stream(int id) { } track->fourcc = FOURCC('m', 'p', 'g', '0' + track->v_version); - } else { // if (track->fourcc == ...) + } else // if (track->fourcc == ...) // Unsupported video track type - mm_io->restore_pos(); return; - } - - } else { // if (track->type == 'v') + } else if (track->type == 'a') { // if (track->type == 'v') if (track->fourcc == FOURCC('M', 'P', '2', ' ')) { mp3_header_t header; @@ -554,17 +655,20 @@ mpeg_ps_reader_c::found_new_stream(int id) { track->a_sample_rate = header.sample_rate; track->a_bsid = header.bsid; -// } else if (track->fourcc == FOURCC('D', 'T', 'S', ' ')) { + } else if (track->fourcc == FOURCC('D', 'T', 'S', ' ')) { + if (find_dts_header(buf, length, &track->dts_header) != 0) + throw "Error parsing the first DTS audio frame."; // } else if (track->fourcc == FOURCC('P', 'C', 'M', ' ')) { - } else { + } else // Unsupported audio track type - mm_io->restore_pos(); return; - } - } + } else + // Unsupported track type + return; + track->id = id; id2idx[id] = tracks.size(); tracks.push_back(track); } catch(const char *msg) { @@ -574,8 +678,6 @@ mpeg_ps_reader_c::found_new_stream(int id) { "header reading phase. This stream seems to be badly damaged.\n", ti->fname.c_str()); } - - mm_io->restore_pos(); } bool @@ -678,8 +780,15 @@ mpeg_ps_reader_c::create_packetizer(int64_t id) { track->a_channels, track->a_bsid, ti)); if (verbose) - mxinfo(FMT_TID "Using the AC3 output module.\n", - ti->fname.c_str(), id); + mxinfo(FMT_TID "Using the AC3 output module.\n", ti->fname.c_str(), + id); + + } else if (track->fourcc == FOURCC('D', 'T', 'S', ' ')) { + track->ptzr = + add_packetizer(new dts_packetizer_c(this, track->dts_header, ti)); + if (verbose) + mxinfo(FMT_TID "Using the DTS output module.\n", ti->fname.c_str(), + id); } else mxerror("mpeg_ps_reader: Should not have happened #1. %s", BUGMSG); @@ -721,26 +830,43 @@ file_status_e mpeg_ps_reader_c::read(generic_packetizer_c *, bool) { int64_t timecode, packet_pos; - int new_id, length; + int new_id, length, aid; unsigned char *buf; + if (file_done) + return FILE_STATUS_DONE; + try { while (find_next_packet(new_id)) { - if ((id2idx[new_id] == -1) || - (tracks[id2idx[new_id]]->ptzr == -1)) { + if ((new_id != 0xbd) && + ((id2idx[new_id] == -1) || + (tracks[id2idx[new_id]]->ptzr == -1))) { mm_io->skip(mm_io->read_uint16_be()); continue; } - packet_pos = mm_io->getFilePointer() - 4; - if (!parse_packet(new_id, timecode, length)) - return FILE_STATUS_DONE; - mxverb(2, "mpeg_ps: packet for %d length %d at %lld\n", new_id, length, + packet_pos = mm_io->getFilePointer() - 4; + if (!parse_packet(new_id, timecode, length, aid)) { + file_done = true; + return FILE_STATUS_DONE; + } + + if (new_id == 0xbd) + new_id = 256 + aid; + + if ((id2idx[new_id] == -1) || + (tracks[id2idx[new_id]]->ptzr == -1)) { + mm_io->skip(length); + continue; + } + + mxverb(3, "mpeg_ps: packet for %d length %d at %lld\n", new_id, length, packet_pos); buf = (unsigned char *)safemalloc(length); if (mm_io->read(buf, length) != length) { safefree(buf); + file_done = true; return FILE_STATUS_DONE; } @@ -751,6 +877,7 @@ mpeg_ps_reader_c::read(generic_packetizer_c *, } } catch(...) { } + file_done = true; return FILE_STATUS_DONE; } diff --git a/src/input/r_mpeg.h b/src/input/r_mpeg.h index dda5b01c3..8d642404f 100644 --- a/src/input/r_mpeg.h +++ b/src/input/r_mpeg.h @@ -18,6 +18,7 @@ #include "os.h" +#include "dts_common.h" #include "smart_pointers.h" #include "pr_generic.h" #include "M2VParser.h" @@ -54,9 +55,10 @@ struct mpeg_ps_track_t { int ptzr; char type; // 'v' for video, 'a' for audio, 's' for subs + int id; uint32_t fourcc; - int64_t first_timecode; + int64_t timecode_offset; int v_version, v_width, v_height, v_dwidth, v_dheight; double v_frame_rate, v_aspect_ratio; @@ -64,9 +66,10 @@ struct mpeg_ps_track_t { int raw_seq_hdr_size; int a_channels, a_sample_rate, a_bits_per_sample, a_bsid; + dts_header_t dts_header; mpeg_ps_track_t(): - ptzr(-1), type(0), fourcc(0), + ptzr(-1), type(0), fourcc(0), timecode_offset(-1), v_version(0), v_width(0), v_height(0), v_dwidth(0), v_dheight(0), v_frame_rate(0), v_aspect_ratio(0), raw_seq_hdr(NULL), raw_seq_hdr_size(0), @@ -84,8 +87,9 @@ private: mm_io_c *mm_io; int64_t bytes_processed, size, duration; - int id2idx[256]; + int id2idx[512]; int version; + bool file_done; vector tracks; @@ -102,7 +106,7 @@ public: virtual void found_new_stream(int id); virtual bool read_timestamp(int c, int64_t ×tamp); - virtual bool parse_packet(int id, int64_t ×tamp, int &size); + virtual bool parse_packet(int id, int64_t ×tamp, int &size, int &aid); virtual bool find_next_packet(int &id); virtual bool find_next_packet_for_id(int id);