mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 20:01:53 +00:00
The beginnings of a wonderful friendship... uhm... of a MPEG PS demuxer.
This commit is contained in:
parent
78cd9491bc
commit
7bb703c8aa
@ -22,6 +22,22 @@
|
|||||||
#define MPEGVIDEO_SEQUENCE_START_CODE 0x000001b3
|
#define MPEGVIDEO_SEQUENCE_START_CODE 0x000001b3
|
||||||
/** Start code for a MPEG-1 and -2 packet */
|
/** Start code for a MPEG-1 and -2 packet */
|
||||||
#define MPEGVIDEO_PACKET_START_CODE 0x000001ba
|
#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 */
|
/** MPEG-1/-2 frame rate: 24000/1001 frames per second */
|
||||||
#define MPEGVIDEO_FPS_23_976 0x01
|
#define MPEGVIDEO_FPS_23_976 0x01
|
||||||
|
107
src/common/smart_pointers.h
Normal file
107
src/common/smart_pointers.h
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#ifndef COUNTED_PTR_H
|
||||||
|
#define COUNTED_PTR_H
|
||||||
|
|
||||||
|
template <class X> 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 <class Y> friend class counted_ptr<Y>;
|
||||||
|
template <class Y> counted_ptr(const counted_ptr<Y>& r) throw() {
|
||||||
|
acquire(r.its_counter);
|
||||||
|
}
|
||||||
|
template <class Y> counted_ptr& operator=(const counted_ptr<Y>& 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 X> 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
|
@ -234,3 +234,212 @@ mpeg_es_reader_c::identify() {
|
|||||||
mxinfo("File '%s': container: MPEG elementary stream (ES)\n"
|
mxinfo("File '%s': container: MPEG elementary stream (ES)\n"
|
||||||
"Track ID 0: video (MPEG %d)\n", ti->fname.c_str(), version);
|
"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<unsigned char> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
$Id$
|
$Id$
|
||||||
|
|
||||||
class definitions for the MPEG ES demultiplexer module
|
class definitions for the MPEG ES/PS demultiplexer module
|
||||||
|
|
||||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||||
*/
|
*/
|
||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
|
#include "smart_pointers.h"
|
||||||
#include "pr_generic.h"
|
#include "pr_generic.h"
|
||||||
#include "M2VParser.h"
|
#include "M2VParser.h"
|
||||||
|
|
||||||
@ -50,4 +51,50 @@ public:
|
|||||||
static int probe_file(mm_io_c *mm_io, int64_t size);
|
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_t> 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<mpeg_ps_track_ptr> 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
|
#endif // __R_MPEG_H
|
||||||
|
@ -88,8 +88,9 @@ file_type_t file_types[] =
|
|||||||
{"flac", FILE_TYPE_FLAC, "FLAC lossless audio"},
|
{"flac", FILE_TYPE_FLAC, "FLAC lossless audio"},
|
||||||
#endif
|
#endif
|
||||||
{"tta ", FILE_TYPE_TTA, "TTA lossless audio"},
|
{"tta ", FILE_TYPE_TTA, "TTA lossless audio"},
|
||||||
{"m1v ", FILE_TYPE_MPEG, "MPEG-1 video elementary stream"},
|
{"m1v ", FILE_TYPE_MPEG_ES, "MPEG-1 video elementary stream"},
|
||||||
{"m2v ", FILE_TYPE_MPEG, "MPEG-2 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"},
|
{"wv ", FILE_TYPE_WAVPACK4, "WAVPACK lossless audio"},
|
||||||
{"output modules:", -1, ""},
|
{"output modules:", -1, ""},
|
||||||
{" ", -1, "AAC audio"},
|
{" ", -1, "AAC audio"},
|
||||||
|
@ -45,10 +45,11 @@ enum file_type_e {
|
|||||||
FILE_TYPE_QTMP4,
|
FILE_TYPE_QTMP4,
|
||||||
FILE_TYPE_FLAC,
|
FILE_TYPE_FLAC,
|
||||||
FILE_TYPE_TTA,
|
FILE_TYPE_TTA,
|
||||||
FILE_TYPE_MPEG,
|
FILE_TYPE_MPEG_ES,
|
||||||
FILE_TYPE_VOBBTN,
|
FILE_TYPE_VOBBTN,
|
||||||
FILE_TYPE_WAVPACK4,
|
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);
|
int64_t create_track_number(generic_reader_c *reader, int64_t tid);
|
||||||
|
@ -308,8 +308,10 @@ get_file_type(const string &filename) {
|
|||||||
type = FILE_TYPE_AAC;
|
type = FILE_TYPE_AAC;
|
||||||
else if (vobbtn_reader_c::probe_file(mm_io, size))
|
else if (vobbtn_reader_c::probe_file(mm_io, size))
|
||||||
type = FILE_TYPE_VOBBTN;
|
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))
|
else if (mpeg_es_reader_c::probe_file(mm_io, size))
|
||||||
type = FILE_TYPE_MPEG;
|
type = FILE_TYPE_MPEG_ES;
|
||||||
else {
|
else {
|
||||||
delete mm_io;
|
delete mm_io;
|
||||||
|
|
||||||
@ -1116,9 +1118,12 @@ create_readers() {
|
|||||||
case FILE_TYPE_TTA:
|
case FILE_TYPE_TTA:
|
||||||
file->reader = new tta_reader_c(file->ti);
|
file->reader = new tta_reader_c(file->ti);
|
||||||
break;
|
break;
|
||||||
case FILE_TYPE_MPEG:
|
case FILE_TYPE_MPEG_ES:
|
||||||
file->reader = new mpeg_es_reader_c(file->ti);
|
file->reader = new mpeg_es_reader_c(file->ti);
|
||||||
break;
|
break;
|
||||||
|
case FILE_TYPE_MPEG_PS:
|
||||||
|
file->reader = new mpeg_ps_reader_c(file->ti);
|
||||||
|
break;
|
||||||
case FILE_TYPE_VOBBTN:
|
case FILE_TYPE_VOBBTN:
|
||||||
file->reader = new vobbtn_reader_c(file->ti);
|
file->reader = new vobbtn_reader_c(file->ti);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user