From 15f47f2741f267a045ea0ff45ef491ed290cd9c0 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sat, 6 Aug 2005 11:52:14 +0000 Subject: [PATCH] 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. --- src/merge/pr_generic.cpp | 134 +++++++++++++++++++++++++++------ src/merge/pr_generic.h | 11 ++- src/merge/timecode_factory.cpp | 9 ++- src/merge/timecode_factory.h | 7 ++ src/output/p_passthrough.cpp | 2 + src/output/p_realaudio.cpp | 2 +- src/output/p_video.cpp | 12 ++- tests/results.txt | 2 +- 8 files changed, 151 insertions(+), 28 deletions(-) diff --git a/src/merge/pr_generic.cpp b/src/merge/pr_generic.cpp index 1bd8e7155..9f2789903 100644 --- a/src/merge/pr_generic.cpp +++ b/src/merge/pr_generic.cpp @@ -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(*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 (&GetChild(*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::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 *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_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 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; } diff --git a/src/merge/pr_generic.h b/src/merge/pr_generic.h index 9807d0b9a..582354003 100644 --- a/src/merge/pr_generic.h +++ b/src/merge/pr_generic.h @@ -374,9 +374,11 @@ enum connection_result_e { return CAN_CONNECT_NO_PARAMETERS; \ } +typedef deque::iterator packet_cptr_di; + class generic_packetizer_c { protected: - deque packet_queue, deferred_packets; + deque 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; diff --git a/src/merge/timecode_factory.cpp b/src/merge/timecode_factory.cpp index 2a72ea00a..ada61ecd5 100644 --- a/src/merge/timecode_factory.cpp +++ b/src/merge/timecode_factory.cpp @@ -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; } diff --git a/src/merge/timecode_factory.h b/src/merge/timecode_factory.h index f29a447a5..3b327d257 100644 --- a/src/merge/timecode_factory.h +++ b/src/merge/timecode_factory.h @@ -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; diff --git a/src/output/p_passthrough.cpp b/src/output/p_passthrough.cpp index db7af4299..9e77b05fc 100644 --- a/src/output/p_passthrough.cpp +++ b/src/output/p_passthrough.cpp @@ -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 diff --git a/src/output/p_realaudio.cpp b/src/output/p_realaudio.cpp index eaa9a1180..25dbbd6c5 100644 --- a/src/output/p_realaudio.cpp +++ b/src/output/p_realaudio.cpp @@ -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() { diff --git a/src/output/p_video.cpp b/src/output/p_video.cpp index 84ced4abc..afe46e37e 100644 --- a/src/output/p_video.cpp +++ b/src/output/p_video.cpp @@ -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 diff --git a/tests/results.txt b/tests/results.txt index aa20f9c16..2480362c6 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -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