diff --git a/NEWS.md b/NEWS.md index 7f6a4494d..42078d525 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,9 @@ ## New features and enhancements +* mkvmerge: the JSON identification result now includes a track's codec delay + if set (only for Matroska source files). The JSON schema version has been + bumped to 6. * mkvmerge: MPEG TS: added a workaround for files where the subtitle packets are mulitplexed properly, but where their timestamps are way off from the audio and video timestamps. Implements #1841. diff --git a/doc/json-schema/mkvmerge-identification-output-schema-v6.json b/doc/json-schema/mkvmerge-identification-output-schema-v6.json new file mode 100644 index 000000000..9e0d51480 --- /dev/null +++ b/doc/json-schema/mkvmerge-identification-output-schema-v6.json @@ -0,0 +1,403 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "https://mkvtoolnix.download/doc/mkvmerge-identification-output-schema-v6.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 + }, + "date_local": { + "description": "The muxing date in ISO 8601 format (in local time zone)", + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([+-][0-9]{2}:[0-9]{2}|Z)$" + }, + "date_utc": { + "description": "The muxing date in ISO 8601 format (in UTC)", + "type": "string", + "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([+-][0-9]{2}:[0-9]{2}|Z)$" + }, + "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" + }, + "muxing_application": { + "description": "A Unicode string containing the name and possibly version of the low-level library or application that created the file", + "type": "string" + }, + "next_segment_uid": { + "description": "A hexadecimal string of the next segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "other_file": { + "description": "An array of names of additional files processed as well", + "type": "array", + "items": { + "type": "string" + } + }, + "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 + }, + "previous_segment_uid": { + "description": "A hexadecimal string of the previous segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "segment_uid": { + "description": "A hexadecimal string of the segment's UID (only for Matroska files)", + "type": "string", + "minLength": 32, + "maxLength": 32 + }, + "title": { + "type": "string" + }, + "writing_application": { + "description": "A Unicode string containing the name and possibly version of the high-level application that created the file", + "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": { + "type": "object", + "properties": { + "num_entries": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "num_entries" + ] + } + }, + "identification_format_version": { + "description": "The output format's version", + "type": "integer", + "minimum": 6, + "maximum": 6 + }, + "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_delay": { + "type": "integer" + }, + "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" + }, + "minimum_timestamp": { + "description": "The minimum timestamp in nanoseconds of all the frames of this track found within the first couple of seconds of the file", + "type": "integer", + "minimum": 0 + }, + "multiplexed_tracks": { + "description": "An array of track IDs indicating which tracks were originally multiplexed within the same track in the source file", + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + } + }, + "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" + }, + "teletext_page": { + "type": "integer", + "minimum": 0 + }, + "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/id_info.h b/src/common/id_info.h index cded4d199..76a9d28b3 100644 --- a/src/common/id_info.h +++ b/src/common/id_info.h @@ -24,6 +24,7 @@ char const * const audio_bits_per_sample = "audio_bits_per_sample"; char const * const audio_channels = "audio_channels"; // track unsigned-integer char const * const audio_output_sampling_frequency = "audio_output_sampling_frequency"; // track unsigned-integer char const * const audio_sampling_frequency = "audio_sampling_frequency"; // track unsigned-integer +char const * const codec_delay = "codec_delay"; // track unsigned-integer char const * const codec_id = "codec_id"; // track unicoode-string char const * const codec_private_data = "codec_private_data"; // track binary char const * const codec_private_length = "codec_private_length"; // track unsigned-integer diff --git a/src/input/r_matroska.cpp b/src/input/r_matroska.cpp index 2601413ca..56519757e 100644 --- a/src/input/r_matroska.cpp +++ b/src/input/r_matroska.cpp @@ -2551,6 +2551,7 @@ kax_reader_c::identify() { info.add(mtx::id::uid, track->track_uid); info.add(mtx::id::codec_id, track->codec_id); info.set(mtx::id::codec_private_length, track->private_size); + info.add(mtx::id::codec_delay, track->codec_delay.to_ns(0)); info.add(mtx::id::language, track->language); info.add(mtx::id::track_name, track->track_name); info.add(mtx::id::stereo_mode, static_cast(track->v_stereo_mode), static_cast(stereo_mode_c::unspecified)); diff --git a/src/merge/id_result.h b/src/merge/id_result.h index 931d7b61e..46e13246d 100644 --- a/src/merge/id_result.h +++ b/src/merge/id_result.h @@ -27,7 +27,7 @@ #define ID_RESULT_TAGS "tags" #define ID_RESULT_GLOBAL_TAGS_ID -1 -#define ID_JSON_FORMAT_VERSION 5 +#define ID_JSON_FORMAT_VERSION 6 struct id_result_t { int64_t id; diff --git a/tests/results.txt b/tests/results.txt index d6d5d6469..e25d74c49 100644 --- a/tests/results.txt +++ b/tests/results.txt @@ -421,3 +421,4 @@ T_572hdmv_textst:0c7491296aca98abf20af85926468bac-0c7491296aca98abf20af85926468b T_573teletext_subs_data_unit_id_0x02_and_multiple_magazines:03b961cb80532c45829bd91345f327d0:passed:20161208-202941:0.037765249 T_574tta_with_apev2_tags:342b123b71e0a40a0688f4647c38b1c5-4870070dccb00f5aaeda933c301050f5:passed:20161223-210853:0.019411679 T_575mpeg_ts_subtitle_timestamps_way_off_from_video_timestamps:138df2136703aa80b5f91e37e445d912:passed:20170106-200606:0.508113744 +T_576codec_delay_in_identification:98e3c65500b0d755397c35c8c9579ed9-123000456:passed:20170107-123513:0.075997603 diff --git a/tests/test-576codec_delay_in_identification.rb b/tests/test-576codec_delay_in_identification.rb new file mode 100755 index 000000000..536af45e8 --- /dev/null +++ b/tests/test-576codec_delay_in_identification.rb @@ -0,0 +1,12 @@ +#!/usr/bin/ruby -w + +# T_576codec_delay_in_identification +describe "mkvmerge / codec delay in JSON identification results" + +test_merge "data/aac/v.aac", :keep_tmp => true + +test "set and verify properties" do + propedit tmp, "--edit track:a1 --set codec-delay=123000456" + + identify_json(tmp)["tracks"][0]["properties"]["codec_delay"] +end diff --git a/tests/test.d/simple_test.rb b/tests/test.d/simple_test.rb index 4bc794c25..c3995920a 100644 --- a/tests/test.d/simple_test.rb +++ b/tests/test.d/simple_test.rb @@ -329,7 +329,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-v5.json")) + schema = parser.parse JSON.load(File.read("../doc/json-schema/mkvmerge-identification-output-schema-v6.json")) expander.expand(schema, store: json_store) json_store.add_schema schema