The beginnings of a wonderful friendship... uhm... of a MPEG PS demuxer.

This commit is contained in:
Moritz Bunkus 2004-12-28 10:04:12 +00:00
parent 78cd9491bc
commit 7bb703c8aa
7 changed files with 393 additions and 7 deletions

View File

@ -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
View 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

View File

@ -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);
}

View File

@ -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

View File

@ -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"},

View File

@ -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);

View File

@ -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;