Reworked the audio synchronization code which was not working for Matroska source files.

This commit is contained in:
Moritz Bunkus 2003-11-15 15:29:40 +00:00
parent 739580d885
commit 89b68ad397
15 changed files with 160 additions and 111 deletions

View File

@ -1,3 +1,8 @@
2003-11-15 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: bug fix: Reworked the audio sychronization which did
not work correctly for Matroska source files.
2003-11-13 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: bug fix: Increased the size of the space reserved for

View File

@ -593,8 +593,6 @@ int cluster_helper_c::render() {
return 1;
}
#define iabs(a) ((a) < 0 ? (-1 * (a)) : (a))
ch_contents_t *cluster_helper_c::find_packet_cluster(int64_t ref_timecode,
void *source) {
int i, k;

View File

@ -64,6 +64,8 @@ using namespace libebml;
#define TYPEREAL 14
#define TYPEQTMP4 15
#define iabs(a) ((a) < 0 ? (-1 * (a)) : (a))
#define FOURCC(a, b, c, d) (uint32_t)((((unsigned char)a) << 24) + \
(((unsigned char)b) << 16) + \
(((unsigned char)c) << 8) + \

View File

@ -74,21 +74,17 @@ unsigned char *aac_packetizer_c::get_aac_packet(unsigned long *header,
if ((pos + aacheader->bytes) > size)
return NULL;
pims = ((double)aacheader->bytes) * 1000.0 /
((double)aacheader->bit_rate / 8.0);
pims = 1024000.0 / samples_per_sec;
if (ti->async.displacement < 0) {
if (needs_negative_displacement(pims)) {
/*
* AAC audio synchronization. displacement < 0 means skipping an
* appropriate number of packets at the beginning.
*/
ti->async.displacement += (int)pims;
if (ti->async.displacement > -(pims / 2))
ti->async.displacement = 0;
displace(-pims);
byte_buffer.remove(pos + aacheader->bytes);
return 0;
return NULL;
}
if (verbose && (pos > 0))
@ -120,7 +116,7 @@ unsigned char *aac_packetizer_c::get_aac_packet(unsigned long *header,
}
}
if (ti->async.displacement > 0) {
if (needs_positive_displacement(pims)) {
/*
* AAC audio synchronization. displacement > 0 is solved by duplicating
* the very first AAC packet as often as necessary. I cannot create
@ -128,9 +124,8 @@ unsigned char *aac_packetizer_c::get_aac_packet(unsigned long *header,
* settings the packet's values to 0 does not work as the AAC header
* contains a CRC of its data.
*/
ti->async.displacement -= (int)pims;
if (ti->async.displacement < (pims / 2))
ti->async.displacement = 0;
mxinfo("displacing for %f\n", pims);
displace(pims);
return buf;
}
@ -177,7 +172,7 @@ int aac_packetizer_c::process(unsigned char *buf, int size,
unsigned char *packet;
unsigned long header;
aac_header_t aacheader;
int64_t my_timecode;
int64_t my_timecode, duration;
debug_enter("aac_packetizer_c::process");
@ -185,25 +180,34 @@ int aac_packetizer_c::process(unsigned char *buf, int size,
if (timecode != -1)
my_timecode = timecode;
else
my_timecode = (int64_t)(1000.0 * packetno * 1024 * ti->async.linear /
samples_per_sec);
add_packet(buf, size, my_timecode,
(int64_t)(1000.0 * 1024 * ti->async.linear / samples_per_sec));
die("aac_packetizer_c::process: headerless && timecode == -1\n");
duration = (int64_t)(1000.0 * 1024 * ti->async.linear / samples_per_sec);
if (needs_negative_displacement(duration)) {
displace(-duration);
return EMOREDATA;
}
while (needs_positive_displacement(duration)) {
add_packet(buf, size, my_timecode + ti->async.displacement, duration);
displace(duration);
}
my_timecode = (int64_t)((my_timecode + ti->async.displacement) *
ti->async.linear);
add_packet(buf, size, my_timecode, duration);
debug_leave("aac_packetizer_c::process");
return EMOREDATA;
}
if (timecode != -1)
my_timecode = timecode;
byte_buffer.add(buf, size);
while ((packet = get_aac_packet(&header, &aacheader)) != NULL) {
if (timecode == -1)
my_timecode = (int64_t)(1000.0 * packetno * 1024 * ti->async.linear /
samples_per_sec);
my_timecode = (int64_t)(1000.0 * packetno * 1024 / samples_per_sec);
else
my_timecode = timecode + ti->async.displacement;
my_timecode = (int64_t)(my_timecode * ti->async.linear);
add_packet(packet, aacheader.data_byte_size, my_timecode,
(int64_t)(1000.0 * 1024 * ti->async.linear / samples_per_sec));
packetno++;

View File

@ -72,18 +72,14 @@ unsigned char *ac3_packetizer_c::get_ac3_packet(unsigned long *header,
if ((pos + ac3header->bytes) > size)
return NULL;
pims = ((double)ac3header->bytes) * 1000.0 /
((double)ac3header->bit_rate / 8.0);
pims = 1536000.0 / samples_per_sec;
if (ti->async.displacement < 0) {
if (needs_negative_displacement(pims)) {
/*
* AC3 audio synchronization. displacement < 0 means skipping an
* appropriate number of packets at the beginning.
*/
ti->async.displacement += (int)pims;
if (ti->async.displacement > -(pims / 2))
ti->async.displacement = 0;
displace(-pims);
byte_buffer.remove(pos + ac3header->bytes);
return NULL;
@ -98,7 +94,7 @@ unsigned char *ac3_packetizer_c::get_ac3_packet(unsigned long *header,
else
buf = (unsigned char *)safememdup(packet_buffer + pos, ac3header->bytes);
if (ti->async.displacement > 0) {
if (needs_positive_displacement(pims)) {
/*
* AC3 audio synchronization. displacement > 0 is solved by duplicating
* the very first AC3 packet as often as necessary. I cannot create
@ -106,10 +102,7 @@ unsigned char *ac3_packetizer_c::get_ac3_packet(unsigned long *header,
* settings the packet's values to 0 does not work as the AC3 header
* contains a CRC of its data.
*/
ti->async.displacement -= (int)pims;
if (ti->async.displacement < (pims / 2))
ti->async.displacement = 0;
displace(pims);
return buf;
}
@ -141,15 +134,13 @@ int ac3_packetizer_c::process(unsigned char *buf, int size,
debug_enter("ac3_packetizer_c::process");
if (timecode != -1)
my_timecode = timecode;
add_to_buffer(buf, size);
while ((packet = get_ac3_packet(&header, &ac3header)) != NULL) {
if (timecode == -1)
my_timecode = (int64_t)(1000.0 * packetno * 1536 * ti->async.linear /
samples_per_sec);
my_timecode = (int64_t)(1000.0 * packetno * 1536 / samples_per_sec);
else
my_timecode = timecode + initial_displacement;
my_timecode = (int64_t)(my_timecode * ti->async.linear);
add_packet(packet, ac3header.bytes, my_timecode,
(int64_t)(1000.0 * 1536 * ti->async.linear / samples_per_sec));
packetno++;

View File

@ -168,15 +168,12 @@ unsigned char *dts_packetizer_c::get_dts_packet(dts_header_t &dtsheader) {
pims = get_dts_packet_length_in_milliseconds(&dtsheader);
if (ti->async.displacement < 0) {
if (needs_negative_displacement(pims)) {
/*
* DTS audio synchronization. displacement < 0 means skipping an
* appropriate number of packets at the beginning.
*/
ti->async.displacement += (int)pims;
if (ti->async.displacement > -(pims / 2))
ti->async.displacement = 0;
displace(-pims);
remove_dts_packet(pos, dtsheader.frame_byte_size);
return 0;
@ -190,7 +187,7 @@ unsigned char *dts_packetizer_c::get_dts_packet(dts_header_t &dtsheader) {
buf = (unsigned char *)safememdup(packet_buffer + pos,
dtsheader.frame_byte_size);
if (ti->async.displacement > 0) {
if (needs_positive_displacement(pims)) {
/*
* DTS audio synchronization. displacement > 0 is solved by duplicating
* the very first DTS packet as often as necessary. I cannot create
@ -198,10 +195,7 @@ unsigned char *dts_packetizer_c::get_dts_packet(dts_header_t &dtsheader) {
* settings the packet's values to 0 does not work as the DTS header
* contains a CRC of its data.
*/
ti->async.displacement -= (int)pims;
if (ti->async.displacement < (pims / 2))
ti->async.displacement = 0;
displace(pims);
return buf;
}
@ -237,11 +231,13 @@ int dts_packetizer_c::process(unsigned char *buf, int size,
(int64_t)get_dts_packet_length_in_milliseconds(&dtsheader);
if (timecode == -1)
my_timecode = (int64_t)(((double)samples_written*1000.0) /
my_timecode = (int64_t)(((double)samples_written * 1000.0) /
((double)dtsheader.core_sampling_frequency));
else
my_timecode = timecode + initial_displacement;
my_timecode = (int64_t)(my_timecode * ti->async.linear);
add_packet(packet, dtsheader.frame_byte_size, my_timecode,
packet_len_in_ms);
(int64_t)(packet_len_in_ms * ti->async.linear));
bytes_written += dtsheader.frame_byte_size;
samples_written += get_dts_packet_length_in_core_samples(&dtsheader);

View File

@ -43,10 +43,8 @@ mp3_packetizer_c::mp3_packetizer_c(generic_reader_c *nreader,
spf = 1152;
set_track_type(track_audio);
if (use_durations)
set_track_default_duration_ns((int64_t)(1152000000000.0 *
ti->async.linear /
samples_per_sec));
set_track_default_duration_ns((int64_t)(1152000000000.0 * ti->async.linear /
samples_per_sec));
duplicate_data_on_add(false);
}
@ -84,7 +82,7 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) {
codec_id[codec_id.length() - 1] = (char)(mp3header->layer + '0');
*(static_cast<EbmlString *>
(&GetChild<KaxCodecID>(*track_entry))) = codec_id;
if ((spf != 1152) && use_durations) {
if (spf != 1152) {
set_track_default_duration_ns((int64_t)(1000000000.0 * spf *
ti->async.linear /
samples_per_sec));
@ -100,45 +98,44 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) {
pims = 1000.0 * (float)spf / mp3header->sampling_frequency;
if (ti->async.displacement < 0) {
if (needs_negative_displacement(pims)) {
/*
* MP3 audio synchronization. displacement < 0 means skipping an
* appropriate number of packets at the beginning.
*/
ti->async.displacement += (int)pims;
if (ti->async.displacement > -(pims / 2))
ti->async.displacement = 0;
displace(-pims);
byte_buffer.remove(mp3header->framesize + pos);
return NULL;
}
if ((verbose > 1) && (pos > 1))
mxwarn("mp3_packetizer: skipping %d bytes (no valid MP3 header found).\n",
pos);
if (pos > 0) {
if (verbose)
mxwarn("mp3_packetizer: skipping %d bytes (no valid MP3 header found)."
"\n", pos);
byte_buffer.remove(pos);
pos = 0;
}
if (fast_mode)
buf = (unsigned char *)safemalloc(mp3header->framesize);
else
buf = (unsigned char *)safememdup(byte_buffer.get_buffer() + pos,
buf = (unsigned char *)safememdup(byte_buffer.get_buffer(),
mp3header->framesize);
if (ti->async.displacement > 0) {
if (needs_positive_displacement(pims)) {
/*
* MP3 audio synchronization. displacement > 0 is solved by creating
* silent MP3 packets and repeating it over and over again (well only as
* often as necessary of course. Wouldn't want to spoil your movie by
* providing a silent MP3 stream ;)).
*/
ti->async.displacement -= (int)pims;
if (ti->async.displacement < (pims / 2))
ti->async.displacement = 0;
displace(pims);
memset(buf + 4, 0, mp3header->framesize - 4);
return buf;
}
byte_buffer.remove(mp3header->framesize + pos);
byte_buffer.remove(mp3header->framesize);
return buf;
}
@ -155,28 +152,24 @@ int mp3_packetizer_c::process(unsigned char *buf, int size,
int64_t timecode, int64_t, int64_t, int64_t) {
unsigned char *packet;
mp3_header_t mp3header;
int64_t my_timecode, old_packetno;
int64_t my_timecode;
debug_enter("mp3_packetizer_c::process");
if (timecode != -1)
my_timecode = timecode;
byte_buffer.add(buf, size);
while ((packet = get_mp3_packet(&mp3header)) != NULL) {
old_packetno = packetno;
packetno++;
#ifdef DEBUG
dump_packet(packet, mp3header.framesize);
#endif
if (timecode == -1)
my_timecode = (int64_t)(1000.0 * old_packetno * spf * ti->async.linear /
samples_per_sec);
my_timecode = (int64_t)(1000.0 * packetno * spf / samples_per_sec);
else
my_timecode = timecode + ti->async.displacement;
my_timecode = (int64_t)(my_timecode * ti->async.linear);
add_packet(packet, mp3header.framesize, my_timecode,
(int64_t)(1000.0 * spf * ti->async.linear / samples_per_sec));
packetno++;
}
debug_leave("mp3_packetizer_c::process");

View File

@ -50,7 +50,25 @@ int passthrough_packetizer_c::process(unsigned char *buf, int size,
packets_processed++;
bytes_processed += size;
if (needs_negative_displacement(duration)) {
displace(-duration);
sync_to_keyframe = true;
return EMOREDATA;
}
while (needs_positive_displacement(duration)) {
add_packet(buf, size, (int64_t)((timecode + ti->async.displacement) *
ti->async.linear), bref, fref);
displace(duration);
}
if (sync_to_keyframe && (bref != -1)) {
if (!duplicate_data)
safefree(buf);
return EMOREDATA;
}
sync_to_keyframe = false;
timecode = (int64_t)((timecode + ti->async.displacement) * ti->async.linear);
duration = (int64_t)(duration * ti->async.linear);
add_packet(buf, size, timecode, duration, false, bref, fref);
debug_leave("passthrough_packetizer_c::process");

View File

@ -29,6 +29,7 @@
class passthrough_packetizer_c: public generic_packetizer_c {
private:
int64_t packets_processed, bytes_processed;
bool sync_to_keyframe;
public:
passthrough_packetizer_c(generic_reader_c *nreader, track_info_t *nti)

View File

@ -85,20 +85,20 @@ int pcm_packetizer_c::process(unsigned char *buf, int size,
new_buf = buf;
if (ti->async.displacement != 0) {
if (ti->async.displacement > 0) {
if (initial_displacement != 0) {
if (initial_displacement > 0) {
// Add silence.
int pad_size;
pad_size = bps * ti->async.displacement / 1000;
pad_size = bps * initial_displacement / 1000;
new_buf = (unsigned char *)safemalloc(size + pad_size);
memset(new_buf, 0, pad_size);
memcpy(&new_buf[pad_size], buf, size);
size += pad_size;
} else
// Skip bytes.
remaining_sync = -1 * bps * ti->async.displacement / 1000;
ti->async.displacement = 0;
remaining_sync = -1 * bps * initial_displacement / 1000;
initial_displacement = 0;
}
if (remaining_sync > 0) {

View File

@ -75,9 +75,9 @@ int textsubs_packetizer_c::process(unsigned char *_subs, int, int64_t start,
end = start + length;
// Adjust the start and end values according to the audio adjustment.
start += ti->async.displacement;
start += initial_displacement;
start = (int64_t)(ti->async.linear * start);
end += ti->async.displacement;
end += initial_displacement;
end = (int64_t)(ti->async.linear * end);
if (end < 0)

View File

@ -72,10 +72,10 @@ void vobsub_packetizer_c::set_headers() {
track_entry->EnableLacing(false);
}
#define TIMECODE (timecode - ti->async.displacement) / 60 / 60 / 1000, \
((timecode - ti->async.displacement) / 60 / 1000) % 60, \
((timecode - ti->async.displacement) / 1000) % 60, \
(timecode - ti->async.displacement) % 1000
#define TIMECODE (timecode - initial_displacement) / 60 / 60 / 1000, \
((timecode - initial_displacement) / 60 / 1000) % 60, \
((timecode - initial_displacement) / 1000) % 60, \
(timecode - initial_displacement) % 1000
#define FMT_TIMECODE "%02lld:%02lld:%02lld.%03lld"
int vobsub_packetizer_c::extract_duration(unsigned char *data, int buf_size,
@ -156,7 +156,7 @@ int vobsub_packetizer_c::process(unsigned char *srcbuf, int size,
packet_num++;
timecode += ti->async.displacement;
timecode += initial_displacement;
if (timecode < 0)
return EMOREDATA;

View File

@ -71,6 +71,7 @@ vorbis_packetizer_c::vorbis_packetizer_c(generic_reader_c *nreader,
if (use_durations)
set_track_default_duration_ns((int64_t)(1024000000000.0 *
ti->async.linear / vi.rate));
ti->async.displacement = initial_displacement;
}
vorbis_packetizer_c::~vorbis_packetizer_c() {
@ -142,12 +143,8 @@ int vorbis_packetizer_c::process(unsigned char *data, int size,
debug_enter("vorbis_packetizer_c::process");
// Recalculate the timecode if needed.
if (timecode == -1)
timecode = samples * 1000 / vi.rate;
// Positive displacement, first packet? Well then lets create silence.
if ((packetno == 0) && (ti->async.displacement > 0)) {
if ((packetno == 0) && (initial_displacement > 0)) {
// Create a fake packet so we can use vorbis_packet_blocksize().
zero[0] = 0;
zero[1] = 0;
@ -156,7 +153,7 @@ int vorbis_packetizer_c::process(unsigned char *data, int size,
op.bytes = 2;
// Calculate how many samples we have to create.
samples_needed = vi.rate * ti->async.displacement / 1000;
samples_needed = vi.rate * initial_displacement / 1000;
this_bs = vorbis_packet_blocksize(&vi, &op);
samples_here = (this_bs + last_bs) / 4;
@ -167,8 +164,6 @@ int vorbis_packetizer_c::process(unsigned char *data, int size,
add_packet(zero, 2, samples * 1000 / vi.rate, samples_here * 1000 /
vi.rate);
}
ti->async.displacement = 0;
}
// Update the number of samples we have processed so that we can
@ -180,8 +175,14 @@ int vorbis_packetizer_c::process(unsigned char *data, int size,
samples += samples_here;
last_bs = this_bs;
// Handle the displacement.
timecode += ti->async.displacement;
// Recalculate the timecode if needed.
if (timecode == -1) {
if (initial_displacement > 0)
timecode = samples * 1000 / vi.rate;
else
timecode = samples * 1000 / vi.rate + initial_displacement;
} else
timecode += initial_displacement;
// Handle the linear sync - simply multiply with the given factor.
timecode = (int64_t)((double)timecode * ti->async.linear);

View File

@ -51,6 +51,7 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader,
ti = duplicate_track_info(nti);
free_refs = -1;
enqueued_bytes = 0;
safety_last_timecode = 0;
// Let's see if the user specified audio sync for this track.
found = false;
@ -66,6 +67,8 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader,
ti->async.linear = 1.0;
ti->async.displacement = 0;
}
initial_displacement = ti->async.displacement;
ti->async.displacement = 0;
// Let's see if the user has specified which cues he wants for this track.
ti->cues = CUES_UNSPECIFIED;
@ -598,9 +601,14 @@ void generic_packetizer_c::add_packet(unsigned char *data, int length,
if (data == NULL)
return;
if (timecode < 0)
die("pr_generic.cpp/generic_packetizer_c::add_packet(): timecode < 0 "
"(%lld)", timecode);
if (timecode < 0) {
drop_packet(data);
return;
}
if (timecode < safety_last_timecode)
die("pr_generic.cpp/generic_packetizer_c::add_packet(): timecode < "
"last_timecode (%lld < %lld)", timecode, safety_last_timecode);
safety_last_timecode = timecode;
pack = (packet_t *)safemalloc(sizeof(packet_t));
memset(pack, 0, sizeof(packet_t));
@ -631,6 +639,11 @@ void generic_packetizer_c::add_packet(unsigned char *data, int length,
enqueued_bytes += pack->length;
}
void generic_packetizer_c::drop_packet(unsigned char *data) {
if (!duplicate_data)
safefree(data);
}
packet_t *generic_packetizer_c::get_packet() {
packet_t *pack;
@ -842,6 +855,27 @@ int64_t generic_packetizer_c::get_next_timecode(int64_t timecode) {
return new_timecode;
}
bool generic_packetizer_c::needs_negative_displacement(float duration) {
return ((initial_displacement < 0) &&
(ti->async.displacement > initial_displacement));
}
bool generic_packetizer_c::needs_positive_displacement(float duration) {
return ((initial_displacement > 0) &&
(iabs(ti->async.displacement - initial_displacement) >
(duration / 2)));
}
void generic_packetizer_c::displace(float by_ms) {
ti->async.displacement += (int64_t)by_ms;
if (initial_displacement < 0) {
if (ti->async.displacement < initial_displacement)
initial_displacement = 0;
} else if (iabs(initial_displacement - ti->async.displacement) <
(by_ms / 2))
initial_displacement = 0;
}
//--------------------------------------------------------------------
generic_reader_c::generic_reader_c(track_info_t *nti) {

View File

@ -145,7 +145,8 @@ protected:
bool duplicate_data;
track_info_t *ti;
int64_t free_refs, enqueued_bytes;
int64_t initial_displacement;
int64_t free_refs, enqueued_bytes, safety_last_timecode;
KaxTrackEntry *track_entry;
@ -189,6 +190,7 @@ public:
int64_t duration, bool duration_mandatory = false,
int64_t bref = -1, int64_t fref = -1,
int ref_priority = -1);
virtual void drop_packet(unsigned char *data);
virtual packet_t *get_packet();
virtual int packet_available();
virtual int64_t get_smallest_timecode();
@ -246,6 +248,10 @@ public:
virtual int64_t get_next_timecode(int64_t timecode);
virtual void parse_ext_timecode_file(const char *name);
virtual bool needs_negative_displacement(float duration);
virtual bool needs_positive_displacement(float duration);
virtual void displace(float by_ms);
protected:
virtual void dump_packet(const void *buffer, int size);
};