mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +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
|
||||
/** 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
|
||||
|
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"
|
||||
"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$
|
||||
|
||||
class definitions for the MPEG ES demultiplexer module
|
||||
class definitions for the MPEG ES/PS demultiplexer module
|
||||
|
||||
Written by Moritz Bunkus <moritz@bunkus.org>.
|
||||
*/
|
||||
@ -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_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
|
||||
|
@ -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"},
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user