diff --git a/src/common/mpeg4_common.h b/src/common/mpeg4_common.h index 9fbab0601..e34163fce 100644 --- a/src/common/mpeg4_common.h +++ b/src/common/mpeg4_common.h @@ -22,6 +22,22 @@ #define MPEGVIDEO_SEQUENCE_START_CODE 0x000001b3 /** Start code for a MPEG-1 and -2 packet */ #define MPEGVIDEO_PACKET_START_CODE 0x000001ba +#define MPEGVIDEO_SYSTEM_HEADER_START_CODE 0x000001bb +#define MPEGVIDEO_MPEG_PROGRAM_END_CODE 0x000001b9 + +#define MPEGVIDEO_START_CODE_PREFIX 0x000001 +#define MPEGVIDEO_PROGRAM_STREAM_MAP 0xbc +#define MPEGVIDEO_PRIVATE_STREAM_1 0xbd +#define MPEGVIDEO_PADDING_STREAM 0xbe +#define MPEGVIDEO_PRIVATE_STREAM_2 0xbf +#define MPEGVIDEO_ECM_STREAM 0xf0 +#define MPEGVIDEO_EMM_STREAM 0xf1 +#define MPEGVIDEO_PROGRAM_STREAM_DIRECTORY 0xff +#define MPEGVIDEO_DSMCC_STREAM 0xf2 +#define MPEGVIDEO_ITUTRECH222TYPEE_STREAM 0xf8 + +#define mpeg_is_start_code(v) \ + ((((v) >> 8) & 0xffffff) == MPEGVIDEO_START_CODE_PREFIX) /** MPEG-1/-2 frame rate: 24000/1001 frames per second */ #define MPEGVIDEO_FPS_23_976 0x01 diff --git a/src/common/smart_pointers.h b/src/common/smart_pointers.h new file mode 100644 index 000000000..61cec6a7d --- /dev/null +++ b/src/common/smart_pointers.h @@ -0,0 +1,107 @@ +#ifndef COUNTED_PTR_H +#define COUNTED_PTR_H + +template class counted_ptr { +public: + typedef X element_type; + + explicit counted_ptr(X *p = 0): // allocate a new counter + its_counter(0) { + if (p) + its_counter = new counter(p); + } + + ~counted_ptr() { + release(); + } + + counted_ptr(const counted_ptr& r) throw() { + acquire(r.its_counter); + } + counted_ptr& operator=(const counted_ptr& r) { + if (this != &r) { + release(); + acquire(r.its_counter); + } + return *this; + } + +// template friend class counted_ptr; + template counted_ptr(const counted_ptr& r) throw() { + acquire(r.its_counter); + } + template counted_ptr& operator=(const counted_ptr& r) { + if (this != &r) { + release(); + acquire(r.its_counter); + } + return *this; + } + + X &operator*() const throw() { + return *its_counter->ptr; + } + X *operator->() const throw() { + return its_counter->ptr; + } + X *get() const throw() { + return its_counter ? its_counter->ptr : 0; + } + bool unique() const throw() { + return (its_counter ? its_counter->count == 1 : true); + } + +private: + struct counter { + counter(X *p = 0, unsigned c = 1): + ptr(p), count(c) {} + X *ptr; + unsigned count; + }* its_counter; + + void acquire(counter* c) throw() { // increment the count + its_counter = c; + if (c) + ++c->count; + } + + void release() { // decrement the count, delete if it is 0 + if (its_counter) { + if (--its_counter->count == 0) { + delete its_counter->ptr; + delete its_counter; + } + its_counter = 0; + } + } +}; + +// ----------------------------------------------------------------- + +template class autofree_ptr { +protected: + X *ptr; + +public: + typedef X element_type; + + explicit autofree_ptr(X *p = NULL): + ptr(p) {} + + explicit autofree_ptr(void *p = NULL): + ptr((X *)p) {} + + ~autofree_ptr() { + safefree(ptr); + } + + operator X *() { + return ptr; + } + + operator const X *() { + return ptr; + } +}; + +#endif // COUNTED_PTR_H diff --git a/src/input/r_mpeg.cpp b/src/input/r_mpeg.cpp index 39ee3eb25..d7e1fd5c2 100644 --- a/src/input/r_mpeg.cpp +++ b/src/input/r_mpeg.cpp @@ -234,3 +234,212 @@ mpeg_es_reader_c::identify() { mxinfo("File '%s': container: MPEG elementary stream (ES)\n" "Track ID 0: video (MPEG %d)\n", ti->fname.c_str(), version); } + +// ------------------------------------------------------------------------ + +#define PS_PROBE_SIZE 1024 * 1024 + +int +mpeg_ps_reader_c::probe_file(mm_io_c *mm_io, + int64_t size) { + try { + autofree_ptr af_buf(safemalloc(PS_PROBE_SIZE)); + unsigned char *buf = af_buf; + int num_read; + + mm_io->setFilePointer(0, seek_beginning); + num_read = mm_io->read(buf, PS_PROBE_SIZE); + if (num_read < 4) + return 0; + mm_io->setFilePointer(0, seek_beginning); + + if (get_uint32_be(buf) != MPEGVIDEO_PACKET_START_CODE) + return 0; + + return 1; + + } catch (exception &ex) { + return 0; + } +} + +mpeg_ps_reader_c::mpeg_ps_reader_c(track_info_c *nti) + throw (error_c): + generic_reader_c(nti) { + try { + uint32_t header; + uint8_t byte; + bool streams_found[256], done; + int i; + + mm_io = new mm_file_io_c(ti->fname); + size = mm_io->get_size(); + + bytes_processed = 0; + + memset(streams_found, 0, sizeof(bool) * 256); + header = mm_io->read_uint32_be(); + done = mm_io->eof(); + version = -1; + + while (!done) { + uint8_t stream_id; + uint16_t pes_packet_length; + + switch (header) { + case MPEGVIDEO_PACKET_START_CODE: + mxverb(3, "mpeg_ps: packet start at %lld\n", + mm_io->getFilePointer() - 4); + + if (version == -1) { + byte = mm_io->read_uint8(); + if ((byte & 0xc0) != 0) + version = 2; // MPEG-2 PS + else + version = 1; + mm_io->skip(-1); + } + + mm_io->skip(2 * 4); // pack header + if (version == 2) { + mm_io->skip(1); + byte = mm_io->read_uint8() & 0x07; + mm_io->skip(byte); // stuffing bytes + } + header = mm_io->read_uint32_be(); + break; + + case MPEGVIDEO_SYSTEM_HEADER_START_CODE: + mxverb(3, "mpeg_ps: system header start code at %lld\n", + mm_io->getFilePointer() - 4); + + mm_io->skip(2 * 4); // system header + byte = mm_io->read_uint8(); + while ((byte & 0x80) == 0x80) { + mm_io->skip(2); // P-STD info + byte = mm_io->read_uint8(); + } + mm_io->skip(-1); + header = mm_io->read_uint32_be(); + break; + + case MPEGVIDEO_MPEG_PROGRAM_END_CODE: + done = true; + break; + + default: + if (!mpeg_is_start_code(header)) { + mxverb(3, "mpeg_ps: unknown header 0x%08x at %lld\n", + header, mm_io->getFilePointer() - 4); + done = true; + break; + } + + stream_id = header & 0xff; + streams_found[stream_id] = true; + 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); + + switch (stream_id) { + + } + + mm_io->skip(pes_packet_length); + + header = mm_io->read_uint32_be(); + while (!mpeg_is_start_code(header) && !mm_io->eof() && + (mm_io->getFilePointer() < PS_PROBE_SIZE)) { + header <<= 8; + header |= mm_io->read_uint8(); + } + if (!mpeg_is_start_code(header)) + done = true; + + break; + } + + done |= mm_io->eof() || (mm_io->getFilePointer() >= PS_PROBE_SIZE); + } // while (!done) + + mxverb(3, "mpeg_ps: Streams found: "); + for (i = 0; i < 256; i++) + if (streams_found[i]) + mxverb(3, "%02x ", i); + mxverb(3, "\n"); + + } catch (exception &ex) { + throw error_c("mpeg_ps_reader: Could not open the file."); + } + if (verbose) + mxinfo(FMT_FN "Using the MPEG PS demultiplexer.\n", ti->fname.c_str()); +} + +mpeg_ps_reader_c::~mpeg_ps_reader_c() { + delete mm_io; +} + +void +mpeg_ps_reader_c::create_packetizer(int64_t) { +} + +file_status_e +mpeg_ps_reader_c::read(generic_packetizer_c *, + bool) { + return FILE_STATUS_DONE; +} + +bool +mpeg_ps_reader_c::read_frame(M2VParser &parser, + mm_io_c &in, + int64_t max_size) { +// int bytes_probed; + +// bytes_probed = 0; +// while (true) { +// int state; + +// state = parser.GetState(); + +// if (state == MPV_PARSER_STATE_NEED_DATA) { +// unsigned char *buffer; +// int bytes_read, bytes_to_read; + +// if ((max_size != -1) && (bytes_probed > max_size)) +// return false; + +// bytes_to_read = (parser.GetFreeBufferSpace() < READ_SIZE) ? +// parser.GetFreeBufferSpace() : READ_SIZE; +// buffer = new unsigned char[bytes_to_read]; +// bytes_read = in.read(buffer, bytes_to_read); +// if (bytes_read == 0) { +// delete [] buffer; +// break; +// } +// bytes_probed += bytes_read; + +// parser.WriteData(buffer, bytes_read); +// delete [] buffer; + +// } else if (state == MPV_PARSER_STATE_FRAME) +// return true; + +// else if ((state == MPV_PARSER_STATE_EOS) || +// (state == MPV_PARSER_STATE_ERROR)) +// return false; +// } + + return false; +} + +int +mpeg_ps_reader_c::get_progress() { + return 100 * bytes_processed / size; +} + +void +mpeg_ps_reader_c::identify() { + mxinfo("File '%s': container: MPEG %d program stream (PS)\n", + ti->fname.c_str(), version); +} + diff --git a/src/input/r_mpeg.h b/src/input/r_mpeg.h index a1b3a3da2..d825a6194 100644 --- a/src/input/r_mpeg.h +++ b/src/input/r_mpeg.h @@ -8,7 +8,7 @@ $Id$ - class definitions for the MPEG ES demultiplexer module + class definitions for the MPEG ES/PS demultiplexer module Written by Moritz Bunkus . */ @@ -18,6 +18,7 @@ #include "os.h" +#include "smart_pointers.h" #include "pr_generic.h" #include "M2VParser.h" @@ -50,4 +51,50 @@ public: static int probe_file(mm_io_c *mm_io, int64_t size); }; +struct mpeg_ps_track_t { + char type; // 'v' for video, 'a' for audio, 's' for subs + int id; + + int v_version, v_width, v_height; + double v_frame_rate, v_aspect_ratio; + M2VParser *m2v_parser; + + int a_channels, a_sample_rate, a_bits_per_sample; + + mpeg_ps_track_t(): + type(0), id(0), v_version(0), v_width(0), v_height(0), + v_frame_rate(0), v_aspect_ratio(0), m2v_parser(NULL), + a_channels(0), a_sample_rate(0), a_bits_per_sample(0) { + }; + ~mpeg_ps_track_t() { + delete m2v_parser; + } +}; + +typedef counted_ptr mpeg_ps_track_ptr; + +class mpeg_ps_reader_c: public generic_reader_c { +private: + mm_io_c *mm_io; + int64_t bytes_processed, size, duration; + + int version; + + vector tracks; + +public: + mpeg_ps_reader_c(track_info_c *nti) throw (error_c); + virtual ~mpeg_ps_reader_c(); + + virtual file_status_e read(generic_packetizer_c *ptzr, bool force = false); + virtual int get_progress(); + virtual void identify(); + virtual void create_packetizer(int64_t id); + + static bool read_frame(M2VParser &parser, mm_io_c &in, + int64_t max_size = -1); + + static int probe_file(mm_io_c *mm_io, int64_t size); +}; + #endif // __R_MPEG_H diff --git a/src/merge/mkvmerge.cpp b/src/merge/mkvmerge.cpp index 5b3b5c9f0..fa7392744 100644 --- a/src/merge/mkvmerge.cpp +++ b/src/merge/mkvmerge.cpp @@ -88,8 +88,9 @@ file_type_t file_types[] = {"flac", FILE_TYPE_FLAC, "FLAC lossless audio"}, #endif {"tta ", FILE_TYPE_TTA, "TTA lossless audio"}, - {"m1v ", FILE_TYPE_MPEG, "MPEG-1 video elementary stream"}, - {"m2v ", FILE_TYPE_MPEG, "MPEG-2 video elementary stream"}, + {"m1v ", FILE_TYPE_MPEG_ES, "MPEG-1 video elementary stream"}, + {"m2v ", FILE_TYPE_MPEG_ES, "MPEG-2 video elementary stream"}, + {"mpg ", FILE_TYPE_MPEG_PS, "MPEG program stream"}, {"wv ", FILE_TYPE_WAVPACK4, "WAVPACK lossless audio"}, {"output modules:", -1, ""}, {" ", -1, "AAC audio"}, diff --git a/src/merge/mkvmerge.h b/src/merge/mkvmerge.h index 19002d1fd..e587d8ca0 100644 --- a/src/merge/mkvmerge.h +++ b/src/merge/mkvmerge.h @@ -45,10 +45,11 @@ enum file_type_e { FILE_TYPE_QTMP4, FILE_TYPE_FLAC, FILE_TYPE_TTA, - FILE_TYPE_MPEG, + FILE_TYPE_MPEG_ES, FILE_TYPE_VOBBTN, FILE_TYPE_WAVPACK4, - FILE_TYPE_MAX = FILE_TYPE_WAVPACK4 + FILE_TYPE_MPEG_PS, + FILE_TYPE_MAX = FILE_TYPE_MPEG_PS }; int64_t create_track_number(generic_reader_c *reader, int64_t tid); diff --git a/src/merge/output_control.cpp b/src/merge/output_control.cpp index bbefc1df8..5c78ab7aa 100644 --- a/src/merge/output_control.cpp +++ b/src/merge/output_control.cpp @@ -308,8 +308,10 @@ get_file_type(const string &filename) { type = FILE_TYPE_AAC; else if (vobbtn_reader_c::probe_file(mm_io, size)) type = FILE_TYPE_VOBBTN; + else if (mpeg_ps_reader_c::probe_file(mm_io, size)) + type = FILE_TYPE_MPEG_PS; else if (mpeg_es_reader_c::probe_file(mm_io, size)) - type = FILE_TYPE_MPEG; + type = FILE_TYPE_MPEG_ES; else { delete mm_io; @@ -1116,9 +1118,12 @@ create_readers() { case FILE_TYPE_TTA: file->reader = new tta_reader_c(file->ti); break; - case FILE_TYPE_MPEG: + case FILE_TYPE_MPEG_ES: file->reader = new mpeg_es_reader_c(file->ti); break; + case FILE_TYPE_MPEG_PS: + file->reader = new mpeg_ps_reader_c(file->ti); + break; case FILE_TYPE_VOBBTN: file->reader = new vobbtn_reader_c(file->ti); break;