diff --git a/ChangeLog b/ChangeLog index f2109f165..d3bf7ba30 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2016-01-31 Moritz Bunkus + + * mkvmerge: bug fix: fixed the output of the "playlist_file" + property of the "container" entity in the JSON identification + format from a single string to an array of strings. The format + version has been bumped to 3 due to this change. + 2016-01-30 Moritz Bunkus * docs: added a Polish translation of the man pages by Daniel Kluz diff --git a/doc/json-schema/mkvmerge-identification-output-schema-v3.json b/doc/json-schema/mkvmerge-identification-output-schema-v3.json new file mode 100644 index 000000000..4c039f66d --- /dev/null +++ b/doc/json-schema/mkvmerge-identification-output-schema-v3.json @@ -0,0 +1,353 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://mkvtoolnix.download/doc/mkvmerge-identification-output-schema-v3.json", + "title": "mkvmerge identification output", + "description": "The JSON output produced by mkvmerge's file identification mode", + "type": "object", + "properties": { + "attachments": { + "description": "an array describing the attachments found if any", + "type": "array", + "items": { + "type": "object", + "properties": { + "content_type": { + "type": "string", + "minLength": 1 + }, + "description": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "id": { + "type": "integer", + "minimum": 0 + }, + "size": { + "type": "integer", + "minimum": 0 + }, + "properties": { + "type": "object", + "properties": { + "uid": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "file_name", + "id", + "properties", + "size" + ] + } + }, + "chapters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "num_entries": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "num_entries" + ] + } + }, + "container": { + "description": "information about the identified container", + "type": "object", + "properties": { + "properties": { + "description": "additional properties for the container varying by container format", + "type": "object", + "properties": { + "container_type": { + "description": "A unique number identifying the container type that's supposed to stay constant over all future releases of MKVToolNix", + "type": "integer", + "minLength": 1 + }, + "duration": { + "description": "The file's/segment's duration in nanoseconds", + "type": "integer", + "minimum": 0 + }, + "is_providing_timecodes": { + "description": "States whether or not the container has timestamps for the packets (e.g. Matroska, MP4) or not (e.g. SRT, MP3)", + "type": "boolean" + }, + "segment_uid": { + "description": "A hexadecimal string of the segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "next_segment_uid": { + "description": "A hexadecimal string of the next segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "previous_segment_uid": { + "description": "A hexadecimal string of the previous segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "other_file": { + "description": "The file name of an additional file processed as well", + "type": "string", + "minLength": 1 + }, + "playlist": { + "description": "States whether or not the identified file is a playlist (e.g. MPLS) referring to several other files", + "type": "boolean" + }, + "playlist_chapters": { + "description": "The number of chapters in a playlist if it is a one", + "type": "integer", + "minimum": 0 + }, + "playlist_duration": { + "description": "The total duration in nanoseconds of all files referenced by the playlist if it is a one", + "type": "integer", + "minimum": 0 + }, + "playlist_file": { + "description": "An array of file names the playlist contains", + "type": "array", + "items": { + "type": "string" + } + }, + "playlist_size": { + "description": "The total size in bytes of all files referenced by the playlist if it is a one", + "type": "integer", + "minimum": 0 + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "recognized": { + "description": "States whether or not mkvmerge knows about the format", + "type": "boolean" + }, + "supported": { + "description": "States whether or not mkvmerge can read the format", + "type": "boolean" + }, + "type": { + "description": "A human-readable description/name for the container format", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "required": [ + "recognized", + "supported" + ] + }, + "errors": { + "type": "array", + "items": { + "type": "string" + } + }, + "file_name": { + "description": "the identified file's name", + "type": "string", + "minLength": 1 + }, + "global_tags": { + "type": "array", + "items": { + } + }, + "identification_format_version": { + "description": "The output format's version", + "type": "integer", + "minimum": 3, + "maximum": 3 + }, + "track_tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "num_entries": { + "type": "integer" + }, + "track_id": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "num_entries", + "track_id" + ] + } + }, + "tracks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "codec": { + "type": "string", + "minLength": 1 + }, + "id": { + "type": "integer", + "minLength": 0 + }, + "type": { + "type": "string" + }, + "properties": { + "type": "object", + "properties": { + "aac_is_sbr": { + "type": "string", + "enum": [ + "true", + "false", + "unknown" + ] + }, + "audio_bits_per_sample": { + "type": "integer", + "minimum": 0 + }, + "audio_channels": { + "type": "integer", + "minimum": 0 + }, + "audio_sampling_frequency": { + "type": "integer", + "minimum": 0 + }, + "codec_id": { + "type": "string" + }, + "codec_private_data": { + "type": "string" + }, + "codec_private_length": { + "type": "integer", + "minimum": 0 + }, + "content_encoding_algorithms": { + "type": "string", + "minLength": 1 + }, + "default_duration": { + "type": "integer", + "minimum": 0 + }, + "default_track": { + "type": "boolean" + }, + "display_dimensions": { + "type": "string", + "pattern": "^[0-9]+x[0-9]+$" + }, + "enabled_track": { + "type": "boolean" + }, + "forced_track": { + "type": "boolean" + }, + "language": { + "type": "string" + }, + "number": { + "type": "integer", + "minimum": 0 + }, + "packetizer": { + "type": "string", + "minLength": 1 + }, + "pixel_dimensions": { + "type": "string", + "pattern": "^[0-9]+x[0-9]+$" + }, + "stereo_mode": { + "type": "integer", + "minimum": 0 + }, + "stream_id": { + "type": "string", + "pattern": "^[0-9a-f]{2}$" + }, + "sub_stream_id": { + "type": "string", + "pattern": "^[0-9a-f]{2}$" + }, + "tag_artist": { + "type": "string" + }, + "tag_bitsps": { + "type": "string" + }, + "tag_bps": { + "type": "string" + }, + "tag_fps": { + "type": "string" + }, + "tag_title": { + "type": "string" + }, + "text_subtitles": { + "type": "boolean" + }, + "track_name": { + "type": "string" + }, + "ts_pid": { + "type": "integer", + "minimum": 0 + }, + "uid": { + "type": "integer", + "minimum": 0 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "codec", + "id", + "type" + ] + } + }, + "warnings": { + "type": "array", + "items": { + "type": "string" + } + } + } +} diff --git a/src/common/mm_mpls_multi_file_io.cpp b/src/common/mm_mpls_multi_file_io.cpp index ab9f32ceb..e7154c8a5 100644 --- a/src/common/mm_mpls_multi_file_io.cpp +++ b/src/common/mm_mpls_multi_file_io.cpp @@ -101,6 +101,10 @@ mm_mpls_multi_file_io_c::create_verbose_identification_info(mtx::id::info_c &inf info.add(mtx::id::playlist_duration, m_mpls_parser->get_playlist().duration.to_ns()); info.add(mtx::id::playlist_size, m_total_size); info.add(mtx::id::playlist_chapters, m_mpls_parser->get_chapters().size()); + + auto file_names = nlohmann::json::array(); for (auto &file : m_files) - info.add(mtx::id::playlist_file, file.string()); + file_names.push_back(file.string()); + + info.add(mtx::id::playlist_file, file_names); } diff --git a/src/merge/generic_reader.cpp b/src/merge/generic_reader.cpp index f10b41c79..c079c558e 100644 --- a/src/merge/generic_reader.cpp +++ b/src/merge/generic_reader.cpp @@ -320,6 +320,13 @@ generic_reader_c::display_identification_results_as_text() { auto formatter = boost::format{"%1%:%2%"}; for (auto const &pair : info) { + if (pair.second.is_array()) { + for (auto it = pair.second.begin(), end = pair.second.end(); it != end; ++it) + formatted.emplace_back((formatter % escape(pair.first) % escape(it->get())).str()); + + continue; + } + auto value = pair.second.is_number() ? to_string(pair.second.get()) : pair.second.is_boolean() ? std::string{pair.second.get() ? "1" : "0"} : pair.second.get(); @@ -415,7 +422,7 @@ generic_reader_c::display_identification_results_as_json() { }; auto json = nlohmann::json{ - { "identification_format_version", 2 }, + { "identification_format_version", ID_JSON_FORMAT_VERSION }, { "file_name", m_ti.m_fname }, { "tracks", nlohmann::json::array() }, { "attachments", nlohmann::json::array() }, diff --git a/src/merge/id_result.cpp b/src/merge/id_result.cpp index 07daa7553..ab364fde1 100644 --- a/src/merge/id_result.cpp +++ b/src/merge/id_result.cpp @@ -33,8 +33,8 @@ static void output_container_unsupported_json(std::string const &filename, translatable_string_c const &info) { auto json = nlohmann::json{ - { "identification_format_version", 2 }, - { "file_name", filename }, + { "identification_format_version", ID_JSON_FORMAT_VERSION }, + { "file_name", filename }, { "container", { { "recognized", true }, { "supported", false }, diff --git a/src/merge/id_result.h b/src/merge/id_result.h index 3264f014e..3e02d3707 100644 --- a/src/merge/id_result.h +++ b/src/merge/id_result.h @@ -27,6 +27,8 @@ #define ID_RESULT_TAGS "tags" #define ID_RESULT_GLOBAL_TAGS_ID -1 +#define ID_JSON_FORMAT_VERSION 3 + struct id_result_t { int64_t id; std::string type, info, description; diff --git a/src/merge/mkvmerge.cpp b/src/merge/mkvmerge.cpp index 9da1a4235..95ff098f0 100644 --- a/src/merge/mkvmerge.cpp +++ b/src/merge/mkvmerge.cpp @@ -369,8 +369,8 @@ list_file_types() { static void display_unsupported_file_type_json(filelist_t const &file) { auto json = nlohmann::json{ - { "identification_format_version", 2 }, - { "file_name", file.name }, + { "identification_format_version", ID_JSON_FORMAT_VERSION }, + { "file_name", file.name }, { "container", { { "recognized", false }, { "supported", false }, diff --git a/tests/results.txt b/tests/results.txt index aeb316292..e3416b43b 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -357,7 +357,7 @@ T_508splitting_by_parts_with_segment_linking:existence0-true-true-true-existence T_509rerender_track_headers_chapters_attachments:76712bb36be12e38e98a1342452f28d5:passed:20151115-230226:0.287840782 T_510propedit_add_attachments_without_meta_seek_present:770103c238a0f502c9ec55f0599d8544:passed:20151121-101043:0.070892905 T_511propedit_ensure_seek_head_exists_at_front:20f53afd94e39f5bbf3f1091eefbe31d:passed:20151129-194025:0.152563199 -T_512json_identification:dc56910afee27e5f42414fde294a262c+ok-4d5b44ce8fea381a4de100ed77ee77bc+ok-4ce52c415319a3c9ace3394b251bfa04+ok-b31447af73fb7453a6801f6817a5f904+ok-35d9a8927414617bce87deeeacb24a0b+ok-1d7e45d0520f72ac8cc40b4a31f62ce2+ok-7c4bfe1e467c782175892a155c9769aa+ok-2f1b1e7b1845e40b66c42c555e2214bd+ok-76d5c58b6fc06efcfea66e447ad8bf44+ok-4f84d928bfa74c33a5fd2f08f8e138b7+ok-8dd46981cf9e1787ea682fe03aba4d92+ok-e0fd5247650d9eee6a259b0a796cbcb0+ok-c56940a2497513380531e693ec06b76c+ok-ee1ae1f2602ebaef4e83772ab5e39804+ok-617e011e200630baff85bc674b2a1292+ok:passed:20151207-223859:6.280036064 +T_512json_identification:e2c1bd814ea805d9711a3875e646a51d+ok-d815a6390e25b861d21093ef66f17191+ok-19f449d85ad9c8c2a3d3840bc3734e58+ok-a86b43982a842c6b9c8572534e40dcca+ok-bf4664fb8aac8ad51e16d018d2c6697e+ok-e6e342718925ef84c9c65288d477ac76+ok-59c47f40f7c35d90a4a8ba0f15aebf5b+ok-b524a708c8e65517026daf4e18c6efcf+ok-eabca6419fcd27291e5db66c1e391c0d+ok-01f6792cb05fb5dd4823a5919a5565b4+ok-3f9fd90d34f591fa205eb235b1efe36c+ok-f7c9d7c612f38a0a2c0548a38008882f+ok-10bd29b4b37ba8e8fd746a45e67af4a1+ok-b137e5af83e68f2c52af9bfe3976a977+ok-c8b2bd3f66486fc47ad98d7b6a06f713+ok:passed:20151207-223859:1.876447112 T_513vp9_10bit_key_frame_detection:9eab6e85ec792dcf670873d70a87f6ea:passed:20151208-224613:0.267556245 T_514remove_track_statistics_tags_during_remux:43d4c0ec5cbbc31018715e62a99377df-afe190e36be530592fe3b83fb28d3e69-a7f246fe02132a1fb9cd3d7d0f85f180:passed:20151215-134129:1.426290351 T_515aac_sampling_frequency_8000_is_not_sbr:b8f857faf759eae5362fefbb7cbeed23:passed:20151219-130357:0.066237884 @@ -365,7 +365,7 @@ T_516hevc_rap_sample_grouping:bb42041df575edd35f36d47aebc341a7:passed:20151228-1 T_517h264_forbidden_byte_sequence_in_slice_nalu:74e0fb1a25397078335c6d94974dc168:passed:20151228-134333:3.276176597 T_518mlp:bc0269df9e0c0c968e4a408d7da46d35-0624d3f1975273199a0e12a9f4b0fd20:passed:20151229-134422:0.895368448 T_519truehd:33f8c0f013c71529281179cf8669c567-33f8c0f013c71529281179cf8669c567:passed:20151229-135950:3.160459472 -T_520truehd_mlp_atmos_detection:790fe7a0cca6dfe0f4439fb36afd9d40+true-dc4a06c661033cb21e70bb25b661130a+true-565bc55dc9b15f81064785367040a978+true-93866c2e47b7aa3b8cf2483e8beb09ac+true-39d23fdc03248412b4bd364e51e4cf23+true-b41aaef4bd7e736256ff497be64e1e1f+true:passed:20151229-160649:5.913862696 +T_520truehd_mlp_atmos_detection:9337a350fa1e3451ff22b16afe0c770c+true-307f37e86e9df0245aca5908c0409c66+true-e1d9b0e42dc386b54916cfd8d6f6722b+true-531bd72e14816ccacd33202720f94544+true-12b89a0d1fe60bb5271bafbf1b34d275+true-01bcbc0330a73eb417d83c5576378466+true:passed:20151229-160649:2.357134495 T_521mp4_edit_list_constant_offset_with_segment_duration_not_0:07984c40325903cbc35c8ae05dc86a72:passed:20151228-185646:0.621563048 T_522mpeg_1_2_es_no_start_code_at_beginning:067554d94399178b6bb56b49746b04b6:passed:20151230-182435:0.299243222 T_523mpeg_ts_pes_size_0:5351b2c74ad4327eeecee1ccf658ef5f-217f7e6e3e04a6afe253487af08c0598:passed:20151230-221825:1.635929691 diff --git a/tests/test-512json_identification.rb b/tests/test-512json_identification.rb index 177a2e96a..c1f293bdc 100755 --- a/tests/test-512json_identification.rb +++ b/tests/test-512json_identification.rb @@ -26,15 +26,19 @@ test "identification and validation" do files.each do |file| output, _ = identify file, :format => :json output = output.join '' + json = JSON.load(output) - valid, errors = json_schema_identification.validate(JSON.load(output)) + valid, errors = json_schema_identification.validate(json) if !valid puts " JSON validation errors in #{file}:" puts errors.join("\n") end - hashes << "#{output.md5}+#{valid ? "ok" : "invalid"}" + json.delete("identification_format_version") + json_md5 = JSON.dump(json).md5 + + hashes << "#{json_md5}+#{valid ? "ok" : "invalid"}" end hashes.join '-' diff --git a/tests/test-520truehd_mlp_atmos_detection.rb b/tests/test-520truehd_mlp_atmos_detection.rb index 6bfc94380..b74ab0af2 100755 --- a/tests/test-520truehd_mlp_atmos_detection.rb +++ b/tests/test-520truehd_mlp_atmos_detection.rb @@ -22,7 +22,8 @@ def verify520 file, track, codec end - "#{output.md5}+#{ok}" + json.delete("identification_format_version") + JSON.dump(json).md5 + "+#{ok}" end end diff --git a/tests/test.d/simple_test.rb b/tests/test.d/simple_test.rb index eeaf7b032..8de583af9 100644 --- a/tests/test.d/simple_test.rb +++ b/tests/test.d/simple_test.rb @@ -308,7 +308,7 @@ class SimpleTest json_store = JsonSchema::DocumentStore.new parser = JsonSchema::Parser.new expander = JsonSchema::ReferenceExpander.new - schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v2.json")) + schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v3.json")) expander.expand(schema, store: json_store) json_store.add_schema schema