Non-native to native MPEG-4 part 2: If the frame queue contains more frames than the timecode queue timecodes then use the FPS and create new ones. If a FPS is not known then issue a warning and drop the frames which do not have a timecode available (at most one frame should have to be dropped anyway).

This commit is contained in:
Moritz Bunkus 2005-06-17 09:37:52 +00:00
parent d809e013f7
commit 0c356f0350
2 changed files with 81 additions and 18 deletions

View File

@ -303,7 +303,7 @@ mpeg4_p2_video_packetizer_c(generic_reader_c *_reader,
bool _input_is_native,
track_info_c &_ti):
video_packetizer_c(_reader, MKV_V_MPEG4_ASP, _fps, _width, _height, _ti),
timecodes_generated(0),
timecodes_generated(0), last_i_p_frame(0), previous_timecode(0),
aspect_ratio_extracted(false), input_is_native(_input_is_native),
output_is_native(hack_engaged(ENGAGE_NATIVE_MPEG4)) {
@ -402,6 +402,8 @@ mpeg4_p2_video_packetizer_c::process_non_native(packet_cptr packet) {
queued_frames.push_back(*frame);
}
previous_timecode = available_timecodes.back();
return FILE_STATUS_MOREDATA;
}
@ -454,7 +456,7 @@ mpeg4_p2_video_packetizer_c::flush_frames_maybe(frame_type_e next_frame) {
if ((FRAME_TYPE_I == next_frame) ||
(FRAME_TYPE_P == queued_frames[0].type)) {
flush_frames();
flush_frames(false);
return;
}
@ -464,29 +466,89 @@ mpeg4_p2_video_packetizer_c::flush_frames_maybe(frame_type_e next_frame) {
++num_bframes;
if ((0 < num_bframes) || (2 <= queued_frames.size()))
flush_frames();
flush_frames(false);
}
void
mpeg4_p2_video_packetizer_c::flush_frames() {
int i, num_bframes, b_offset;
int64_t b_bref, b_fref;
/** \brief Handle frame sequences in which too few timecodes are available
if ((available_timecodes.size() < queued_frames.size()) ||
(available_durations.size() < queued_frames.size())) {
This function gets called if mkvmerge wants to flush its frame queue
but it doesn't have enough timecodes and/or durations available for
each queued frame. This can happen in two cases:
1. A picture sequence is found that mkvmerge does not support. An
example: Two frames have been read. The first contained a
P and a B frame (that's OK so far), but the second one contained
another P or I frame without an intermediate dummy frame.
2. The end of the file has been reached but the frame queue contains
more frames than the timecode queue. For example: The last frame
contained two frames, a P and a B frame. Right afterwards the
end of the file is reached. In this case a dummy frame is missing.
Both cases can be solved if the source file provides a FPS for this
track. Otherwise such frames have to be dropped.
*/
void
mpeg4_p2_video_packetizer_c::handle_missing_timecodes(bool end_of_file) {
if (0.0 < fps) {
while (available_timecodes.size() < queued_frames.size()) {
previous_timecode = (int64_t)(previous_timecode + 1000000000.0 / fps);
available_timecodes.push_back(previous_timecode);
mxverb(3, "mpeg4_p2::flush_frames(): Needed new timecode %lld\n",
previous_timecode);
}
while (available_durations.size() < queued_frames.size()) {
available_durations.push_back((int64_t)(1000000000.0 / fps));
mxverb(3, "mpeg4_p2::flush_frames(): Needed new duration %lld\n",
available_durations.back());
}
} else {
int64_t timecode;
int num_dropped, i;
if (available_timecodes.size() < available_durations.size()) {
num_dropped = queued_frames.size() - available_timecodes.size();
available_durations.erase(available_durations.begin() +
available_timecodes.size(),
available_durations.end());
} else {
num_dropped = queued_frames.size() - available_durations.size();
available_timecodes.erase(available_timecodes.begin() +
available_durations.size(),
available_timecodes.end());
}
for (i = available_timecodes.size(); queued_frames.size() > i; ++i)
safefree(queued_frames[i].data);
queued_frames.erase(queued_frames.begin() + available_timecodes.size(),
queued_frames.end());
if (available_timecodes.empty())
timecode = 0;
else
timecode = available_timecodes[0];
timecode = available_timecodes.front();
mxerror("Invalid/unsupported sequence of MPEG4 video frames regarding "
"B frames. If your video plays normally around timecode "
FMT_TIMECODE " then this is a bug in mkvmerge and you should "
"contact the author Moritz Bunkus <moritz@bunkus.org>.\n",
mxwarn(FMT_TID "During MPEG-4 part 2 B frame handling: The frame queue "
"contains more frames than timecodes are available that "
"can be assigned to them because %s. Therefore %d frame%s to be "
"dropped. The video might be broken around timecode "
FMT_TIMECODE ".\n", ti.fname.c_str(), (int64_t)ti.id,
end_of_file ? "the end of the source file was encountered" :
"an unsupported sequence of frames was encountered",
num_dropped, 1 == num_dropped ? " has" : "s have",
ARG_TIMECODE_NS(timecode));
}
}
void
mpeg4_p2_video_packetizer_c::flush_frames(bool end_of_file) {
int i, num_bframes, b_offset;
int64_t b_bref, b_fref;
if ((available_timecodes.size() < queued_frames.size()) ||
(available_durations.size() < queued_frames.size()))
handle_missing_timecodes(end_of_file);
if ((2 <= queued_frames.size()) && (FRAME_TYPE_B != queued_frames[1].type))
b_offset = 1;
@ -546,7 +608,7 @@ mpeg4_p2_video_packetizer_c::flush_frames() {
void
mpeg4_p2_video_packetizer_c::flush() {
flush_frames();
flush_frames(true);
generic_packetizer_c::flush();
}

View File

@ -78,7 +78,7 @@ class mpeg4_p2_video_packetizer_c: public video_packetizer_c {
protected:
deque<video_frame_t> queued_frames;
deque<int64_t> available_timecodes, available_durations;
int64_t timecodes_generated, last_i_p_frame;
int64_t timecodes_generated, last_i_p_frame, previous_timecode;
bool aspect_ratio_extracted, input_is_native, output_is_native;
public:
@ -93,9 +93,10 @@ protected:
virtual int process_native(packet_cptr packet);
virtual int process_non_native(packet_cptr packet);
virtual void flush_frames_maybe(frame_type_e next_frame);
virtual void flush_frames();
virtual void flush_frames(bool end_of_file);
virtual void extract_aspect_ratio(const unsigned char *buffer, int size);
virtual void fix_codec_string();
virtual void handle_missing_timecodes(bool end_of_file);
};
class mpeg4_p10_video_packetizer_c: public video_packetizer_c {