mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 04:11:44 +00:00
Supprt for AAC in Quicktime.
This commit is contained in:
parent
449e95848e
commit
2ef1bc1b3c
@ -141,4 +141,42 @@ typedef struct {
|
||||
video_stsd_atom_t id;
|
||||
} qt_image_description_t;
|
||||
|
||||
// one byte tag identifiers
|
||||
#define MP4DT_O 0x01
|
||||
#define MP4DT_IO 0x02
|
||||
#define MP4DT_ES 0x03
|
||||
#define MP4DT_DEC_CONFIG 0x04
|
||||
#define MP4DT_DEC_SPECIFIC 0x05
|
||||
#define MP4DT_SL_CONFIG 0x06
|
||||
#define MP4DT_CONTENT_ID 0x07
|
||||
#define MP4DT_SUPPL_CONTENT_ID 0x08
|
||||
#define MP4DT_IP_PTR 0x09
|
||||
#define MP4DT_IPMP_PTR 0x0A
|
||||
#define MP4DT_IPMP 0x0B
|
||||
#define MP4DT_REGISTRATION 0x0D
|
||||
#define MP4DT_ESID_INC 0x0E
|
||||
#define MP4DT_ESID_REF 0x0F
|
||||
#define MP4DT_FILE_IO 0x10
|
||||
#define MP4DT_FILE_O 0x11
|
||||
#define MP4DT_EXT_PROFILE_LEVEL 0x13
|
||||
#define MP4DT_TAGS_START 0x80
|
||||
#define MP4DT_TAGS_END 0xFE
|
||||
|
||||
// MPEG4 esds structure
|
||||
typedef struct {
|
||||
uint8_t version;
|
||||
uint32_t flags;
|
||||
uint16_t esid;
|
||||
uint8_t stream_priority;
|
||||
uint8_t object_type_id;
|
||||
uint8_t stream_type;
|
||||
uint32_t buffer_size_db;
|
||||
uint32_t max_bitrate;
|
||||
uint32_t avg_bitrate;
|
||||
uint8_t decoder_config_len;
|
||||
unsigned char *decoder_config;
|
||||
uint8_t sl_config_len;
|
||||
unsigned char *sl_config;
|
||||
} esds_t;
|
||||
|
||||
#endif // __QTMP4_ATOMS_H
|
||||
|
200
src/r_mp4.cpp
200
src/r_mp4.cpp
@ -26,6 +26,7 @@
|
||||
#include "common.h"
|
||||
#include "matroska.h"
|
||||
#include "mkvmerge.h"
|
||||
#include "p_aac.h"
|
||||
#include "p_passthrough.h"
|
||||
#include "p_video.h"
|
||||
#include "r_mp4.h"
|
||||
@ -35,8 +36,12 @@ using namespace libmatroska;
|
||||
|
||||
#define PFX "Quicktime/MP4 reader: "
|
||||
|
||||
#define BE2STR(a) ((char *)&a)[3], ((char *)&a)[2], ((char *)&a)[1], \
|
||||
((char *)&a)[0]
|
||||
|
||||
int qtmp4_reader_c::probe_file(mm_io_c *in, int64_t size) {
|
||||
uint32_t atom_size, object;
|
||||
uint32_t atom;
|
||||
uint64_t atom_size;
|
||||
|
||||
if (size < 20)
|
||||
return 0;
|
||||
@ -44,10 +49,14 @@ int qtmp4_reader_c::probe_file(mm_io_c *in, int64_t size) {
|
||||
in->setFilePointer(0, seek_beginning);
|
||||
|
||||
atom_size = in->read_uint32_be();
|
||||
object = in->read_uint32_be();
|
||||
if (atom_size == 1)
|
||||
atom_size = in->read_uint64_be();
|
||||
atom = in->read_uint32_be();
|
||||
|
||||
if ((object == FOURCC('m', 'o', 'o', 'v')) ||
|
||||
(object == FOURCC('f', 't', 'y', 'p')))
|
||||
mxverb(2, PFX "Atom: '%c%c%c%c'; size: %llu\n", BE2STR(atom), atom_size);
|
||||
|
||||
if ((atom == FOURCC('m', 'o', 'o', 'v')) ||
|
||||
(atom == FOURCC('f', 't', 'y', 'p')))
|
||||
return 1;
|
||||
|
||||
} catch (exception &ex) {
|
||||
@ -105,6 +114,8 @@ void qtmp4_reader_c::free_demuxer(qtmp4_demuxer_t *dmx) {
|
||||
safefree(dmx->editlist_table);
|
||||
safefree(dmx->v_desc);
|
||||
safefree(dmx->a_priv);
|
||||
safefree(dmx->a_esds.decoder_config);
|
||||
safefree(dmx->a_esds.sl_config);
|
||||
}
|
||||
|
||||
void qtmp4_reader_c::read_atom(uint32_t &atom, uint64_t &size, uint64_t &pos,
|
||||
@ -122,8 +133,6 @@ void qtmp4_reader_c::read_atom(uint32_t &atom, uint64_t &size, uint64_t &pos,
|
||||
atom = io->read_uint32_be();
|
||||
}
|
||||
|
||||
#define BE2STR(a) ((char *)&a)[3], ((char *)&a)[2], ((char *)&a)[1], \
|
||||
((char *)&a)[0]
|
||||
#define skip_atom() io->setFilePointer(atom_pos + atom_size)
|
||||
|
||||
void qtmp4_reader_c::parse_headers() {
|
||||
@ -183,10 +192,19 @@ void qtmp4_reader_c::parse_headers() {
|
||||
strncasecmp(dmx->fourcc, "svq", 3) &&
|
||||
strncasecmp(dmx->fourcc, "cvid", 4)) ||
|
||||
((dmx->type == 'a') &&
|
||||
strncasecmp(dmx->fourcc, "QDM", 3))) {
|
||||
strncasecmp(dmx->fourcc, "QDM", 3) &&
|
||||
strncasecmp(dmx->fourcc, "MP4A", 4))) {
|
||||
mxwarn(PFX "Unknown/unsupported FourCC '%.4s' for track %u.\n",
|
||||
&dmx->fourcc, dmx->id);
|
||||
free_demuxer(dmx);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((dmx->type == 'a') &&
|
||||
!strncasecmp(dmx->fourcc, "MP4A", 4) &&
|
||||
(dmx->a_esds.decoder_config == NULL)) {
|
||||
mxwarn(PFX "AAC track %u is missing the esds atom/the decoder config.\n",
|
||||
dmx->id);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -325,10 +343,9 @@ void qtmp4_reader_c::handle_header_atoms(uint32_t parent, int64_t parent_size,
|
||||
(new_dmx->fourcc[2] == 0) && (new_dmx->fourcc[3] == 0))) {
|
||||
free_demuxer(new_dmx);
|
||||
safefree(new_dmx);
|
||||
} else {
|
||||
} else
|
||||
demuxers.push_back(new_dmx);
|
||||
new_dmx = NULL;
|
||||
}
|
||||
new_dmx = NULL;
|
||||
|
||||
}
|
||||
|
||||
@ -480,15 +497,44 @@ void qtmp4_reader_c::handle_header_atoms(uint32_t parent, int64_t parent_size,
|
||||
if ((get_uint16_be(&sv1_stsd.v0.version) == 1) &&
|
||||
(size > sizeof(sound_v1_stsd_atom_t))) {
|
||||
io->setFilePointer(pos + sizeof(sound_v1_stsd_atom_t) + 4);
|
||||
new_dmx->a_priv_size = io->read_uint32_be();
|
||||
io->skip(4);
|
||||
new_dmx->a_priv =
|
||||
(unsigned char *)safemalloc(new_dmx->a_priv_size);
|
||||
if (io->read(new_dmx->a_priv, new_dmx->a_priv_size) !=
|
||||
new_dmx->a_priv_size)
|
||||
throw exception();
|
||||
mxverb(2, PFX "%*sAudio private data size %u\n", (level + 1) * 2,
|
||||
"", new_dmx->a_priv_size);
|
||||
while (io->getFilePointer() < (pos + size)) {
|
||||
uint32_t add_atom, add_atom_size;
|
||||
|
||||
add_atom_size = io->read_uint32_be();
|
||||
add_atom = io->read_uint32();
|
||||
add_atom_size -= 8;
|
||||
mxverb(2, PFX "%*sAudio private data size: %u, type: '%.4s'\n",
|
||||
(level + 1) * 2, "", add_atom_size + 8, &add_atom);
|
||||
if (new_dmx->a_priv == NULL) {
|
||||
mm_mem_io_c *memio;
|
||||
uint32_t patom_size, patom;
|
||||
|
||||
new_dmx->a_priv_size = add_atom_size;
|
||||
new_dmx->a_priv =
|
||||
(unsigned char *)safemalloc(new_dmx->a_priv_size);
|
||||
if (io->read(new_dmx->a_priv, new_dmx->a_priv_size) !=
|
||||
new_dmx->a_priv_size)
|
||||
throw exception();
|
||||
|
||||
memio = new mm_mem_io_c(new_dmx->a_priv,
|
||||
new_dmx->a_priv_size);
|
||||
while (!memio->eof()) {
|
||||
patom_size = memio->read_uint32_be();
|
||||
patom = memio->read_uint32_be();
|
||||
mxverb(2, PFX "%*sAtom size: %u, atom: '%c%c%c%c'\n",
|
||||
(level + 2) * 2, "", patom_size, BE2STR(patom));
|
||||
if ((patom == FOURCC('e', 's', 'd', 's')) &&
|
||||
!new_dmx->a_esds_parsed) {
|
||||
memio->save_pos();
|
||||
new_dmx->a_esds_parsed = parse_esds_atom(memio, new_dmx);
|
||||
memio->restore_pos();
|
||||
}
|
||||
memio->skip(patom_size - 8);
|
||||
}
|
||||
delete memio;
|
||||
} else
|
||||
io->skip(add_atom_size);
|
||||
}
|
||||
}
|
||||
memcpy(&new_dmx->a_stsd, &sv1_stsd, sizeof(sound_v1_stsd_atom_t));
|
||||
|
||||
@ -825,6 +871,95 @@ int qtmp4_reader_c::read(generic_packetizer_c *ptzr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t qtmp4_reader_c::read_esds_descr_len(mm_mem_io_c *memio) {
|
||||
uint32_t len, num_bytes;
|
||||
uint8_t byte;
|
||||
|
||||
len = 0;
|
||||
num_bytes = 0;
|
||||
do {
|
||||
byte = memio->read_uint8();
|
||||
num_bytes++;
|
||||
len = (len << 7) | (byte & 0x7f);
|
||||
} while (((byte & 0x80) == 0x80) && (num_bytes < 4));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
bool qtmp4_reader_c::parse_esds_atom(mm_mem_io_c *memio,
|
||||
qtmp4_demuxer_t *dmx) {
|
||||
uint32_t len;
|
||||
uint8_t tag;
|
||||
esds_t *e;
|
||||
|
||||
e = &dmx->a_esds;
|
||||
e->version = memio->read_uint8();
|
||||
e->flags = memio->read_uint24_be();
|
||||
mxverb(2, PFX "esds: version: %u, flags: %u\n", e->version, e->flags);
|
||||
tag = memio->read_uint8();
|
||||
if (tag == MP4DT_ES) {
|
||||
len = read_esds_descr_len(memio);
|
||||
e->esid = memio->read_uint16_be();
|
||||
e->stream_priority = memio->read_uint8();
|
||||
mxverb(2, PFX "esds: id: %u, stream priority: %u, len: %u\n", e->esid,
|
||||
(uint32_t)e->stream_priority, len);
|
||||
} else {
|
||||
e->esid = memio->read_uint16_be();
|
||||
mxverb(2, PFX "esds: id: %u\n", e->esid);
|
||||
}
|
||||
|
||||
tag = memio->read_uint8();
|
||||
if (tag != MP4DT_DEC_CONFIG) {
|
||||
mxverb(2, PFX "tag is not DEC_CONFIG (0x%02x) but 0x%02x.\n",
|
||||
MP4DT_DEC_CONFIG, (uint32_t)tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = read_esds_descr_len(memio);
|
||||
|
||||
e->object_type_id = memio->read_uint8();
|
||||
e->stream_type = memio->read_uint8();
|
||||
e->buffer_size_db = memio->read_uint24_be();
|
||||
e->max_bitrate = memio->read_uint32_be();
|
||||
e->avg_bitrate = memio->read_uint32_be();
|
||||
mxverb(2, PFX "esds: decoder config descriptor, len: %u, object_type_id: "
|
||||
"%u, stream_type: 0x%2x, buffer_size_db: %u, max_bitrate: %.3fkbit/s"
|
||||
", avg_bitrate: %.3fkbit/s\n", len, e->object_type_id, e->stream_type,
|
||||
e->buffer_size_db, e->max_bitrate / 1000.0, e->avg_bitrate / 1000.0);
|
||||
|
||||
e->decoder_config_len = 0;
|
||||
|
||||
tag = memio->read_uint8();
|
||||
if (tag != MP4DT_DEC_SPECIFIC) {
|
||||
mxverb(2, PFX "tag is not DEC_SPECIFIC (0x%02x) but 0x%02x.\n",
|
||||
MP4DT_DEC_SPECIFIC, (uint32_t)tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = read_esds_descr_len(memio);
|
||||
e->decoder_config_len = len;
|
||||
e->decoder_config = (uint8_t *)safemalloc(len);
|
||||
if (memio->read(e->decoder_config, len) != len)
|
||||
throw exception();
|
||||
mxverb(2, PFX "esds: decoder specific descriptor, len: %u\n", len);
|
||||
|
||||
tag = memio->read_uint8();
|
||||
if (tag != MP4DT_SL_CONFIG) {
|
||||
mxverb(2, PFX "tag is not SL_CONFIG (0x%02x) but 0x%02x.\n",
|
||||
MP4DT_SL_CONFIG, (uint32_t)tag);
|
||||
return false;
|
||||
}
|
||||
|
||||
len = read_esds_descr_len(memio);
|
||||
e->sl_config_len = len;
|
||||
e->sl_config = (uint8_t *)safemalloc(len);
|
||||
if (memio->read(e->sl_config, len) != len)
|
||||
throw exception();
|
||||
mxverb(2, PFX "esds: sync layer config descriptor, len: %u\n", len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void qtmp4_reader_c::create_packetizers() {
|
||||
uint32_t i;
|
||||
qtmp4_demuxer_t *dmx;
|
||||
@ -850,7 +985,8 @@ void qtmp4_reader_c::create_packetizers() {
|
||||
|
||||
dmx->packetizer->duplicate_data_on_add(false);
|
||||
|
||||
mxinfo("+-> Using the video packetizer for track %u.\n", dmx->id);
|
||||
mxinfo("+-> Using the video packetizer for track %u (FourCC: %.4s).\n",
|
||||
dmx->id, dmx->fourcc);
|
||||
|
||||
} else {
|
||||
if (!strncasecmp(dmx->fourcc, "QDMC", 4) ||
|
||||
@ -869,6 +1005,28 @@ void qtmp4_reader_c::create_packetizers() {
|
||||
mxinfo("+-> Using generic audio output module for stream "
|
||||
"%u (FourCC: %.4s).\n", dmx->id, dmx->fourcc);
|
||||
|
||||
} else if (!strncasecmp(dmx->fourcc, "MP4A", 4)) {
|
||||
int profile, sample_rate_idx, channels;
|
||||
|
||||
if (dmx->a_esds.decoder_config_len == 2) {
|
||||
profile = (dmx->a_esds.decoder_config[0] >> 3) - 1;
|
||||
sample_rate_idx = ((dmx->a_esds.decoder_config[0] & 0x07) << 1) |
|
||||
(dmx->a_esds.decoder_config[1] >> 7);
|
||||
channels = (dmx->a_esds.decoder_config[1] & 0x7f) >> 3;
|
||||
|
||||
mxverb(2, PFX "AAC: profile: %d, sample_rate_idx: %d, channels: "
|
||||
"%d\n", profile, sample_rate_idx, channels);
|
||||
|
||||
dmx->packetizer = new aac_packetizer_c(this, AAC_ID_MPEG4, profile,
|
||||
(uint32_t)dmx->a_samplerate,
|
||||
channels, ti, false, true);
|
||||
if (verbose)
|
||||
mxinfo("+-> Using AAC output module for stream %u.\n", dmx->id,
|
||||
dmx->fourcc);
|
||||
} else
|
||||
mxerror(PFX "AAC found, but decoder config data has length %u.\n",
|
||||
dmx->a_esds.decoder_config_len);
|
||||
|
||||
} else
|
||||
die(PFX "Should not have happened #1.");
|
||||
}
|
||||
|
@ -102,6 +102,8 @@ typedef struct {
|
||||
unsigned char *a_priv;
|
||||
uint32_t a_priv_size;
|
||||
sound_v1_stsd_atom_t a_stsd;
|
||||
esds_t a_esds;
|
||||
bool a_esds_parsed;
|
||||
|
||||
bool warning_printed;
|
||||
|
||||
@ -137,8 +139,8 @@ protected:
|
||||
virtual void read_atom(uint32_t &atom, uint64_t &size, uint64_t &pos,
|
||||
uint32_t &hsize);
|
||||
virtual void free_demuxer(qtmp4_demuxer_t *dmx);
|
||||
// virtual real_demuxer_t *find_demuxer(int id);
|
||||
// virtual int finish();
|
||||
virtual bool parse_esds_atom(mm_mem_io_c *memio, qtmp4_demuxer_t *dmx);
|
||||
virtual uint32_t read_esds_descr_len(mm_mem_io_c *memio);
|
||||
};
|
||||
|
||||
#endif // __R_MP4_H
|
||||
|
Loading…
Reference in New Issue
Block a user