From 8696db7e11280544c6a8c19ddb16223145d05854 Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Sat, 11 Apr 2015 14:33:45 +0200 Subject: [PATCH] MPEG-2 parser: re-write timestamping code not to rely on exact sequence number progression The old code was calculating an expected sequence number. As soon as that sequence number was matched with frames, all previous frames would be timestamped. This method is problematic if the actual sequence numbers don't follow the expected progression. The new code is much simpler. As soon as an I frame is encountered (or at the end of the file) the queued frames are first sorted by their sequence number, then timestamped, and lastly sorted back into their decoding order. This fixes #1162 and most likely others (see #1145, #1099 et. al.). --- ChangeLog | 6 + src/input/r_mpeg_ps.cpp | 19 ++- src/mpegparser/M2VParser.cpp | 114 ++++++------------ src/mpegparser/M2VParser.h | 7 +- tests/results.txt | 11 +- ...t-490sequence_numbers_no_0_in_first_gop.rb | 5 + 6 files changed, 69 insertions(+), 93 deletions(-) create mode 100755 tests/test-490sequence_numbers_no_0_in_first_gop.rb diff --git a/ChangeLog b/ChangeLog index 1b7c58ed0..61f3cd7f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2015-04-11 Moritz Bunkus + * mkvmerge: bug fix: MPEG-1/2 parser: fixed a long-standing issue + that prevented mkvmerge from recognizing certain MPEG-1/2 video + tracks and files if the frame's sequence numbers didn't follow a + certain expected pattern. Fixes #1162 and probably others like + #1145 or #1099. + * MKVToolNix GUI: merge tool enhancement: Implemented adding and append files and adding files as additional parts via drag & drop from external applications. diff --git a/src/input/r_mpeg_ps.cpp b/src/input/r_mpeg_ps.cpp index f7fe4e593..df6a8cbff 100644 --- a/src/input/r_mpeg_ps.cpp +++ b/src/input/r_mpeg_ps.cpp @@ -602,19 +602,26 @@ mpeg_ps_reader_c::new_stream_v_mpeg_1_2(mpeg_ps_id_t id, bool found_i_frame = false; bool found_non_b_frame = false; + bool flushed = false; MPEG2SequenceHeader seq_hdr; while ( (MPV_PARSER_STATE_EOS != state) && (MPV_PARSER_STATE_ERROR != state) && (PS_PROBE_SIZE >= m_in->getFilePointer())) { - if (!find_next_packet_for_id(id, PS_PROBE_SIZE)) + if (find_next_packet_for_id(id, PS_PROBE_SIZE)) { + auto packet = parse_packet(id); + if (!packet) + break; + + m2v_parser->WriteData(packet.m_buffer->get_buffer(), packet.m_length); + + } else if (flushed) break; - auto packet = parse_packet(id); - if (!packet) - break; - - m2v_parser->WriteData(packet.m_buffer->get_buffer(), packet.m_length); + else { + m2v_parser->SetEOS(); + flushed = true; + } state = m2v_parser->GetState(); diff --git a/src/mpegparser/M2VParser.cpp b/src/mpegparser/M2VParser.cpp index 08914742e..ea7ce245d 100644 --- a/src/mpegparser/M2VParser.cpp +++ b/src/mpegparser/M2VParser.cpp @@ -64,6 +64,7 @@ void M2VParser::SetEOS(){ c = mpgBuf->ReadChunk(); if(c) chunks.push_back(c); FillQueues(); + TimestampWaitingFrames(); m_eos = true; } @@ -117,7 +118,6 @@ M2VParser::M2VParser() notReachedFirstGOP = true; previousTimecode = 0; previousDuration = 0; - queueTime = 0; waitExpectedTime = 0; probing = false; b_frame_warning_printed = false; @@ -205,12 +205,15 @@ MPEG2ParserState_e M2VParser::GetState(){ void M2VParser::FlushWaitQueue(){ waitSecondField = false; - while(!waitQueue.empty()){ - delete waitQueue.front(); - waitQueue.pop(); - if (!m_timecodes.empty()) - m_timecodes.pop_front(); + for (auto const &frame : waitQueue) + delete frame; + while (!buffers.empty()) { + delete buffers.front(); + buffers.pop(); } + + waitQueue.clear(); + m_timecodes.clear(); } void M2VParser::StampFrame(MPEGFrame* frame){ @@ -248,7 +251,8 @@ void M2VParser::UpdateFrame(MPEGFrame* frame){ int32_t M2VParser::OrderFrame(MPEGFrame* frame){ MPEGFrame *p = frame; - bool flushQueue = false; + + // mxinfo(boost::format("picStr %1% frame type %2% qt tc %3%\n") % p->timecode % static_cast(p->pictureStructure) % p->frameType); if (waitSecondField && (p->pictureStructure == MPEG2_PICTURE_TYPE_FRAME)){ auto error = Y("Unexpected picture frame after single field frame. Fix the MPEG2 video stream before attempting to multiplex it.\n"); @@ -257,86 +261,38 @@ int32_t M2VParser::OrderFrame(MPEGFrame* frame){ mxerror(error); } - /* While the refs of a frame are set in SetFrameRef, the actual final timecodes are possibly derived later. - * Based on display order, a dependent frame (below: "DEP") can be located after or before a ref frame (below: "REF"). - * This leads to the following behaviour: - * - * ***** case "after" => REF takes branch 1 below ***** - * - REF: ShoveRef/StampFrame set both frame pointer and timecode at M2VParser::refs. - * - DEP: SetFrameRef already derives the timecode. - * - * ***** case "before" => REF takes branch 2 below ***** - * - REF: ShoveRef sets the frame pointer at M2VParser::refs. - * - DEP: SetFrameRef derives that pointer in the first place. - * Later, while queue flushing: - * - REF: StampFrame sets the timecode. - * - DEP: UpdateFrame derives the timecode via frame pointer. - * The queue frames are in decoding order, therefore DEP's UpdateFrame happens before REF's StampFrame. - * In addition, tmpForwardQueue ensures that the pointed to ref frame is still available. - */ SetFrameRef(p); ShoveRef(p); - /* - * Process incoming frames (being in decoding order) and ensure correct stamping (in display order) - output again happens in decoding order. - * - branch 1: not waiting; forward frame, which has consecutive sequence number - * - branch 2: not waiting; queue frame, which has preceding frames (regarding display order) + start waiting for that intermediate frames - * - branch 3: waiting: queue frame (if last intermediate frame, finish waiting by invoking flush) - * - branch 4: waiting; queue frame - sequence number beyond expected range (this can only occur at defective streams!) - */ - if ((p->timecode == queueTime) && waitQueue.empty()) { // branch 1 - if ((p->pictureStructure == MPEG2_PICTURE_TYPE_FRAME) || waitSecondField) - queueTime++; - StampFrame(p); - UpdateFrame(p); - buffers.push(p); - } else if ((p->timecode > queueTime) && waitQueue.empty()) { // branch 2 - waitExpectedTime = p->timecode - 1; - waitQueue.push(p); - } else if (p->timecode <= waitExpectedTime) { // branch 3 - if ((p->timecode == waitExpectedTime) && ((p->pictureStructure == MPEG2_PICTURE_TYPE_FRAME) || waitSecondField)) - flushQueue = true; - StampFrame(p); - waitQueue.push(p); - } else // branch 4 - waitQueue.push(p); + if (('I' == p->frameType) && !waitQueue.empty()) + TimestampWaitingFrames(); - if(flushQueue){ - int i,l; - waitSecondField = false; - - l = waitQueue.size(); - for(i=0;istamped) { - StampFrame(p); - } - UpdateFrame(p); - - // temporarily cache all frames, to ensure that all needed ref frames are still available while UpdateFrame (see above) - tmpForwardQueue.push(p); - } - for (i = 0; i < l; i++) { - p = tmpForwardQueue.front(); - tmpForwardQueue.pop(); - - if((p->pictureStructure == MPEG2_PICTURE_TYPE_FRAME) || waitSecondField){ - queueTime++; - } - if(p->pictureStructure != MPEG2_PICTURE_TYPE_FRAME) - waitSecondField = !waitSecondField; - - buffers.push(p); - } - waitSecondField = false; - }else if (p->pictureStructure != MPEG2_PICTURE_TYPE_FRAME){ - waitSecondField = !waitSecondField; - } + waitQueue.push_back(p); return 0; } +void +M2VParser::TimestampWaitingFrames() { + // mxinfo(boost::format(" flushing %1%\n") % waitQueue.size()); + + for (std::size_t idx = 0, numFrames = waitQueue.size(); idx < numFrames; ++idx) + waitQueue[idx]->decodingOrder = idx; + + brng::sort(waitQueue, [](MPEGFrame *a, MPEGFrame *b) { return a->timecode < b->timecode; }); + + for (auto const &frame : waitQueue) + StampFrame(frame); + for (auto const &frame : waitQueue) + UpdateFrame(frame); + + brng::sort(waitQueue, [](MPEGFrame *a, MPEGFrame *b) { return a->decodingOrder < b->decodingOrder; }); + + for (auto const &frame : waitQueue) + buffers.push(frame); + waitQueue.clear(); +} + int32_t M2VParser::PrepareFrame(MPEGChunk* chunk, MediaTime timecode, MPEG2PictureHeader picHdr){ MPEGFrame* outBuf; bool bCopy = true; diff --git a/src/mpegparser/M2VParser.h b/src/mpegparser/M2VParser.h index f50e2df77..279ffdd19 100644 --- a/src/mpegparser/M2VParser.h +++ b/src/mpegparser/M2VParser.h @@ -61,6 +61,7 @@ public: MediaTime duration; char frameType; MediaTime timecode; // before stamping: sequence number; after stamping: real timecode + unsigned int decodingOrder; MediaTime refs[2]; MPEGFrameRef tmpRefs[2]; bool stamped; @@ -78,8 +79,7 @@ public: class M2VParser { private: std::vector chunks; //Hold the chunks until we can order them - std::queue waitQueue; //Holds unstamped buffers until we can stamp them. - std::queue tmpForwardQueue; //Temporarily holds stamped buffers until we can forward them. + std::vector waitQueue; //Holds unstamped buffers until we can stamp them. std::queue buffers; //Holds stamped buffers until they are requested. MediaTime previousTimecode; MediaTime previousDuration; @@ -87,7 +87,6 @@ private: MPEGChunk* seqHdrChunk, *gopChunk; MPEG2SequenceHeader m_seqHdr; //current sequence header MPEG2GOPHeader m_gopHdr; //current GOP header - MediaTime queueTime; MediaTime waitExpectedTime; bool waitSecondField; bool probing; @@ -168,6 +167,8 @@ public: void AddTimecode(int64_t timecode); void SetThrowOnError(bool doThrow); + + void TimestampWaitingFrames(); }; diff --git a/tests/results.txt b/tests/results.txt index 5fe937247..168892bdd 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -94,7 +94,7 @@ T_244iconv_missing_character:b0cea35ff7be939dc13b0e373657695a:passed:20081004-21 T_245srt_timecode_formats:ce80ae14afb44f507cfe4226944d1266-ce80ae14afb44f507cfe4226944d1266:passed:20081202-141604:0.255023139 T_246theora_pixel_aspect_ratio:2067abd2dc972e76ec7b1080927cbe8e:passed:20081205-174857:0.061985381 T_247attachment_selection:04e4c3afcde00ea879132481a68d1478-9c6b5c11674833d484f9a3465c42f4bd-6e3afba12988ca0d66b15096002dcaa3-04a48cc0da545825357249eace746de8+7aba76631fef5d8be295411dd12ccc9e:passed:20090228-191612:0.113510024 -T_248mpeg2:c11e561110cebaa7268be5c7991ccbf3-00db43954883d06d2805a81d277ecc10:passed:20090531-132819:8.128150931 +T_248mpeg2:326006565ffe36597085992dccbe19ac-00db43954883d06d2805a81d277ecc10:passed:20090531-132819:8.128150931 T_249mpeg2_no_codecprivate:9a338fc21ac14764bc63cbdde68d7f2b:passed:20090531-132821:1.45695982 T_250tag_selection:9df21986c4523e8d83533391e9d3a3dd-d1b4cc27362894ef70176a4e42ce88b7-59ee8324b57552b1b63b7c8a6866efae-0411dd08be9cc75d7ac861782af4150b-ee207fb3cb5933299d2bc80919c2d7f4-ee207fb3cb5933299d2bc80919c2d7f4-09c92b6792730887038da4c7e25ebc9f:passed:20090531-205640:1.287172749 T_251vc1_truehd_eac3_from_evo:74614cfa6328f70b12b2327277f5f61e:passed:20090606-220945:1.173449174 @@ -171,7 +171,7 @@ T_322propedit_track_headers:785209f2dc35ad6177bea2ca6e43198f-3e9ff1255235f31945c T_323propedit_segment_info:785209f2dc35ad6177bea2ca6e43198f-0baf1cb46e7d365580b51aa4452551d6-d6cd21681b8544aa730744f128d46fd7:passed:20111203-152845:0.425794709 T_324propedit_chapters:785209f2dc35ad6177bea2ca6e43198f-a9255d40de93e2731aaead0a746e582f-493859725ffbe8bf65cfd397e26a7c90-ae788bbd0580dd01d10672a46e3be84d-9ff842a7f02fe26ef95313b626be83b3-b6aafbfe2bc4902f3187031a71730f8d-3057bf142672a2c86657c64f4e4d189a-48614f8c72edb5d3e23115e1998f5997-8594e3734741654285cc5ce5e48f3e29-d41d8cd98f00b204e9800998ecf8427e:passed:20111203-154502:0.736303091 T_325propedit_tags:785209f2dc35ad6177bea2ca6e43198f-26ad4ab0491d76d9fb6f57b4a4b35400-2d6bc3c519e65853430cd53e64d46386-52322241c33de4f7b56ede7a4764f72f-2d6bc3c519e65853430cd53e64d46386-52322241c33de4f7b56ede7a4764f72f-ddb1f5475eca068a3e4b6ad8df1dea7e-591a65deb231cb0150a4da224d5f3415-e657d1255b0b7a1a1610bda7460dc318-438f0a8ce757e03b77c084bd593cb196-e657d1255b0b7a1a1610bda7460dc318-438f0a8ce757e03b77c084bd593cb196-6f60e83c7d351a25ad6af4545ce3dff6-5a8f05d63ffe9d53046abf40ec30e42a-e126ac5e6a97297f4ee14cfd648e1e33-d41d8cd98f00b204e9800998ecf8427e:passed:20111203-160727:1.314514241 -T_326mpeg_ps_mpeg_audio_layer4:92c7507e54caf041d729c2e9e97c775f:passed:20111207-224511:1.505513342 +T_326mpeg_ps_mpeg_audio_layer4:fca2d489ec0cd9ade511f3145187464b:passed:20111207-224511:1.505513342 T_327vp8_frame_type:da02f5873c366315b4594e34a04966e8:passed:20111207-233304:0.089263543 T_328dts_detected_as_ac3:6cadc1ac331dda8e80eae3abfeca1358:passed:20111229-192324:0.090561076 T_329X_timecodes_v2:dadc36ce79c1c4b281f8f1f865746598-049cdc2d9226fac8c61d193d803bfc1f-3720aac3f16b66ec3308ffa7bf913c6e-6469e2522a4b48b7b20bae93f5d9086d-1ff091abfcb0938d6ac7fd0495e899b3-049cdc2d9226fac8c61d193d803bfc1f-d172a9340cbf2802690479e396879d1e-bf76c5886cc7c18cc7e6ee796c3406b4-b3f9d126c31505c22f292a1d2bdffba2-4bd97467fac0ac0b561d68b8b15a79dd:passed:20120105-202451:1.376047868 @@ -206,7 +206,7 @@ T_357segment_info:c734542adcdeca270db3b6e41fd85ffc-61d4730547bcd79e9a692caa4c214 T_358usf:13cc323a8e690b4e1c236010938e1ee3:passed:20120329-142144:0.051754089 T_359split_parts:bc2ff718d54847937b9f6b4f2e38036d+49341e4669faff0225236af4eda8eb09+ok-5efda882c230b3ff5a044af4148f8d1c+ok-b1619e39ba9194cf2ef3187991f6ed18+ok-497448adf872f4050f5e34d9aacf5fa0+4f25451d573042cfdff4809a28638e93+782db07154db9054ff9f0ef3bf1235c6+f129ab41e298b6358fc0197c92c3da5f+91120826bea5d0462090d54416a571b1+ad267ff44bf21e0acee2a0dc009908d6+1586ba9b356c823bd58566c58e635889+ok-32b1d647cec6dc843b31f3030daf9ee7+ok-a72d22cdc1ab54c605930259a9953aff+ok:passed:20120331-133448:2.321768368 T_360X_chapters_hex_format:87a60c81c05fb0a153a2e041485ae2cb-3853793b0d88fc10efadb146ca948833:passed:20120404-152038:0.047282116 -T_361file_concatenation:68b5608e9ff344d493fdcbbdc24b821b-35c3e616631e24ef8df17c8cfb0b35d0-35c3e616631e24ef8df17c8cfb0b35d0-98fd5ee77c40d1de6244c7afca3cfa15-98fd5ee77c40d1de6244c7afca3cfa15-98fd5ee77c40d1de6244c7afca3cfa15-2b0cde47d9cff1d464c78f2e158582f1-2b64e3421c5553b1dcd8675f69c0b6cc-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-09ffbb6cd3ce4f4947f1f2e782be6cdd-d7039c2d63d417c29d4ba7bf623563e5-80e456901895bd509cc8f2f6ed10d587-80e456901895bd509cc8f2f6ed10d587:passed:20120406-144928:18.646532342 +T_361file_concatenation:68b5608e9ff344d493fdcbbdc24b821b-ed5c6fbaedc5f24c2a8321c306baff41-ed5c6fbaedc5f24c2a8321c306baff41-98fd5ee77c40d1de6244c7afca3cfa15-98fd5ee77c40d1de6244c7afca3cfa15-98fd5ee77c40d1de6244c7afca3cfa15-2b0cde47d9cff1d464c78f2e158582f1-2b64e3421c5553b1dcd8675f69c0b6cc-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-740607972d7bb60c595835efdb9d880d-09ffbb6cd3ce4f4947f1f2e782be6cdd-d7039c2d63d417c29d4ba7bf623563e5-80e456901895bd509cc8f2f6ed10d587-80e456901895bd509cc8f2f6ed10d587:passed:20120406-144928:18.646532342 T_362xtr_avc:abc9dd7b4579a2e14783271d1d64d486-49ae33bdb1e43de90886bc2ac6410c35:passed:20120416-153515:1.811589633 T_363srt_colon_decimal_separator:d75be97f27797c8b3fc62e5d39a8d7ea:passed:20120520-180625:0.032379535 T_364qtmp4_track_with_empty_chunkmap_table:f7837ed142ed9a5cca5d2fbc5788d597:passed:20120605-223925:0.168392001 @@ -234,7 +234,7 @@ T_385split_parts_frames:bc2ff718d54847937b9f6b4f2e38036d+49341e4669faff0225236af T_386flv_vp6f:06bccf045351fc4946cb24c4990d396c:passed:20130121-223417:0.187881106 T_387mp4_free_invalid_size:6a338e059717d57b89efa683031ee3c6:passed:20130216-003333:0.04107827 T_388split_parts_and_chapters:8d2a65e53a0d3c8ed6050fc8d4679538-7d1a25c6a3c78c7f1469d1dcf637af4f-0e101a08e2be05f0247b09e93635b1de:passed:20130216-203522:0.690018077 -T_389mpeg1_in_ps_misdetected_as_avc:276be21585918359788a0bf47dbf8502:passed:20130224-132738:1.205560654 +T_389mpeg1_in_ps_misdetected_as_avc:566aeaa72d47e30589f3227555bf24d3:passed:20130224-132738:1.205560654 T_390timecode_info_on_resync:ok+ok:passed:20130318-185621:0.115461517 T_391fix_bitstream_frame_rate:6d71f661ca5682143eddaf0842435952:passed:20130329-114522:0.460370742 T_392avi_audio_chunk_size_0:77f8ab45d16202e5e0a4bdabc7f616f0:passed:20130331-134751:0.893777097 @@ -283,7 +283,7 @@ T_434mkvpropedit_no_track_uid:99631d7c0f79faf45a37696dae506b21-ab7b3ff004b7ed5a1 T_435mp4_edit_list_duration_uses_global_time_scale:eba0b6ddc51c05e4a97f39a3bb350b01:passed:20140905-183027:0.096674624 T_436extract_ssa_extradata_after_events:8be4c4af0a2d65826071aee718ec44e8:passed:20140906-090602:0.054263072 T_437ac3_from_avi_with_garbage:dd5b3c1593cdccac26d2b8e8c6694893:passed:20140908-144311:0.075599921 -T_438pcm_in_vob:09b8c5d3ca0f0224f3ccc9f0ca4f5930:passed:20140917-213731:1.216717009 +T_438pcm_in_vob:f39c9585f8fe45d89073a4531dc69d9d:passed:20140917-213731:1.216717009 T_439pcm_in_m2ts:e508ff1185855b9454c4b15ca6278790-fae839a729cd17480f7f88d6736df0e4:passed:20140917-222633:4.736872644 T_440chapter_display_language_default_value:6dd844972d790ee741c58f5de5ff1555:passed:20140929-142306:0.021103552 T_441mkvmerge_mp4_big_endian_pcm:ddd27c5fa93e1e4fe610707e8b050365:passed:20141104-190420:0.278181073 @@ -335,3 +335,4 @@ T_486m2ts_eac3_with_extension_in_own_packet:265d8436497eb50f49b24805a08cd39a:pas T_487matroska_version_and_read_version_with_opus:4+2-4+2-4+2-4+1-4+2-4+1-4+1-4+1:passed:20150329-213811:0.670610463 T_488hevc_conformance_window_with_cropping:71d84f56384c2d25fe55477766b7604d:passed:20150329-220212:0.705828455 T_489dts_es:6f9fb5e7a5144060ce4b19ee146bb03a-0770ee503180e4d8cfd57335e684921d-99ccde4d9e7b1cd0ff711edfe9cdad1e:passed:20150403-115948:1.604769748 +T_490sequence_numbers_no_0_in_first_gop:10a0cc2f79acd8cb520661b45721e652:new:20150411-142423:0.832402022 diff --git a/tests/test-490sequence_numbers_no_0_in_first_gop.rb b/tests/test-490sequence_numbers_no_0_in_first_gop.rb new file mode 100755 index 000000000..83d4d3a10 --- /dev/null +++ b/tests/test-490sequence_numbers_no_0_in_first_gop.rb @@ -0,0 +1,5 @@ +#!/usr/bin/ruby -w + +# T_490sequence_numbers_no_0_in_first_gop +describe "mkvmerge / MPEG-2 ES without a sequence number == 0 in the first GOP" +test_merge "data/mpeg12/sequence_numbers_no_0_in_first_gop.m2v"