diff --git a/api/TimeAddressableMediaStore.yaml b/api/TimeAddressableMediaStore.yaml index 1752dff7..b1ab3b0f 100644 --- a/api/TimeAddressableMediaStore.yaml +++ b/api/TimeAddressableMediaStore.yaml @@ -146,6 +146,183 @@ paths: $ref: schemas/storage-backends-list.json example: $ref: examples/storage-backends-get-200.json + /service/profiles: + head: + summary: Profile Backend Information + description: Return Profile headers + operationId: HEAD_profiles + tags: + - Profiles + parameters: + - name: format + in: query + description: Filter on Profile format. + schema: + $ref: 'schemas/content-format.json' + - name: codec + in: query + description: Filter on Profile codec. + schema: + $ref: 'schemas/mime-type.json' + - name: label + in: query + description: Filter on Profiles that have the given label. + schema: + type: string + - $ref: '#/components/parameters/trait_resource_paged_key' + - $ref: '#/components/parameters/trait_paged_limit' + responses: + "200": + description: "" + headers: + Link: + description: Provides references to cursors for paging. Only the 'rel' attribute with value 'next' and a link to the next page is currently supported. If 'next' is not present then it is the last page. + schema: + type: string + X-Paging-Limit: + description: Identifies the current limit being used for paging. This may not match the requested value if the requested value was too high for the implementation + schema: + type: integer + X-Paging-NextKey: + description: Opaque string that can be supplied to the `page` query parameter to get the next page of results. + schema: + type: string + content: + application/json: + schema: + type: string + "400": + description: Bad request. Invalid query options. + get: + summary: Profile Backend Information + description: List of Profiles supported by the store instance for use when creating flows. + operationId: GET_profiles + tags: + - Profiles + parameters: + - name: format + in: query + description: Filter on Profile format. + schema: + $ref: 'schemas/content-format.json' + - name: codec + in: query + description: Filter on Profile codec. + schema: + $ref: 'schemas/mime-type.json' + - name: label + in: query + description: Filter on Profiles that have the given label. + schema: + type: string + - $ref: '#/components/parameters/trait_resource_paged_key' + - $ref: '#/components/parameters/trait_paged_limit' + responses: + "200": + description: "" + headers: + Link: + description: Provides references to cursors for paging. Only the 'rel' attribute with value 'next' and a link to the next page is currently supported. If 'next' is not present then it is the last page. + schema: + type: string + X-Paging-Limit: + description: Identifies the current limit being used for paging. This may not match the requested value if the requested value was too high for the implementation + schema: + type: integer + X-Paging-NextKey: + description: Opaque string that can be supplied to the `page` query parameter to get the next page of results. + schema: + type: string + content: + application/json: + example: + $ref: examples/profiles-get-200.json + schema: + type: array + items: + $ref: "schemas/profile.json" + "400": + description: Bad request. Invalid query options. + /service/profiles/{profileId}: + parameters: + - name: profileId + in: path + required: true + schema: + $ref: 'schemas/uuid.json' + description: The Profile identifier. + head: + summary: Profile Details + description: Return Profile path headers + operationId: HEAD_profiles-profileId + tags: + - Profiles + responses: + "200": + $ref: '#/components/responses/trait_resource_info_head_200' + "404": + $ref: '#/components/responses/trait_resource_info_head_404' + get: + summary: Profile Details + description: Returns Profile metadata. + operationId: GET_profiles-profileId + tags: + - Profiles + responses: + "200": + description: "" + content: + application/json: + schema: + $ref: schemas/profile.json + examples: + video: + summary: Video Profile + value: + $ref: examples/profile-get-200-video.json + jpegimage: + summary: Image Profile + value: + $ref: examples/profile-get-200-image.json + "404": + description: The requested profile does not exist. + post: + summary: Create Profile + description: | + Create a new Profile to be used when creating flows with the matching metadata. + + It is not possible to update an existing Profile as this would mean it no longer matches flows that have already been created using it. + If a change to a Profile is required then a new Profile should be created with the updated information and new flows reference this new Profile. + + Attempting to update an existing profiles should return a 400 error. + + Tags can be created at the Profile level to hold information about the Profile itself. + These do not form part of the flow that is created. + These are tag/value format and could be used for tasks such as version tracking or holding encoding parameters. + operationId: POST_profiles-profileId + tags: + - Profiles + requestBody: + content: + application/json: + example: + $ref: examples/profile-put.json + schema: + $ref: schemas/profile.json + required: true + responses: + "201": + description: The flow has been created. + content: + application/json: + example: + $ref: examples/profile-post-201.json + schema: + $ref: schemas/profile.json + "400": + description: Bad request. Invalid Profile JSON or Profile already exists. + "404": + description: The requested Profile ID in the path is invalid. /service/webhooks: head: summary: List Webhook URLs @@ -832,6 +1009,11 @@ paths: description: Filter on Flow codec. schema: $ref: 'schemas/mime-type.json' + - name: profile_id + in: query + description: Filter on Profile identifier. + schema: + $ref: 'schemas/uuid.json' - name: label in: query description: Filter on Flows that have the given label. @@ -919,6 +1101,11 @@ paths: description: Filter on Flow codec. schema: $ref: 'schemas/mime-type.json' + - name: profile_id + in: query + description: Filter on Profile identifier. + schema: + $ref: 'schemas/uuid.json' - name: label in: query description: Filter on Flows that have the given label. @@ -979,7 +1166,7 @@ paths: schema: type: array items: - $ref: "schemas/flow.json" + $ref: "schemas/flow-get.json" "400": description: Bad request. Invalid query options. /flows/{flowId}: @@ -1041,7 +1228,7 @@ paths: content: application/json: schema: - $ref: schemas/flow.json + $ref: schemas/flow-get.json examples: video: summary: Video Flow - H.264 Codec @@ -1093,6 +1280,7 @@ paths: put: summary: Create or Replace Flow description: | + | Create or replace the Flow metadata. Clients should aim to populate as many of the Flow metadata fields as possible and practical. The fewer parameters that are set, the higher the likelihood that reading clients will have to retrieve the media to determine technical metadata to e.g. configure decoders. @@ -1105,6 +1293,13 @@ paths: Examples of conflicting metadata include `format` not matching, or the `role` in `source_collection` and `flow_collection` not matching. It may also be possible for service implementations to detect some instances where multiple Flows should not be considered of the same Source, such as audio Flows with different numbers of tracks. Further guidance on when Flows/Sources may be considered the same/different may be found in the [Practical Guidance for Media](https://specs.amwa.tv/ms-04/releases/v1.0.0/docs/3.0._Practical_Guidance_for_Media.html) section of AMWA MS-04. + + Flows can be created either by directly supplying all the required technical metadata or by referencing an existing Profile in the store. + When using a Profile, the store will use the technical details (format, codec, essence_parameters etc.) from that profile, along with the metadata provided. + It is up to the store implementation whether this is normalised on the flow creation or read-only. + + When creating a flow using a Profile it is not possible to override any specific fields as this would invalidate the link to the Profile. + Supplying a Profile ID and technical metadata should result in a 400 validation error operationId: PUT_flows-flowId tags: - Flows @@ -1120,8 +1315,12 @@ paths: summary: Multi-essence Flow value: $ref: examples/flow-put-multi.json + profile: + summary: Flow created using a Profile + value: + $ref: examples/flow-put-profile.json schema: - $ref: schemas/flow.json + $ref: schemas/flow-put.json required: true responses: "201": @@ -1131,7 +1330,7 @@ paths: example: $ref: examples/flow-put-201.json schema: - $ref: schemas/flow.json + $ref: schemas/flow-get.json "204": description: No content. The Flow has been updated. "400": @@ -2528,7 +2727,7 @@ webhooks: - flow properties: flow: - $ref: "schemas/flow.json" + $ref: "schemas/flow-get.json" flows/updated: post: security: @@ -2557,7 +2756,7 @@ webhooks: - flow properties: flow: - $ref: "schemas/flow.json" + $ref: "schemas/flow-get.json" flows/deleted: post: security: @@ -2815,6 +3014,8 @@ tags: - name: Flows description: | Sources which have been 'rendered' to a specific encoding/packaging format. + - name: Profiles + description: Centralised management of the technical characteristics of a flow externalDocs: url: 'https://specs.amwa.tv/ms-04/releases/v1.0.0/docs/2.3._Explanation_-_Flow.html' - name: FlowSegments diff --git a/api/examples/flow-put-profile.json b/api/examples/flow-put-profile.json new file mode 100644 index 00000000..d00dcc0e --- /dev/null +++ b/api/examples/flow-put-profile.json @@ -0,0 +1,11 @@ +{ + "id": "6101df05-06bb-41b8-8af4-cf7cd33df209", + "source_id": "41d7f7eb-c48d-4513-9b37-17b418d26d7f", + "description": "audio capture web", + "label": "capture_1", + "tags": { + "input_quality": "web", + "_tams_segmentation_rate": "375/8" + }, + "profile_id": "329b98d6-adeb-418c-8eaf-eca14edc1706" +} \ No newline at end of file diff --git a/api/examples/profile-get-200-image.json b/api/examples/profile-get-200-image.json new file mode 100644 index 00000000..ec0d8a35 --- /dev/null +++ b/api/examples/profile-get-200-image.json @@ -0,0 +1,23 @@ +{ + "id": "ecf7393a-f7ba-4253-9f1a-eebeace5b7b3", + "label": "HD JPG still image ", + "description": "1920 x 1080 JPG image", + "created_by": "tams-dev", + "created": "2013-09-09T19:01:00Z", + "tags": { + "_external_id": "1234567890" + }, + "flow_metadata": { + "format": "urn:x-tam:format:image", + "codec": "image/jpeg", + "container": "image/jpeg", + "essence_parameters": { + "frame_width": 1920, + "frame_height": 1080, + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } +} \ No newline at end of file diff --git a/api/examples/profile-get-200-video.json b/api/examples/profile-get-200-video.json new file mode 100644 index 00000000..b504cf37 --- /dev/null +++ b/api/examples/profile-get-200-video.json @@ -0,0 +1,36 @@ +{ + "id": "329b98d6-adeb-418c-8eaf-eca14edc1706", + "label": "1080p50 TS Video 50Mpbs", + "description": "50Mbps HD video", + "created_by": "tams-dev", + "created": "2008-05-27T18:51:00Z", + "tags": { + "_ffmpeg_command": "...." + }, + "flow_metadata": { + "format": "urn:x-nmos:format:video", + "codec": "video/h264", + "container": "video/mp2t", + "avg_bit_rate": 2479, + "segment_duration": { + "numerator": 6, + "denominator": 1 + }, + "essence_parameters": { + "frame_rate": { + "numerator": 25, + "denominator": 1 + }, + "frame_width": 1280, + "frame_height": 720, + "bit_depth": 8, + "interlace_mode": "progressive", + "colorspace": "BT709", + "transfer_characteristic": "SDR", + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } +} \ No newline at end of file diff --git a/api/examples/profile-post-201.json b/api/examples/profile-post-201.json new file mode 100644 index 00000000..b504cf37 --- /dev/null +++ b/api/examples/profile-post-201.json @@ -0,0 +1,36 @@ +{ + "id": "329b98d6-adeb-418c-8eaf-eca14edc1706", + "label": "1080p50 TS Video 50Mpbs", + "description": "50Mbps HD video", + "created_by": "tams-dev", + "created": "2008-05-27T18:51:00Z", + "tags": { + "_ffmpeg_command": "...." + }, + "flow_metadata": { + "format": "urn:x-nmos:format:video", + "codec": "video/h264", + "container": "video/mp2t", + "avg_bit_rate": 2479, + "segment_duration": { + "numerator": 6, + "denominator": 1 + }, + "essence_parameters": { + "frame_rate": { + "numerator": 25, + "denominator": 1 + }, + "frame_width": 1280, + "frame_height": 720, + "bit_depth": 8, + "interlace_mode": "progressive", + "colorspace": "BT709", + "transfer_characteristic": "SDR", + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } +} \ No newline at end of file diff --git a/api/examples/profile-put.json b/api/examples/profile-put.json new file mode 100644 index 00000000..2b97fe17 --- /dev/null +++ b/api/examples/profile-put.json @@ -0,0 +1,34 @@ +{ + "id": "329b98d6-adeb-418c-8eaf-eca14edc1706", + "label": "1080p50 TS Video 50Mpbs", + "description": "50Mbps HD video", + "tags": { + "_ffmpeg_command": "...." + }, + "flow_metadata": { + "format": "urn:x-nmos:format:video", + "codec": "video/h264", + "container": "video/mp2t", + "avg_bit_rate": 2479, + "segment_duration": { + "numerator": 6, + "denominator": 1 + }, + "essence_parameters": { + "frame_rate": { + "numerator": 25, + "denominator": 1 + }, + "frame_width": 1280, + "frame_height": 720, + "bit_depth": 8, + "interlace_mode": "progressive", + "colorspace": "BT709", + "transfer_characteristic": "SDR", + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } +} \ No newline at end of file diff --git a/api/examples/profiles-get-200.json b/api/examples/profiles-get-200.json new file mode 100644 index 00000000..dd873858 --- /dev/null +++ b/api/examples/profiles-get-200.json @@ -0,0 +1,62 @@ +[ + { + "id": "329b98d6-adeb-418c-8eaf-eca14edc1706", + "label": "1080p50 TS Video 50Mpbs", + "description": "50Mbps HD video", + "created_by": "tams-dev", + "created": "2008-05-27T18:51:00Z", + "tags": { + "_ffmpeg_command": "...." + }, + "flow_metadata": { + "format": "urn:x-nmos:format:video", + "codec": "video/h264", + "container": "video/mp2t", + "avg_bit_rate": 2479, + "segment_duration": { + "numerator": 6, + "denominator": 1 + }, + "essence_parameters": { + "frame_rate": { + "numerator": 25, + "denominator": 1 + }, + "frame_width": 1280, + "frame_height": 720, + "bit_depth": 8, + "interlace_mode": "progressive", + "colorspace": "BT709", + "transfer_characteristic": "SDR", + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } + }, + { + "id": "ecf7393a-f7ba-4253-9f1a-eebeace5b7b3", + "urn": "urn:x-tam:profile:image_1920_1080_jpg", + "label": "HD JPG still image ", + "description": "1920 x 1080 JPG image", + "created_by": "tams-dev", + "created": "2013-09-09T19:01:00Z", + "tags": { + "_external_id": "1234567890" + }, + "flow_metadata": { + "format": "urn:x-tam:format:image", + "codec": "image/jpeg", + "container": "image/jpeg", + "essence_parameters": { + "frame_width": 1920, + "frame_height": 1080, + "aspect_ratio": { + "numerator": 16, + "denominator": 9 + } + } + } + } +] \ No newline at end of file diff --git a/api/schemas/flow-audio.json b/api/schemas/flow-audio.json index b4d30010..fe3bd0f9 100644 --- a/api/schemas/flow-audio.json +++ b/api/schemas/flow-audio.json @@ -2,96 +2,94 @@ "title": "Audio Flow", "description": "Describes an audio Flow", "type": "object", - "allOf": + "allOf": [ { - "$ref": "flow-core.json" + "$ref": "flow-technical.json" }, { "type": "object", - "required": - [ + "required": [ "format", "essence_parameters", "codec" ], - "properties": + "properties": { - "format": + "format": { "description": "The primary content type URN for the Flow.", "type": "string", - "enum": + "enum": [ "urn:x-nmos:format:audio" ] }, - "essence_parameters": + "essence_parameters": { "title": "Audio Flow Essence Parameters", "description": "Describes the parameters of the essence inside this audio Flow", "type": "object", - "required": + "required": [ "sample_rate", "channels" ], "additionalProperties": false, - "properties": + "properties": { - "sample_rate": + "sample_rate": { "description": "The fixed number of samples per second.", "type": "integer", "exclusiveMinimum": 0 }, - "channels": + "channels": { "description": "The channel count.", "type": "integer", "exclusiveMinimum": 0 }, - "bit_depth": + "bit_depth": { "description": "The number of significant bits used to represent the audio sample. The minumum number of bytes then equals `round_up(bit_depth / 8)`. If codec is `audio/x-raw-int` bit_depth must be set. If codec is `audio/x-raw-float` bit_depth must be set to 32 or 64", "type": "integer", "exclusiveMinimum": 0 }, - "codec_parameters": + "codec_parameters": { "title": "Audio Codec Parameters", "type": "object", - "required": - [], - "properties": + "required": [], + "properties": { - "coded_frame_size": + "coded_frame_size": { "description": "The fixed number of samples per coded audio frame.", "type": "integer" }, - "mp4_oti": + "mp4_oti": { "description": "The MPEG-4 Object Type Identification. For more information on the use of this property in codec strings, see https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#mpeg-4_audio", "type": "integer" } } }, - "unc_parameters": + "unc_parameters": { "title": "Uncompressed Audio Parameters", "type": "object", - "required": + "required": [ "unc_type" ], - "properties": + "properties": { - "unc_type": + "unc_type": { "description": "The uncompressed audio multi-channel representation type. If codec is `audio/x-raw-int` or `audio/x-raw-float`, unc_type must be set.", "type": "string", - "enum": + "enum": [ "interleaved", "planar", diff --git a/api/schemas/flow-common.json b/api/schemas/flow-common.json new file mode 100644 index 00000000..f2b80f14 --- /dev/null +++ b/api/schemas/flow-common.json @@ -0,0 +1,101 @@ +{ + "type": "object", + "description": "Describes the the core fields of a Flow (common properties to all Flows, imported by flow-get and flow-put specifications)", + "title": "Flow Common", + "required": + [ + "id", + "source_id" + ], + "properties": + { + "id": + { + "description": "Flow identifier", + "$ref": "uuid.json" + }, + "source_id": + { + "description": "Source identifier", + "$ref": "uuid.json" + }, + "label": + { + "description": "Freeform string label for the Flow. This should be a very short, human-readable label that may be displayed in listings of Flows.", + "type": "string" + }, + "description": + { + "description": "Freeform text describing the Flow. This should be a human-readable description that may be showed in detailed views of Flows. The description should be longer and more detailed than `label`.", + "type": "string" + }, + "created_by": + { + "description": "A string identifier for the entity that created the Flow. Service implementations SHOULD set suitable default values for `created_by` based on the principal accessing the system, and MAY permit clients to edit the value, subject to suitable permissions-based limitations.", + "type": "string" + }, + "updated_by": + { + "description": "A string identifier for the entity that updated the Flow metadata most recently. Service implementations SHOULD set suitable default values for `updated_by` based on the principal accessing the system, and MAY permit clients to edit the value, subject to suitable permissions-based limitations.", + "type": "string" + }, + "tags": + { + "description": "Key value is a freeform string. WARNING: When updating a Flow with `tags` set, `tags` will be replaced with the provided dictionary. `tags` WILL NOT be merged with the provided values. When `tags` is not set in the request, `tags` will be unset (i.e. set to `{}`). To update individual tags, clients should use the [Create or Update Flow Tag](#/operations/PUT_flows-flowId-tags-name) endpoint.", + "$ref": "tags.json" + }, + "metadata_version": + { + "description": "A change to the Flow metadata, not including metadata_version, metadata_updated, segments_updated or Segments, results in a new version. If the metadata_version for Flow instances is identical then the metadata is identical. Service implementations SHOULD set suitable default values for `metadata_version` whenever Flow metadata is changed and `metadata_version` is either not set by the client, or set to it's existing value. Service implementations MAY permit clients to edit the value, subject to suitable permissions-based limitations. Where media is transfered between TAMS service instances without changing the Flow metadata, clients SHOULD maintain the `metadata_version`. To support this, service implementations SHOULD always accept the setting of `metadata_version` by the client on initial Flow creation. Service implementations SHOULD update this field where metadata is updated via child endpoints. Note that this specification places no requirements on incremental versioning. Service implementations may, for example, choose to use hashes or date-time version identifiers.", + "type": "string" + }, + "generation": + { + "description": "An indication of how many lossy encodings the Flow content has been through. This parameter provides a hint to clients as to which is the \"highest qualty\" Flow available to them. A Flow with a higher generation may contain less of the original information than a flow with a lower generation. Where a Flow is captured straight from the orginating device (e.g. camera/microphone) in its highest quality, and there is no possibility of the content becoming available in a higher quality (e.g. via capture from ST2110 or SDI), it SHOULD have a `generation` of `0`. Where the originating device outputs multiple qualities of the Source, `generation` should represent the encoding processes each has been through as accurately as possible.", + "type": "integer", + "minimum": 0 + }, + "created": + { + "description": "The date-time the Flow was created in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", + "type": "string", + "format": "date-time" + }, + "metadata_updated": + { + "description": "The date-time the Flow metadata was updated in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", + "type": "string", + "format": "date-time" + }, + "segments_updated": + { + "description": "The date-time the Flow Segments were updated in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", + "type": "string", + "format": "date-time" + }, + "read_only": + { + "description": "If set to 'true', service implementations SHOULD reject client requests to update Flow metadata (other than the read_only property), and Flow Segments. Service implementations should also reject requests to the [`/flows/{flowId}/storage`](#/operations/POST_flows-flowId-storage) endpoint for the Flow, and requests to delete the Flow.", + "type": "boolean" + }, + "timerange": + { + "description": "The timerange of samples available in the Flow, as described by the [TimeRange](../schemas/timerange#top) type. Service implementations MUST ignore this if given in a PUT request, and instead manage it internally.", + "$ref": "timerange.json" + }, + "flow_collection": + { + "description": "List of Flows that are collected together by this Flow.", + "$ref": "flow-collection.json" + }, + "collected_by": + { + "type": "array", + "description": "Flows that reference this Flow to include it in a collection. This attribute is intended to be read-only. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", + "items": + { + "$ref": "uuid.json" + } + } + } +} \ No newline at end of file diff --git a/api/schemas/flow-core.json b/api/schemas/flow-core.json deleted file mode 100644 index 2ad77c37..00000000 --- a/api/schemas/flow-core.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "title": "Flow Core", - "description": "Describes a Flow (common properties to all Flows, imported by type-specific specifications)", - "type": "object", - "required": - [ - "id", - "source_id" - ], - "properties": - { - "id": - { - "description": "Flow identifier", - "$ref": "uuid.json" - }, - "source_id": - { - "description": "Source identifier", - "$ref": "uuid.json" - }, - "label": - { - "description": "Freeform string label for the Flow. This should be a very short, human-readable label that may be displayed in listings of Flows.", - "type": "string" - }, - "description": - { - "description": "Freeform text describing the Flow. This should be a human-readable description that may be showed in detailed views of Flows. The description should be longer and more detailed than `label`.", - "type": "string" - }, - "created_by": - { - "description": "A string identifier for the entity that created the Flow. Service implementations SHOULD set suitable default values for `created_by` based on the principal accessing the system, and MAY permit clients to edit the value, subject to suitable permissions-based limitations.", - "type": "string" - }, - "updated_by": - { - "description": "A string identifier for the entity that updated the Flow metadata most recently. Service implementations SHOULD set suitable default values for `updated_by` based on the principal accessing the system, and MAY permit clients to edit the value, subject to suitable permissions-based limitations.", - "type": "string" - }, - "tags": - { - "description": "Key value is a freeform string. WARNING: When updating a Flow with `tags` set, `tags` will be replaced with the provided dictionary. `tags` WILL NOT be merged with the provided values. When `tags` is not set in the request, `tags` will be unset (i.e. set to `{}`). To update individual tags, clients should use the [Create or Update Flow Tag](#/operations/PUT_flows-flowId-tags-name) endpoint.", - "$ref": "tags.json" - }, - "metadata_version": - { - "description": "A change to the Flow metadata, not including metadata_version, metadata_updated, segments_updated, or Segments, results in a new version. If the metadata_version for Flow instances is identical then the metadata is identical. Service implementations SHOULD set suitable default values for `metadata_version` whenever Flow metadata is changed and `metadata_version` is either not set by the client, or set to it's existing value. Service implementations MAY permit clients to edit the value, subject to suitable permissions-based limitations. Where media is transfered between TAMS service instances without changing the Flow metadata, clients SHOULD maintain the `metadata_version`. To support this, service implementations SHOULD always accept the setting of `metadata_version` by the client on initial Flow creation. Service implementations SHOULD update this field where metadata is updated via child endpoints. Note that this specification places no requirements on incremental versioning. Service implementations may, for example, choose to use hashes or date-time version identifiers.", - "type": "string" - }, - "generation": - { - "description": "An indication of how many lossy encodings the Flow content has been through. This parameter provides a hint to clients as to which is the \"highest qualty\" Flow available to them. A Flow with a higher generation may contain less of the original information than a Flow with a lower generation. Where a Flow is captured straight from the orginating device (e.g. camera/microphone) in its highest quality, and there is no possibility of the content becoming available in a higher quality (e.g. via capture from ST2110 or SDI), it SHOULD have a `generation` of `0`. Where the originating device outputs multiple qualities of the Source, `generation` should represent the encoding processes each has been through as accurately as possible.", - "type": "integer", - "minimum": 0 - }, - "created": - { - "description": "The date-time the Flow was created in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", - "type": "string", - "format": "date-time" - }, - "metadata_updated": - { - "description": "The date-time the Flow metadata was updated in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", - "type": "string", - "format": "date-time" - }, - "segments_updated": - { - "description": "The date-time the Flow Segments were updated in a given context, e.g. in the service instance. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", - "type": "string", - "format": "date-time" - }, - "read_only": - { - "description": "If set to 'true', service implementations SHOULD reject client requests to update Flow metadata (other than the read_only property), and Flow Segments. Service implementations should also reject requests to the [`/flows/{flowId}/storage`](#/operations/POST_flows-flowId-storage) endpoint for the Flow, and requests to delete the Flow.", - "type": "boolean" - }, - "codec": - { - "description": "A MIME type identification of the (lossy or lossless) coding used for the Flow content. Note that the `type` component of the container MIME type (i.e. the component before the `/`) may be different to the `type` component of the codec MIME type. e.g. An audio Flow may have `audio/aac` coded content may be wrapped in a `video/mp2t` container. Mime types from the [IANA registry](https://www.iana.org/assignments/media-types/media-types.xhtml) should be preferred. Where multiple MIME types are possible, the most common should be preferred. Where this is insufficient, the maintainers of the TAMS repository may create an application note advising which MIME type to use.", - "$ref": "mime-type.json" - }, - "container": - { - "description": "The container MIME type for Flow Segments. Note that the `type` component of the container MIME type (i.e. the component before the `/`) may be different to the `type` component of the codec MIME type. e.g. An audio Flow may have `audio/aac` coded content may be wrapped in a `video/mp2t` container. Where multiple types exist for a subtype (e.g. `video/mp4`, `audio/mp4`, `application/mp4`), the closest MIME type to the Flow `format` should be used (e.g. `audio/mp4` for a Flow `format` of `urn:x-nmos:format:audio`). Mime types from the [IANA registry](https://www.iana.org/assignments/media-types/media-types.xhtml) should be preferred. Where multiple MIME types are possible, the most common should be preferred. Where this is insufficient, the maintainers of the TAMS repository may create an application note advising which MIME type to use.", - "$ref": "mime-type.json" - }, - "avg_bit_rate": - { - "description": "The average bit rate of the Flow Segments in 1000 bits/second. A precise definition can be found in the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote.", - "type": "integer", - "minimum": 0 - }, - "max_bit_rate": - { - "description": "The maximum bit rate of the Flow Segments in 1000 bits/second. A precise definition can be found in the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote.", - "type": "integer", - "minimum": 0 - }, - "segment_duration": - { - "description": "The target Flow Segment duration in seconds. The duration for each Segment may vary around this target value. See also the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote for how this property can be used to calculate buffer sizes.", - "type": "object", - "required": - [ - "numerator" - ], - "properties": - { - "numerator": - { - "description": "numerator", - "type": "integer", - "exclusiveMinimum": 0 - }, - "denominator": - { - "description": "denominator", - "type": "integer", - "default": 1, - "exclusiveMinimum": 0 - } - } - }, - "timerange": - { - "description": "The timerange of samples available in the Flow, as described by the [TimeRange](#/schemas/timerange) type. Service implementations MUST ignore this if given in a PUT request, and instead manage it internally.", - "$ref": "timerange.json" - }, - "flow_collection": - { - "description": "List of Flows that are collected together by this Flow.", - "$ref": "flow-collection.json" - }, - "collected_by": - { - "type": "array", - "description": "Flows that reference this Flow to include it in a collection. This attribute is intended to be read-only. Service implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", - "items": - { - "$ref": "uuid.json" - } - }, - "container_mapping": - { - "description": "Describes the mapping of the Flow essence from the this Flow's container", - "$ref": "container-mapping.json" - } - } -} \ No newline at end of file diff --git a/api/schemas/flow-data.json b/api/schemas/flow-data.json index 3dd84beb..15b57173 100644 --- a/api/schemas/flow-data.json +++ b/api/schemas/flow-data.json @@ -1,47 +1,39 @@ { - "title": "Data Flow", - "description": "Describes a data Flow", - "type": "object", - "allOf": - [ - { - "$ref": "flow-core.json" + "title": "Data Flow", + "description": "Describes a data Flow", + "type": "object", + "allOf": [ + { + "$ref": "flow-technical.json" + }, + { + "type": "object", + "required": [ + "format", + "essence_parameters", + "codec" + ], + "properties": { + "format": { + "description": "The primary content type URN for the flow.", + "type": "string", + "enum": [ + "urn:x-nmos:format:data" + ] }, - { - "type": "object", - "required": - [ - "format", - "essence_parameters", - "codec" - ], - "properties": - { - "format": - { - "description": "The primary content type URN for the Flow.", - "type": "string", - "enum": - [ - "urn:x-nmos:format:data" - ] - }, - "essence_parameters": - { - "title": "Data Flow Essence Parameters", - "description": "Describes the parameters of the essence inside this data Flow", - "type": "object", - "additionalProperties": false, - "properties": - { - "data_type": - { - "description": "The type of information encoded in the Flow, identified using a URN. e.g. The data_type may be urn:x-tams:data:bounding-box, and the codec `application/json`.", - "type": "string" - } - } - } + "essence_parameters": { + "title": "Data Flow Essence Parameters", + "description": "Describes the parameters of the essence inside this data Flow", + "type": "object", + "additionalProperties": false, + "properties": { + "data_type": { + "description": "The type of information encoded in the Flow, identified using a URN. e.g. The data_type may be urn:x-tams:data:bounding-box, and the codec `application/json`.", + "type": "string" } + } } - ] + } + } + ] } \ No newline at end of file diff --git a/api/schemas/flow-get.json b/api/schemas/flow-get.json new file mode 100644 index 00000000..ed77f2e5 --- /dev/null +++ b/api/schemas/flow-get.json @@ -0,0 +1,30 @@ +{ + "title": "Flow", + "description": "Describes a Flow", + "type": "object", + "properties": + { + "profile_id": + { + "description": "Profile identifier that was used to create the flow.", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + }, + "allOf": + [ + { + "$ref": "flow-common.json" + }, + { + "oneOf": + [ + { "$ref": "flow-video.json" }, + { "$ref": "flow-audio.json" }, + { "$ref": "flow-image.json" }, + { "$ref": "flow-data.json" }, + { "$ref": "flow-multi.json" } + ] + } + ] +} \ No newline at end of file diff --git a/api/schemas/flow-image.json b/api/schemas/flow-image.json index 3705aa2f..224af6a1 100644 --- a/api/schemas/flow-image.json +++ b/api/schemas/flow-image.json @@ -4,7 +4,7 @@ "type": "object", "allOf": [ { - "$ref": "flow-core.json" + "$ref": "flow-technical.json" }, { "type": "object", diff --git a/api/schemas/flow-multi.json b/api/schemas/flow-multi.json index 3fe24ceb..bf1565de 100644 --- a/api/schemas/flow-multi.json +++ b/api/schemas/flow-multi.json @@ -1,30 +1,17 @@ { - "title": "Multi-essence Flow", - "description": "Describes a multi-essence Flow", - "type": "object", - "allOf": - [ - { - "$ref": "flow-core.json" - }, - { - "type": "object", - "required": - [ - "format" - ], - "properties": - { - "format": - { - "description": "The primary content type URN for the Flow.", - "type": "string", - "enum": - [ - "urn:x-nmos:format:multi" - ] - } - } - } - ] + "title": "Multi-essence Flow", + "description": "Describes a multi-essence Flow", + "type": "object", + "required": [ + "format" + ], + "properties": { + "format": { + "description": "The primary content type URN for the flow.", + "type": "string", + "enum": [ + "urn:x-nmos:format:multi" + ] + } + } } \ No newline at end of file diff --git a/api/schemas/flow-put.json b/api/schemas/flow-put.json new file mode 100644 index 00000000..dfc3266f --- /dev/null +++ b/api/schemas/flow-put.json @@ -0,0 +1,33 @@ +{ + "type": "object", + "description": "Describes a Flow", + "title": "Flow", + "allOf": [ + { + "$ref": "flow-common.json" + }, + { + "oneOf": [ + { + "type": "object", + "title": "Flow Profile", + "required": [ + "profile_id" + ], + "properties": { + "profile_id": { + "description": "Profile identifier that was used to create the flow. When supplying a profile_id no technical metadata should also be provided. Doing so will result in a 400 validation error", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + } + } + }, + { "$ref": "flow-video.json" }, + { "$ref": "flow-audio.json" }, + { "$ref": "flow-image.json" }, + { "$ref": "flow-data.json" }, + { "$ref": "flow-multi.json" } + ] + } + ] +} \ No newline at end of file diff --git a/api/schemas/flow-technical.json b/api/schemas/flow-technical.json new file mode 100644 index 00000000..7b6e7869 --- /dev/null +++ b/api/schemas/flow-technical.json @@ -0,0 +1,61 @@ +{ + "title": "Flow Technical", + "description": "Describes the technical characteristics of a Flow (imported by type-specific specifications or as part of a profile)", + "type": "object", + "properties": { + + "codec": + { + "description": "A MIME type identification of the (lossy or lossless) coding used for the Flow content. Note that the `type` component of the container MIME type (i.e. the component before the `/`) may be different to the `type` component of the codec MIME type. e.g. An audio Flow may have `audio/aac` coded content may be wrapped in a `video/mp2t` container. Mime types from the [IANA registry](https://www.iana.org/assignments/media-types/media-types.xhtml) should be preferred. Where multiple MIME types are possible, the most common should be preferred. Where this is insufficient, the maintainers of the TAMS repository may create an application note advising which MIME type to use.", + "type": "string", + "pattern": "^[^\\s\/]+/[^\\s\/]+$" + }, + "container": + { + "description": "The container MIME type for Flow Segments. Note that the `type` component of the container MIME type (i.e. the component before the `/`) may be different to the `type` component of the codec MIME type. e.g. An audio Flow may have `audio/aac` coded content may be wrapped in a `video/mp2t` container. Where multiple types exist for a subtype (e.g. `video/mp4`, `audio/mp4`, `application/mp4`), the closest MIME type to the Flow `format` should be used (e.g. `audio/mp4` for a Flow `format` of `urn:x-nmos:format:audio`). Mime types from the [IANA registry](https://www.iana.org/assignments/media-types/media-types.xhtml) should be preferred. Where multiple MIME types are possible, the most common should be preferred. Where this is insufficient, the maintainers of the TAMS repository may create an application note advising which MIME type to use.", + "type": "string", + "pattern": "^[^\\s\/]+/[^\\s\/]+$" + }, + "avg_bit_rate": + { + "description": "The average bit rate of the Flow Segments in 1000 bits/second. A precise definition can be found in the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote.", + "type": "integer", + "minimum": 0 + }, + "max_bit_rate": + { + "description": "The maximum bit rate of the Flow Segments in 1000 bits/second. A precise definition can be found in the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote.", + "type": "integer", + "minimum": 0 + }, + "segment_duration": + { + "description": "The target Flow Segment duration in seconds. The duration for each Segment may vary around this target value. See also the [Setting Flow Bit Rate Properties](https://github.com/bbc/tams/blob/main/docs/appnotes/0013-setting-flow-bit-rate-properties.md) AppNote for how this property can be used to calculate buffer sizes.", + "type": "object", + "required": + [ + "numerator" + ], + "properties": + { + "numerator": + { + "description": "numerator", + "type": "integer", + "exclusiveMinimum": 0 + }, + "denominator": + { + "description": "denominator", + "type": "integer", + "default": 1, + "exclusiveMinimum": 0 + } + } + }, + "container_mapping": { + "description": "Describes the mapping of the Flow essence from the this Flow's container", + "$ref": "container-mapping.json" + } + } +} diff --git a/api/schemas/flow-video.json b/api/schemas/flow-video.json index 02b7e5e5..955e962d 100644 --- a/api/schemas/flow-video.json +++ b/api/schemas/flow-video.json @@ -4,7 +4,7 @@ "type": "object", "allOf": [ { - "$ref": "flow-core.json" + "$ref": "flow-technical.json" }, { "type": "object", diff --git a/api/schemas/flow.json b/api/schemas/flow.json deleted file mode 100644 index 9a8b70a0..00000000 --- a/api/schemas/flow.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "title": "Flow", - "description": "Describes a Flow", - "type": "object", - "oneOf": - [ - { - "$ref": "flow-video.json" - }, - { - "$ref": "flow-audio.json" - }, - { - "$ref": "flow-image.json" - }, - { - "$ref": "flow-data.json" - }, - { - "$ref": "flow-multi.json" - } - ] -} \ No newline at end of file diff --git a/api/schemas/profile.json b/api/schemas/profile.json new file mode 100644 index 00000000..198c61a9 --- /dev/null +++ b/api/schemas/profile.json @@ -0,0 +1,57 @@ +{ + "type": "object", + "description": "Describes a Profile", + "title": "Profile", + "required": + [ + "id", + "flow_metadata" + ], + "properties": + { + "id": + { + "description": "Profile identifier.", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "label": + { + "description": "Freeform string label for the Profile", + "type": "string" + }, + "description": + { + "description": "Freeform text describing the Profile", + "type": "string" + }, + "created_by": + { + "description": "A string identifier for the entity that created the Profile. Implementations SHOULD set suitable default values for `created_by` based on the principal accessing the system, and MAY permit clients to edit the value, subject to suitable permissions-based limitations.", + "type": "string" + }, + "created": + { + "description": "The date-time the Profile was created in a given context, e.g. in the store. Implementations SHOULD ignore this if given in a PUT request, and instead manage it internally", + "type": "string", + "format": "date-time" + }, + "tags": + { + "description": "Key value is a freeform string. As Profiles are considered immutable then this also applies to tags which cannot be updated after a Profile has been created", + "$ref": "tags.json" + }, + "flow_metadata": + { + "description": "The technical characteristics of the Profile. This section will be mapped directly to all flows created using this Profile.", + "type": "object", + "oneOf": + [ + { "$ref": "flow-video.json" }, + { "$ref": "flow-audio.json" }, + { "$ref": "flow-image.json" }, + { "$ref": "flow-data.json" } + ] + } + } +} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index bb3c89a8..03a14eea 100644 --- a/docs/README.md +++ b/docs/README.md @@ -79,3 +79,28 @@ For more information on how we use ADRs, see [here](./adr/README.md). | [0042](./adr/0042-uncontrolled-object-instance-labels.md) | Make `label` Mandatory for Uncontrolled Object Instances | \* Note: ADR 0004a was the unintended result of a number clash in the early development of TAMS which wasn't caught before publication + +## Application Notes + +Application notes are informatative documents describing the recommended usage of the API. +For more information on how we use application notes, see [here](./appnotes/README.md). + +| Application Note Number | Title | +| -------------------------------------------------------------------- | --------------------------------------------------------------- | +| [0001](./appnotes/0001-multi-mono-essence-flows-sources.md) | Practical Application of the TAMS Content Model | +| [0002](./appnotes/0002-Timing-in-MPEG-TS.md) | Timing in MPEG-TS | +| [0003](./appnotes/0003-tag-names.md) | Tags, how to use them, and how we manage them | +| [0004](./appnotes/0004-tams-for-data.md) | When TAMS is a good fit for non-media data. And when it’s not | +| [0005](./appnotes/0005-indepentent-segments.md) | Media objects should be independently decodable. Here's why | +| [0006](./appnotes/0006-containers-and-mappings.md) | Containers and Mappings | +| [0007](./appnotes/0007-populating-source-metadata.md) | Populating Source Metadata | +| [0008](./appnotes/0008-timestamps-in-TAMS.md) | Timestamps in TAMS | +| [0009](./appnotes/0009-storage-label-format.md) | Storage label format specification - **Superseded by ADR-0032** | +| [0010](./appnotes/0010-long-running-sources-and-flows.md) | Long-running Sources and Flows | +| [0011](./appnotes/0011-c2pa.md) | C2PA provenance across related Sources and Flows | +| [0012](./appnotes/0012-using-flow-segment-timeranges.md) | Using Flow Segment timeranges | +| [0013](./appnotes/0013-setting-flow-bit-rate-properties.md) | Setting Flow bit rate properties | +| [0014](./appnotes/0014-referencing-tams-content-in-other-systems.md) | Referencing TAMS content in other systems | +| [0015](./appnotes/0015-using-tams-in-opentimelineio.md) | Using TAMS in OpenTimelineIO | +| [0017](./appnotes/0017-reuse-of-ids.md) | When to re-use IDs in TAMS and compatible systems | +| [0019](./appnotes/0019-using-flow-profiles.md) | Using flow profiles | diff --git a/docs/appnotes/0019-using-flow-profiles.md b/docs/appnotes/0019-using-flow-profiles.md new file mode 100644 index 00000000..0ea1bcca --- /dev/null +++ b/docs/appnotes/0019-using-flow-profiles.md @@ -0,0 +1,90 @@ +# 0019: Using Flow Profiles + +## Abstract + +The TAMS API is deliberately agnostic to the format of the media which it is managing, to allow for future compatibility with new formats without needing to change the API specification. +This provides challenges for interoperability and workflows such as edit by reference where standard media formats are required across multiple sources. + +This document describes how Flow Profiles can be used within the TAMS API to simplify workflows while maintaining the flexibility of the core API. + +## Challenges + +### House formats + +Within a customer deployment of TAMS it is expected that there would be limited number of recommended formats for users to work in. +Typically this could include a target format for the high quality media and standardised formats for proxy media and images. +These formats are typically referred to as House formats and do not preclude the storing of other content within the store, however it is likely to be normalised at some point to the house format. + +The use of house formats then makes workflows such as ingest, rendering new content after editing, generation of proxy content and edit by reference easier. +House formats define one or more common formats for all the systems to utilise. + +### Edit by reference flow matching + +For edit by reference workflows there is a need both to create new Sources and Flows for the new item and also combine the segments together to create the new edit. + +If the content to be referenced is only coming from a one source (potentially with multiple Flows), for example a simple clip, then it is relatively easy to clone the technical parameters of the existing Flows and then join the Segments to the new Flow. +However if the content for the new edit is derived from multiple Sources then there is a need to match the Flows from the different Sources based on the technical characteristics. +Currently the TAMS API does not provide an easy method to do this, so it is necessary to compare all the Flows in code to find the matches. +The more Sources involved in this process, the harder this becomes. + +## TAMS Profiles within the API + +The TAMS Profile model is split into a number of stages: + +* An endpoint to list and describe the available Profiles supported by the store +* The ability to supply a Profile when creating a Flow and the de-normalisation of the technical details to maintain read compatibility with non-Profile based Flows +* The ability to query Flows for a given Profile to easily and quickly find the required media type + +### Profiles endpoint + +It is possible to create and view Profiles via the dedicated API calls under the TAMS service end point (/services/profiles). +These Profiles hold all the technical parameters required to create a Flow in a single location. +A system looking to ingest standardised content into TAMS would create content matching one or more Profiles as defined in the endpoint. + +A Profile should be treated as immutable, so once created it cannot be updated. +Updating a Profile would cause mismatches with Flows which have been already created using that Profile and so breaks the model for Profiles. +To update a Profile a new one with a new ID should be created. + +The Profiles endpoint has been designed to provide extensibility of the metadata through the same tags model as for Flows and Sources. +This would enable implementations to store additional metadata such as encoding parameters alongside the TAMS Profile metadata + +### Creating a Flow using a Profile + +When creating a Flow the generating system has two options: + +1. Specify all the technical characteristics of the Flow including the wrapper, codec and essence parameters alongside the non-technical parameters such as label and description +2. Provide just the technical Profile for the Flow and non-technical parameters required + +When using the second option on submission of the create Flow request it is simpler for the creating system. +The store is responsible for the de-normalisation of the technical parameters so that when reading from the API the Flows created via both mechanisms have the same technical parameters available. + +Flows that have been created from a Profile will include the parameter indicating which Profile they were created from. +This differentiates them from the Flows that have been created with the technical characteristics directly. + +### Query Flows using a Profile + +The get Flows endpoint has the ability to query for Flows with a given Profile. +When combined with other query parameters such as Source id then this means it is easy to start matching content formats. + +For example on an edit by reference workflow it would be possible to read the Profile endpoint for the recommended profiles that content should have. +This then simplifies the process of creating the required Flows and Sources for the new content item. +The edit process could then process each input source in turn read the available Flows and match them to the destination Flows using the Profile tag easily. +If the Source content has additional non standard Flows then these could be ignored. + +By the nature of the TAMS API it is possible to query via both Profile ID and also the individual parameters of the Flow that have been inherited from the profile. +As per standard behaviour, the API should only return results which match all fields, so for this scenario the Flow must have a profile ID and match the other parameters requested. + +## Multi-store working with Profiles + +The UUID of a Profile is assumed to be globally unique. +This is the same model as for Flow, Source and Object ID's which should be preserved when replicating content. + +For workflows where replication of the same content formats are happening on a regular basis then it is recommended that the same Profile is loaded into both stores using the same UUID. +This will mean than when Flows are replicated between the stores then the Profile identifier will continue to link to the metadata. + +If the Profile does not exist within the destination store then the Profile ID should continue to be preserved. +This will continue to allow the matching of content within the store by Profile ID, plus the Profile can be added later and will link to the existing content. + +For workflows including more than two organisations it is recommended that one organisation takes responsibility for owning and publishing the Profiles. +These Profiles can then be loaded into the destination stores. +The organisation could be a single company or could be an industry body.