Reworked the packet queueing if timecode files are involved. First, do not queue anything if no timecode file has been specified and don't create a factory in that case either. Second, queue only as many frames as are needed. This depends on the track type and codec used. Subtitle tracks do not need any queueing. Neither do most audio tracks with the exception of RealAudio. Most normal B frame video codecs like MPEG-4 part 2, MPEG-1/-2, RealVideo etc only need short queueing while AVC and all unknown video codecs should be fully queued.

This commit is contained in:
Moritz Bunkus 2005-08-06 11:52:14 +00:00
parent 2f9f2801e7
commit 15f47f2741
8 changed files with 151 additions and 28 deletions

View File

@ -75,6 +75,7 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader,
default_track_warning_printed = false;
connected_to = 0;
has_been_flushed = false;
timecode_factory_application_mode = TFA_AUTOMATIC;
// Let's see if the user specified audio sync for this track.
found = false;
@ -278,6 +279,10 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader,
generic_packetizer_c::~generic_packetizer_c() {
safefree(hcodec_private);
if (!packet_queue.empty())
mxerror("Packet queue not empty for new track ID %lld (flushed: %d). "
"Frames have been lost during remux. %s\n", ti.id,
has_been_flushed, BUGMSG);
}
void
@ -338,7 +343,8 @@ generic_packetizer_c::set_uid(uint32_t uid) {
}
void
generic_packetizer_c::set_track_type(int type) {
generic_packetizer_c::set_track_type(int type,
timecode_factory_application_e tfa_mode) {
htrack_type = type;
if ((type == track_audio) && (ti.cues == CUE_STRATEGY_UNSPECIFIED))
@ -351,6 +357,16 @@ generic_packetizer_c::set_track_type(int type) {
video_packetizer = this;
} else
reader->num_subtitle_tracks++;
if ((TFA_AUTOMATIC == tfa_mode) &&
(TFA_AUTOMATIC == timecode_factory_application_mode))
timecode_factory_application_mode =
(track_video == type) ? TFA_FULL_QUEUEING :
(track_subtitle == type) ? TFA_IMMEDIATE :
(track_buttons == type) ? TFA_IMMEDIATE :
TFA_FULL_QUEUEING;
else if (TFA_AUTOMATIC != tfa_mode)
timecode_factory_application_mode = tfa_mode;
}
void
@ -625,8 +641,9 @@ generic_packetizer_c::set_headers() {
(&GetChild<KaxMaxBlockAdditionID>(*track_entry))) =
htrack_max_add_block_ids;
htrack_default_duration =
(int64_t)timecode_factory->get_default_duration(htrack_default_duration);
if (NULL != timecode_factory)
htrack_default_duration =
(int64_t)timecode_factory->get_default_duration(htrack_default_duration);
if (htrack_default_duration != -1.0)
*(static_cast<EbmlUInteger *>
(&GetChild<KaxTrackDefaultDuration>(*track_entry))) =
@ -927,7 +944,11 @@ generic_packetizer_c::add_packet2(packet_cptr pack) {
pack->timecode_before_factory = pack->timecode;
packet_queue.push_back(pack);
apply_factory();
if ((NULL == timecode_factory) ||
(TFA_IMMEDIATE == timecode_factory_application_mode))
apply_factory_once(pack);
else
apply_factory();
}
void
@ -956,7 +977,7 @@ generic_packetizer_c::has_enough_packets() const {
packet_cptr
generic_packetizer_c::get_packet() {
if (packet_queue.size() == 0)
if ((packet_queue.size() == 0) || !packet_queue.front()->factory_applied)
return packet_cptr(NULL);
packet_cptr pack = packet_queue.front();
@ -969,9 +990,17 @@ generic_packetizer_c::get_packet() {
void
generic_packetizer_c::apply_factory_once(packet_cptr &packet) {
packet->gap_following = timecode_factory->get_next(packet);
if (NULL == timecode_factory) {
packet->assigned_timecode = packet->timecode;
packet->gap_following = false;
} else
packet->gap_following = timecode_factory->get_next(packet);
packet->factory_applied = true;
mxverb(4, "apply_factory_once(): source %lld t %lld tbf %lld at %lld\n",
packet->source->get_source_track_num(), packet->timecode,
packet->timecode_before_factory, packet->assigned_timecode);
if (max_timecode_seen < (packet->assigned_timecode + packet->duration))
max_timecode_seen = packet->assigned_timecode + packet->duration;
if (reader->max_timecode_seen < max_timecode_seen)
@ -980,20 +1009,30 @@ generic_packetizer_c::apply_factory_once(packet_cptr &packet) {
void
generic_packetizer_c::apply_factory() {
deque<packet_cptr>::iterator p_start, p_end, p_current;
packet_cptr_di p_start;
if (packet_queue.empty())
return;
// Find the first packet to which the factory hasn't been applied yet.
p_start = packet_queue.begin();
while ((packet_queue.end() != p_start) && (*p_start)->factory_applied)
++p_start;
if (packet_queue.end() == p_start)
return;
if (TFA_SHORT_QUEUEING == timecode_factory_application_mode)
apply_factory_short_queueing(p_start);
else
apply_factory_full_queueing(p_start);
}
void
generic_packetizer_c::apply_factory_short_queueing(packet_cptr_di &p_start) {
packet_cptr_di p_end, p_current;
while (packet_queue.end() != p_start) {
while ((packet_queue.end() != p_start) && (*p_start)->factory_applied)
++p_start;
if (packet_queue.end() == p_start)
return;
// Find the next packet with a timecode bigger than the start packet's
// timecode. All packets between those two including the start packet
// and excluding the end packet can be timestamped.
@ -1005,10 +1044,7 @@ generic_packetizer_c::apply_factory() {
// Abort if no such packet was found, but keep on assigning if the
// packetizer has been flushed already.
// Subtitles are a special case. They don't have B frames. But they
// may have big gaps. So apply the factory right now, too.
if (!has_been_flushed && (get_track_type() != track_subtitle) &&
(packet_queue.end() == p_end))
if (!has_been_flushed && (packet_queue.end() == p_end))
return;
// Now assign timecodes to the ones between p_start and p_end...
@ -1017,10 +1053,66 @@ generic_packetizer_c::apply_factory() {
// ...and to p_start itself.
apply_factory_once(*p_start);
mxverb(4, "apply_factory(): source %lld t %lld tbf %lld at %lld\n",
(*p_start)->source->get_source_track_num(),
(*p_start)->timecode, (*p_start)->timecode_before_factory,
(*p_start)->assigned_timecode);
p_start = p_end;
}
}
struct packet_sorter_t {
int m_index;
static deque<packet_cptr> *m_packet_queue;
packet_sorter_t(int index):
m_index(index) {}
bool operator <(const packet_sorter_t &cmp) const {
return (*m_packet_queue)[m_index]->timecode <
(*m_packet_queue)[cmp.m_index]->timecode;
}
};
deque<packet_cptr> *packet_sorter_t::m_packet_queue = NULL;
void
generic_packetizer_c::apply_factory_full_queueing(packet_cptr_di &p_start) {
packet_cptr_di p_end, p_current;
vector<packet_sorter_t> sorter;
bool needs_sorting;
int64_t previous_timecode;
int i;
packet_sorter_t::m_packet_queue = &packet_queue;
while (packet_queue.end() != p_start) {
// Find the next I frame packet.
p_end = p_start + 1;
while ((packet_queue.end() != p_end) &&
((0 <= (*p_end)->fref) || (0 <= (*p_end)->bref)))
++p_end;
// Abort if no such packet was found, but keep on assigning if the
// packetizer has been flushed already.
if (!has_been_flushed && (packet_queue.end() == p_end))
return;
// Now sort the frames by their timecode as the factory has to be
// applied to the packets in the same order as they're timestamped.
sorter.clear();
needs_sorting = false;
previous_timecode = 0;
i = distance(packet_queue.begin(), p_start);
for (p_current = p_start; p_current != p_end; ++i, ++p_current) {
sorter.push_back(packet_sorter_t(i));
if (packet_queue[i]->timecode < previous_timecode)
needs_sorting = true;
previous_timecode = packet_queue[i]->timecode;
}
if (needs_sorting)
sort(sorter.begin(), sorter.end());
// Finally apply the factory.
for (i = 0; sorter.size() > i; ++i)
apply_factory_once(packet_queue[sorter[i].m_index]);
p_start = p_end;
}

View File

@ -374,9 +374,11 @@ enum connection_result_e {
return CAN_CONNECT_NO_PARAMETERS; \
}
typedef deque<packet_cptr>::iterator packet_cptr_di;
class generic_packetizer_c {
protected:
deque<packet_cptr > packet_queue, deferred_packets;
deque<packet_cptr> packet_queue, deferred_packets;
int64_t initial_displacement;
int64_t m_free_refs, m_next_free_refs, enqueued_bytes;
@ -407,6 +409,7 @@ protected:
compressor_ptr compressor;
timecode_factory_c *timecode_factory;
timecode_factory_application_e timecode_factory_application_mode;
int64_t last_cue_timecode;
@ -493,7 +496,9 @@ public:
virtual int get_uid() {
return huid;
}
virtual void set_track_type(int type);
virtual void set_track_type(int type,
timecode_factory_application_e tfa_mode =
TFA_AUTOMATIC);
virtual int get_track_type() {
return htrack_type;
}
@ -568,6 +573,8 @@ public:
virtual void apply_factory();
virtual void apply_factory_once(packet_cptr &packet);
virtual void apply_factory_short_queueing(packet_cptr_di &p_start);
virtual void apply_factory_full_queueing(packet_cptr_di &p_start);
protected:
inline bool has_enough_packets() const;

View File

@ -33,7 +33,7 @@ timecode_factory_c::create(const string &_file_name,
timecode_factory_c *factory;
if (_file_name == "")
return new timecode_factory_c("", _source_name, _tid);
return NULL;
in = NULL; // avoid gcc warning
try {
@ -270,6 +270,13 @@ timecode_factory_v2_c::get_next(packet_cptr &packet) {
"the way you intended them to be. mkvmerge might even crash.\n",
source_name.c_str(), tid, timecodes.size());
warning_printed = true;
if (timecodes.empty()) {
packet->assigned_timecode = 0;
packet->duration = 0;
} else {
packet->assigned_timecode = timecodes.back();
packet->duration = timecodes.back();
}
return false;
}

View File

@ -27,6 +27,13 @@ using namespace std;
class mm_io_c;
enum timecode_factory_application_e {
TFA_AUTOMATIC,
TFA_IMMEDIATE,
TFA_SHORT_QUEUEING,
TFA_FULL_QUEUEING
};
class timecode_range_c {
public:
int64_t start_frame, end_frame;

View File

@ -30,6 +30,8 @@ passthrough_packetizer_c::passthrough_packetizer_c(generic_reader_c *_reader,
throw (error_c):
generic_packetizer_c(_reader, _ti),
packets_processed(0), bytes_processed(0), sync_to_keyframe(false) {
timecode_factory_application_mode = TFA_FULL_QUEUEING;
}
void

View File

@ -47,7 +47,7 @@ ra_packetizer_c::ra_packetizer_c(generic_reader_c *_reader,
initial_displacement = 0;
}
set_track_type(track_audio);
set_track_type(track_audio, TFA_SHORT_QUEUEING);
}
ra_packetizer_c::~ra_packetizer_c() {

View File

@ -172,6 +172,8 @@ mpeg1_2_video_packetizer_c(generic_reader_c *_reader,
}
} else
aspect_ratio_extracted = true;
timecode_factory_application_mode = TFA_SHORT_QUEUEING;
}
int
@ -185,8 +187,12 @@ mpeg1_2_video_packetizer_c::process(packet_cptr packet) {
if (!aspect_ratio_extracted)
extract_aspect_ratio(packet->memory->data, packet->memory->size);
if (framed)
return video_packetizer_c::process(packet);
if (framed) {
if (0 != packet->memory->size)
return video_packetizer_c::process(packet);
else
return FILE_STATUS_MOREDATA;
}
state = parser.GetState();
if ((state == MPV_PARSER_STATE_EOS) ||
@ -321,6 +327,8 @@ mpeg4_p2_video_packetizer_c(generic_reader_c *_reader,
ti.private_size = 0;
}
}
timecode_factory_application_mode = TFA_SHORT_QUEUEING;
}
int

View File

@ -7,7 +7,7 @@ T_006oggflac:afe650ed8d490f21282eb6bfe01ba69a:passed:20040825-175700
T_007oggvorbis:1f05b22929f01ce695b2ac8c40769c6e:passed:20040825-175700
T_008avi_divx3_mp3:07c04723d50eec6ca1b5f97b9980c47f:passed:20040825-175700
T_009realvideo_3:38b56ad040e532fe086f484a3521dead:passed:20040825-175700
T_010realvideo_4:dbdb10df5958c54c2ba273de6a54fe51:passed:20040825-175700
T_010realvideo_4:1ff5c2a82835c53568f3b06ae239e721:passed:20040825-175700
T_011srt:202f13d4200fa1e4ea758e4fa9a19d85:passed:20040825-175700
T_012ssa:16103cb04eef9fe7fbbe029c17052ecb:passed:20040825-175700
T_013vobsubs:b5f2200404d4501859c92dcf78cf3a89:passed:20040825-175700