diff options
| author | Carl Hetherington <cth@carlh.net> | 2021-10-18 08:55:27 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2021-10-24 20:52:11 +0200 |
| commit | d5ab7ecf942405bcaa7fd8d367e09f95d6c0c978 (patch) | |
| tree | aa947c1cc41bb4de56b22ba628567f9357a6c72b | |
| parent | 4d708138f22aa70370494f226497542cad0f0bb4 (diff) | |
Order subtitles in the XML according to their vertical position (DoM bug #2106).v1.8.4
| -rw-r--r-- | src/smpte_subtitle_asset.h | 4 | ||||
| -rw-r--r-- | src/subtitle_asset.cc | 3 | ||||
| -rw-r--r-- | src/verify.cc | 113 | ||||
| -rw-r--r-- | src/verify.h | 6 | ||||
| -rw-r--r-- | test/data/verify_incorrect_closed_caption_ordering3.xml | 20 | ||||
| -rw-r--r-- | test/data/verify_incorrect_closed_caption_ordering4.xml | 20 | ||||
| -rw-r--r-- | test/smpte_subtitle_test.cc | 175 | ||||
| -rw-r--r-- | test/verify_test.cc | 136 |
8 files changed, 476 insertions, 1 deletions
diff --git a/src/smpte_subtitle_asset.h b/src/smpte_subtitle_asset.h index 4e220d4d..b707da12 100644 --- a/src/smpte_subtitle_asset.h +++ b/src/smpte_subtitle_asset.h @@ -58,6 +58,8 @@ namespace ASDCP { struct verify_invalid_language1; struct verify_invalid_language2; +struct write_subtitles_in_vertical_order_with_top_alignment; +struct write_subtitles_in_vertical_order_with_bottom_alignment; namespace dcp { @@ -204,6 +206,8 @@ private: friend struct ::write_smpte_subtitle_test2; friend struct ::verify_invalid_language1; friend struct ::verify_invalid_language2; + friend struct ::write_subtitles_in_vertical_order_with_top_alignment; + friend struct ::write_subtitles_in_vertical_order_with_bottom_alignment; void read_fonts (std::shared_ptr<ASDCP::TimedText::MXFReader>); void parse_xml (std::shared_ptr<cxml::Document> xml); diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc index 2793772a..22781196 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_asset.cc @@ -579,6 +579,9 @@ struct SubtitleSorter if (a->in() != b->in()) { return a->in() < b->in(); } + if (a->v_align() == VAlign::BOTTOM) { + return a->v_position() > b->v_position(); + } return a->v_position() < b->v_position(); } }; diff --git a/src/verify.cc b/src/verify.cc index 771898da..8a065e68 100644 --- a/src/verify.cc +++ b/src/verify.cc @@ -921,6 +921,113 @@ verify_text_details ( } +static +void +verify_closed_caption_details ( + vector<shared_ptr<Reel>> reels, + vector<VerificationNote>& notes + ) +{ + std::function<void (cxml::ConstNodePtr node, std::vector<cxml::ConstNodePtr>& text_or_image)> find_text_or_image; + find_text_or_image = [&find_text_or_image](cxml::ConstNodePtr node, std::vector<cxml::ConstNodePtr>& text_or_image) { + for (auto i: node->node_children()) { + if (i->name() == "Text") { + text_or_image.push_back (i); + } else { + find_text_or_image (i, text_or_image); + } + } + }; + + auto mismatched_valign = false; + auto incorrect_order = false; + + std::function<void (cxml::ConstNodePtr)> parse; + parse = [&parse, &find_text_or_image, &mismatched_valign, &incorrect_order](cxml::ConstNodePtr node) { + if (node->name() == "Subtitle") { + vector<cxml::ConstNodePtr> text_or_image; + find_text_or_image (node, text_or_image); + optional<string> last_valign; + optional<float> last_vpos; + for (auto i: text_or_image) { + auto valign = i->optional_string_attribute("VAlign"); + if (!valign) { + valign = i->optional_string_attribute("Valign").get_value_or("center"); + } + auto vpos = i->optional_number_attribute<float>("VPosition"); + if (!vpos) { + vpos = i->optional_number_attribute<float>("Vposition").get_value_or(50); + } + + if (last_valign) { + if (*last_valign != valign) { + mismatched_valign = true; + } + } + last_valign = valign; + + if (!mismatched_valign) { + if (last_vpos) { + if (*last_valign == "top" || *last_valign == "center") { + if (*vpos < *last_vpos) { + incorrect_order = true; + } + } else { + if (*vpos > *last_vpos) { + incorrect_order = true; + } + } + } + last_vpos = vpos; + } + } + } + + for (auto i: node->node_children()) { + parse(i); + } + }; + + for (auto reel: reels) { + for (auto ccap: reel->closed_captions()) { + auto reel_xml = ccap->asset()->raw_xml(); + if (!reel_xml) { + notes.push_back ({VerificationNote::Type::WARNING, VerificationNote::Code::MISSED_CHECK_OF_ENCRYPTED}); + continue; + } + + /* We need to look at <Subtitle> instances in the XML being checked, so we can't use the subtitles + * read in by libdcp's parser. + */ + + shared_ptr<cxml::Document> doc; + optional<int> tcr; + optional<Time> start_time; + try { + doc = make_shared<cxml::Document>("SubtitleReel"); + doc->read_string (*reel_xml); + } catch (...) { + doc = make_shared<cxml::Document>("DCSubtitle"); + doc->read_string (*reel_xml); + } + parse (doc); + } + } + + if (mismatched_valign) { + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN, + }); + } + + if (incorrect_order) { + notes.push_back ({ + VerificationNote::Type::ERROR, VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING, + }); + } +} + + struct LinesCharactersResult { bool warning_length_exceeded = false; @@ -1061,6 +1168,8 @@ verify_text_details (vector<shared_ptr<Reel>> reels, vector<VerificationNote>& n } ); } + + verify_closed_caption_details (reels, notes); } @@ -1650,6 +1759,10 @@ dcp::note_to_string (VerificationNote note) return "Some aspect of this DCP could not be checked because it is encrypted."; case VerificationNote::Code::EMPTY_TEXT: return "There is an empty <Text> node in a subtitle or closed caption."; + case VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN: + return "Some closed <Text> or <Image> nodes have different vertical alignment within a <Subtitle>."; + case VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING: + return "Some closed captions are not listed in the order of their vertical position."; } return ""; diff --git a/src/verify.h b/src/verify.h index c10a0e7e..424b29e7 100644 --- a/src/verify.h +++ b/src/verify.h @@ -387,7 +387,11 @@ public: /** Something could not be verified because content is encrypted and no key is available */ MISSED_CHECK_OF_ENCRYPTED, /** Some timed-text XML has an empty <_Text_> node */ - EMPTY_TEXT + EMPTY_TEXT, + /** Some closed captions do not have the same vertical alignment within a <_Subtitle_> node */ + MISMATCHED_CLOSED_CAPTION_VALIGN, + /** Some closed captions are not listed in the XML in the order of their vertical position */ + INCORRECT_CLOSED_CAPTION_ORDERING, }; VerificationNote (Type type, Code code) diff --git a/test/data/verify_incorrect_closed_caption_ordering3.xml b/test/data/verify_incorrect_closed_caption_ordering3.xml new file mode 100644 index 00000000..c6dffa29 --- /dev/null +++ b/test/data/verify_incorrect_closed_caption_ordering3.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SubtitleReel xmlns="http://www.smpte-ra.org/schemas/428-7/2010/DCST" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <Id>urn:uuid:fc754ec1-eb8d-4029-8023-80d7dadc6313</Id> + <ContentTitleText></ContentTitleText> + <IssueDate>2021-10-24T00:23:49.000+02:00</IssueDate> + <Language>de-DE</Language> + <EditRate>24 1</EditRate> + <TimeCodeRate>24</TimeCodeRate> + <StartTime>00:00:00:00</StartTime> + <SubtitleList> + <Font AspectAdjust="1.0" Color="FF000000" Effect="none" EffectColor="FF000000" Italic="no" Script="normal" Size="42" Underline="no" Weight="normal"> + <Subtitle FadeDownTime="00:00:00:00" FadeUpTime="00:00:00:00" SpotNumber="1" TimeIn="00:00:04:00" TimeOut="00:00:12:12"> + <Text Valign="center" Vposition="20">This</Text> + <Text Valign="center" Vposition="10">is</Text> + <Text Valign="center" Vposition="0">also not fine</Text> + </Subtitle> + </Font> + </SubtitleList> +</SubtitleReel> + diff --git a/test/data/verify_incorrect_closed_caption_ordering4.xml b/test/data/verify_incorrect_closed_caption_ordering4.xml new file mode 100644 index 00000000..5fc2b49b --- /dev/null +++ b/test/data/verify_incorrect_closed_caption_ordering4.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<SubtitleReel xmlns="http://www.smpte-ra.org/schemas/428-7/2010/DCST" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <Id>urn:uuid:fc754ec1-eb8d-4029-8023-80d7dadc6313</Id> + <ContentTitleText></ContentTitleText> + <IssueDate>2021-10-24T00:23:49.000+02:00</IssueDate> + <Language>de-DE</Language> + <EditRate>24 1</EditRate> + <TimeCodeRate>24</TimeCodeRate> + <StartTime>00:00:00:00</StartTime> + <SubtitleList> + <Font AspectAdjust="1.0" Color="FF000000" Effect="none" EffectColor="FF000000" Italic="no" Script="normal" Size="42" Underline="no" Weight="normal"> + <Subtitle FadeDownTime="00:00:00:00" FadeUpTime="00:00:00:00" SpotNumber="1" TimeIn="00:00:04:00" TimeOut="00:00:12:12"> + <Text Valign="bottom" Vposition="20">This</Text> + <Text Valign="bottom" Vposition="10">is</Text> + <Text Valign="bottom" Vposition="0">fine, though.</Text> + </Subtitle> + </Font> + </SubtitleList> +</SubtitleReel> + diff --git a/test/smpte_subtitle_test.cc b/test/smpte_subtitle_test.cc index b1ad4058..2b8491a3 100644 --- a/test/smpte_subtitle_test.cc +++ b/test/smpte_subtitle_test.cc @@ -526,3 +526,178 @@ BOOST_AUTO_TEST_CASE (write_smpte_subtitle_test3) BOOST_CHECK (image->fade_up_time() == dcp::Time(0, 0, 0, 0, 24)); BOOST_CHECK (image->fade_down_time() == dcp::Time(0, 0, 0, 0, 24)); } + + +/* Some closed caption systems require the <Text> elements to be written in order of their + * vertical position (see DoM bug #2106). + */ +BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_top_alignment) +{ + dcp::SMPTESubtitleAsset c; + c.set_reel_number (1); + c.set_language (dcp::LanguageTag("en")); + c.set_content_title_text ("Test"); + c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00")); + + c.add ( + make_shared<dcp::SubtitleString>( + string ("Arial"), + false, + false, + false, + dcp::Colour (255, 255, 255), + 48, + 1.0, + dcp::Time (0, 0, 1, 0, 24), + dcp::Time (0, 0, 9, 0, 24), + 0, + dcp::HAlign::CENTER, + 0.8, + dcp::VAlign::TOP, + dcp::Direction::LTR, + "Top line", + dcp::Effect::NONE, + dcp::Colour (0, 0, 0), + dcp::Time (0, 0, 0, 0, 24), + dcp::Time (0, 0, 0, 0, 24), + 0 + ) + ); + + c.add ( + make_shared<dcp::SubtitleString>( + string ("Arial"), + false, + false, + false, + dcp::Colour (255, 255, 255), + 48, + 1.0, + dcp::Time (0, 0, 1, 0, 24), + dcp::Time (0, 0, 9, 0, 24), + 0, + dcp::HAlign::CENTER, + 0.9, + dcp::VAlign::TOP, + dcp::Direction::LTR, + "Bottom line", + dcp::Effect::NONE, + dcp::Colour (0, 0, 0), + dcp::Time (0, 0, 0, 0, 24), + dcp::Time (0, 0, 0, 0, 24), + 0 + ) + ); + + c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6"; + + check_xml ( + c.xml_as_string(), + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">" + "<Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>" + "<ContentTitleText>Test</ContentTitleText>" + "<IssueDate>2016-04-01T03:52:00.000+00:00</IssueDate>" + "<ReelNumber>1</ReelNumber>" + "<Language>en</Language>" + "<EditRate>24 1</EditRate>" + "<TimeCodeRate>24</TimeCodeRate>" + "<SubtitleList>" + "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">" + "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">" + "<Text Valign=\"top\" Vposition=\"80\">Top line</Text>" + "<Text Valign=\"top\" Vposition=\"90\">Bottom line</Text>" + "</Subtitle>" + "</Font>" + "</SubtitleList>" + "</SubtitleReel>", + {} + ); +} + + +/* See the test above */ +BOOST_AUTO_TEST_CASE (write_subtitles_in_vertical_order_with_bottom_alignment) +{ + dcp::SMPTESubtitleAsset c; + c.set_reel_number (1); + c.set_language (dcp::LanguageTag("en")); + c.set_content_title_text ("Test"); + c.set_issue_date (dcp::LocalTime ("2016-04-01T03:52:00+00:00")); + + c.add ( + make_shared<dcp::SubtitleString>( + string ("Arial"), + false, + false, + false, + dcp::Colour (255, 255, 255), + 48, + 1.0, + dcp::Time (0, 0, 1, 0, 24), + dcp::Time (0, 0, 9, 0, 24), + 0, + dcp::HAlign::CENTER, + 0.8, + dcp::VAlign::BOTTOM, + dcp::Direction::LTR, + "Top line", + dcp::Effect::NONE, + dcp::Colour (0, 0, 0), + dcp::Time (0, 0, 0, 0, 24), + dcp::Time (0, 0, 0, 0, 24), + 0 + ) + ); + + c.add ( + make_shared<dcp::SubtitleString>( + string ("Arial"), + false, + false, + false, + dcp::Colour (255, 255, 255), + 48, + 1.0, + dcp::Time (0, 0, 1, 0, 24), + dcp::Time (0, 0, 9, 0, 24), + 0, + dcp::HAlign::CENTER, + 0.7, + dcp::VAlign::BOTTOM, + dcp::Direction::LTR, + "Bottom line", + dcp::Effect::NONE, + dcp::Colour (0, 0, 0), + dcp::Time (0, 0, 0, 0, 24), + dcp::Time (0, 0, 0, 0, 24), + 0 + ) + ); + + c._xml_id = "a6c58cff-3e1e-4b38-acec-a42224475ef6"; + + check_xml ( + c.xml_as_string(), + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SubtitleReel xmlns=\"http://www.smpte-ra.org/schemas/428-7/2010/DCST\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">" + "<Id>urn:uuid:a6c58cff-3e1e-4b38-acec-a42224475ef6</Id>" + "<ContentTitleText>Test</ContentTitleText>" + "<IssueDate>2016-04-01T03:52:00.000+00:00</IssueDate>" + "<ReelNumber>1</ReelNumber>" + "<Language>en</Language>" + "<EditRate>24 1</EditRate>" + "<TimeCodeRate>24</TimeCodeRate>" + "<SubtitleList>" + "<Font AspectAdjust=\"1.0\" Color=\"FFFFFFFF\" Effect=\"none\" EffectColor=\"FF000000\" ID=\"Arial\" Italic=\"no\" Script=\"normal\" Size=\"48\" Underline=\"no\" Weight=\"normal\">" + "<Subtitle SpotNumber=\"1\" TimeIn=\"00:00:01:00\" TimeOut=\"00:00:09:00\" FadeUpTime=\"00:00:00:00\" FadeDownTime=\"00:00:00:00\">" + "<Text Valign=\"bottom\" Vposition=\"80\">Top line</Text>" + "<Text Valign=\"bottom\" Vposition=\"70\">Bottom line</Text>" + "</Subtitle>" + "</Font>" + "</SubtitleList>" + "</SubtitleReel>", + {} + ); +} + diff --git a/test/verify_test.cc b/test/verify_test.cc index ab0a2f26..c8d0e579 100644 --- a/test/verify_test.cc +++ b/test/verify_test.cc @@ -1618,6 +1618,43 @@ dcp_with_text (path dir, vector<TestText> subs) } +template <class T> +shared_ptr<dcp::CPL> +dcp_with_text_from_file (path dir, boost::filesystem::path subs_xml) +{ + prepare_directory (dir); + auto asset = make_shared<dcp::SMPTESubtitleAsset>(subs_xml); + asset->set_start_time (dcp::Time()); + asset->set_language (dcp::LanguageTag("de-DE")); + + auto subs_mxf = dir / "subs.mxf"; + asset->write (subs_mxf); + + /* The call to write() puts the asset into the DCP correctly but it will have + * XML re-written by our parser. Overwrite the MXF using the given file's verbatim + * contents. + */ + ASDCP::TimedText::MXFWriter writer; + ASDCP::WriterInfo writer_info; + writer_info.LabelSetType = ASDCP::LS_MXF_SMPTE; + unsigned int c; + Kumu::hex2bin (asset->id().c_str(), writer_info.AssetUUID, Kumu::UUID_Length, &c); + DCP_ASSERT (c == Kumu::UUID_Length); + ASDCP::TimedText::TimedTextDescriptor descriptor; + descriptor.ContainerDuration = asset->intrinsic_duration(); + Kumu::hex2bin (asset->xml_id()->c_str(), descriptor.AssetID, ASDCP::UUIDlen, &c); + DCP_ASSERT (c == Kumu::UUID_Length); + ASDCP::Result_t r = writer.OpenWrite (subs_mxf.string().c_str(), writer_info, descriptor, 16384); + BOOST_REQUIRE (!ASDCP_FAILURE(r)); + r = writer.WriteTimedTextResource (dcp::file_to_string(subs_xml)); + BOOST_REQUIRE (!ASDCP_FAILURE(r)); + writer.Finalize (); + + auto reel_asset = make_shared<T>(asset, dcp::Fraction(24, 1), asset->intrinsic_duration(), 0); + return write_dcp_with_single_asset (dir, reel_asset); +} + + BOOST_AUTO_TEST_CASE (verify_invalid_subtitle_first_text_time) { auto const dir = path("build/test/verify_invalid_subtitle_first_text_time"); @@ -1955,6 +1992,105 @@ BOOST_AUTO_TEST_CASE (verify_invalid_closed_caption_line_length) } +BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign1) +{ + auto const dir = path ("build/test/verify_mismatched_closed_caption_valign1"); + auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> ( + dir, + { + { 96, 300, 0.0, dcp::VAlign::TOP, "This" }, + { 96, 300, 0.1, dcp::VAlign::TOP, "is" }, + { 96, 300, 0.2, dcp::VAlign::TOP, "fine" }, + }); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + +BOOST_AUTO_TEST_CASE (verify_mismatched_closed_caption_valign2) +{ + auto const dir = path ("build/test/verify_mismatched_closed_caption_valign2"); + auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> ( + dir, + { + { 96, 300, 0.0, dcp::VAlign::TOP, "This" }, + { 96, 300, 0.1, dcp::VAlign::TOP, "is" }, + { 96, 300, 0.2, dcp::VAlign::CENTER, "not fine" }, + }); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::MISMATCHED_CLOSED_CAPTION_VALIGN }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + +BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering1) +{ + auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering1"); + auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> ( + dir, + { + { 96, 300, 0.0, dcp::VAlign::TOP, "This" }, + { 96, 300, 0.1, dcp::VAlign::TOP, "is" }, + { 96, 300, 0.2, dcp::VAlign::TOP, "fine" }, + }); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + +BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering2) +{ + auto const dir = path ("build/test/verify_invalid_incorrect_closed_caption_ordering2"); + auto cpl = dcp_with_text<dcp::ReelSMPTEClosedCaptionAsset> ( + dir, + { + { 96, 300, 0.2, dcp::VAlign::BOTTOM, "This" }, + { 96, 300, 0.1, dcp::VAlign::BOTTOM, "is" }, + { 96, 300, 0.0, dcp::VAlign::BOTTOM, "also fine" }, + }); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + +BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering3) +{ + auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering3"); + auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering3.xml"); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::ERROR, dcp::VerificationNote::Code::INCORRECT_CLOSED_CAPTION_ORDERING }, + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + +BOOST_AUTO_TEST_CASE (verify_incorrect_closed_caption_ordering4) +{ + auto const dir = path ("build/test/verify_incorrect_closed_caption_ordering4"); + auto cpl = dcp_with_text_from_file<dcp::ReelSMPTEClosedCaptionAsset> (dir, "test/data/verify_incorrect_closed_caption_ordering4.xml"); + check_verify_result ( + {dir}, + { + { dcp::VerificationNote::Type::BV21_ERROR, dcp::VerificationNote::Code::MISSING_CPL_METADATA, cpl->id(), cpl->file().get() } + }); +} + + + BOOST_AUTO_TEST_CASE (verify_invalid_sound_frame_rate) { path const dir("build/test/verify_invalid_sound_frame_rate"); |
