Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ The store provides a mechanism to upload and register new Flow Segments, and an

The media data contained within Flow Segments may be stored separately from the metadata linking them to a position on the timeline, separating the media data and metadata planes.
For example our implementation uses a database (_e.g._ Amazon DynamoDB) to store Flow Segment metadata and an object store (_e.g._ AWS S3) to store the media data for Flow Segments.
We refer to media data stored in the object store as 'Media Objects'.
We refer to data stored in the object store as 'Objects'.
Where these Objects contain media, they are referred to as 'Media Objects'.
Where they contain initialisation segments required to decode associated media (e.g. for CMAF-compatible media), the are referred to as 'init Objects'.
The Flow Segment has a list of S3 download urls which are the location of the Media Object(s) that contains the stored media data for the Flow Segment.
If multiple URLs are returned in the list, they are assumed to return identical media data.
When writing to the store, the S3 URLs can be passed to a client permitting them to upload media data directly.

> [!NOTE]
> For more information on working with init segments, see [AppNote 0024 - Using Media with Initialisation Segments in TAMS](./docs/appnotes/0024-using-init-segments.md).

Another advantage of separating the media data and metadata planes in this way is that a particular Media Object can be referenced by multiple Flows.
On the metadata side, the Flow Segment is just a mapping of a Media Object's ID to a Flow's Timeline, so any number of Flows can record that same ID against other `<flow_id, timestamp>` tuples.
This allows for copy-on-write semantics: immutability means a new Flow must be created to make changes to existing parts of the timeline, but for unmodified portions of the timeline the new `<flow_id, timestamp>` tuple points to the existing Media Object or a part of it.
Expand Down
230 changes: 138 additions & 92 deletions api/TimeAddressableMediaStore.yaml

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions api/examples/flow-get-200-video-h264-fmp4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"id": "4f79cfd1-c057-47f4-8e4d-1b126ca7bf34",
"source_id": "2aa143ac-0ab7-4d75-bc32-5c00c13d186f",
"generation": 0,
"created": "2008-05-27T18:51:00Z",
"metadata_updated": "2023-09-14T09:45:26Z",
"segments_updated": "2023-09-14T09:45:26Z",
"description": "Big Buck Bunny",
"label": "bbb",
"format": "urn:x-nmos:format:video",
"created_by": "tams-dev",
"updated_by": "tams-dev",
"tags": {
"input_quality": "contribution"
},
"codec": "video/h264",
"container": "video/iso.segment",
"avg_bit_rate": 2479,
"segment_duration": {
"numerator": 10
},
"essence_parameters": {
"init_segments": true,
"frame_rate": {
"numerator": 24,
"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
},
"pixel_aspect_ratio": {
"numerator": 1,
"denominator": 1
},
"component_type": "YCbCr",
"vert_chroma_subs": 2,
"horiz_chroma_subs": 2,
"avc_parameters": {
"profile": 100,
"level": 31,
"flags": 0
}
},
"collected_by": [
"e85efab4-993b-4ad6-9af3-4cd8d0d38860"
]
}
53 changes: 53 additions & 0 deletions api/examples/flow-segments-get-200-init.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[
{
"object_id": "846023d3-612d-5014-bc47-88f6eb2d04bb",
"timerange": "[0:0_10:0)",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/846023d3-612d-5014-bc47-88f6eb2d04bb"
}
],
"init_object": {
"object_id": "9ef06e23-b882-4a93-b005-480de42ef4e9",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/9ef06e23-b882-4a93-b005-480de42ef4e9"
}
]
}
},
{
"object_id": "25be83fc-11d1-5743-9d47-6865cef5ea35",
"timerange": "[10:0_20:0)",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/25be83fc-11d1-5743-9d47-6865cef5ea35"
}
],
"init_object": {
"object_id": "9ef06e23-b882-4a93-b005-480de42ef4e9",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/9ef06e23-b882-4a93-b005-480de42ef4e9"
}
]
}
},
{
"object_id": "8b785422-6a82-5d60-b25a-f77e0a748321",
"timerange": "[20:0_30:0)",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/8b785422-6a82-5d60-b25a-f77e0a748321"
}
],
"init_object": {
"object_id": "192472f1-55fd-45dc-b355-e6833a3a140c",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/192472f1-55fd-45dc-b355-e6833a3a140c"
}
]
}
}
]
22 changes: 22 additions & 0 deletions api/examples/objects-get-200-init.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"id": "846023d3-612d-5014-bc47-88f6eb2d04bb",
"referenced_by_flows": [
"4f79cfd1-c057-47f4-8e4d-1b126ca7bf34",
"0fde9c11-da9d-434a-a113-d3b20a2cf251"
],
"first_referenced_by_flow": "4f79cfd1-c057-47f4-8e4d-1b126ca7bf34",
"timerange": "[150:0_200:0)",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/846023d3-612d-5014-bc47-88f6eb2d04bb"
}
],
"init_object": {
"id": "9ef06e23-b882-4a93-b005-480de42ef4e9",
"get_urls": [
{
"url": "https://store.example.com/tams-e2b89b02-21e7-5f9d-aa2d-db38b01453c9/9ef06e23-b882-4a93-b005-480de42ef4e9"
}
]
}
}
4 changes: 4 additions & 0 deletions api/schemas/flow-audio.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@
]
}
}
},
"init_segments": {
"description": "Whether the Flow makes use of initialisation segments. This parameter MUST be set to `true` if Media Objects have `init_object` populated. If set to `true`, all Media Objects MUST have `init_object` populated. Assume `false` if omitted.",
"type": "boolean"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/schemas/flow-core.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"$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. Where the Flow does not reference any Media Object(s) directly (e.g. an empty Multi Flow that serves only to collect related mono-essence Flows that do reference Media Objects), this property MUST NOT be set.",
"description": "The container MIME type for Flow Segments. Where the media format employs initialisation segments, this is the mime type of the media segments and NOT the initialisation segment(s). 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. Where the Flow does not reference any Media Object(s) directly (e.g. an empty Multi Flow that serves only to collect related mono-essence Flows that do reference Media Objects), this property MUST NOT be set.",
"$ref": "mime-type.json"
},
"avg_bit_rate": {
Expand Down
4 changes: 4 additions & 0 deletions api/schemas/flow-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"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"
},
"init_segments": {
"description": "Whether the Flow makes use of initialisation segments. This parameter MUST be set to `true` if Media Objects have `init_object` populated. If set to `true`, all Media Objects MUST have `init_object` populated. Assume `false` if omitted.",
"type": "boolean"
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions api/schemas/flow-multi.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
"enum": [
"urn:x-nmos:format:multi"
]
},
"essence_parameters": {
"title": "Multi Flow Essence Parameters",
"description": "Describes the parameters of the essence inside this multi Flow",
"type": "object",
"additionalProperties": false,
"properties": {
"init_segments": {
"description": "Whether the Flow makes use of initialisation segments. This parameter MUST be set to `true` if Media Objects have `init_object` populated. If set to `true`, all Media Objects MUST have `init_object` populated. Assume `false` if omitted.",
"type": "boolean"
}
}
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion api/schemas/flow-segment-post.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
],
"properties": {
"object_id": {
"description": "The Object identifier for the Media Object.",
"description": "The Object identifier for the Media Object. The `content-type` of the Media Object MUST match the `container` mime-type of the Flow. Service implementations SHOULD reject Objects IDs which have previously been registered as an `init_object_id` on other Flow Segments. i.e. init segments may not be used as media segments.",
"type": "string"
},
"init_object_id": {
"description": "The Object identifier for the initialisation segment Object required to decode the Media Object. The `content-type` of the initialisation segment Object MAY differ from the `container` mime-type of the Flow. This parameter MUST only be set where the media format makes use of initialisation segments. Initialisation Objects SHOULD be re-used where possible. This parameter SHOULD be omitted where the Object `object_id` already exists and is being re-used. Service implementations SHOULD reject Objects IDs which have previously been registered as an `id` on other Flow Segments. i.e. media segments may not be used as init segments.",
"type": "string"
},
"ts_offset": {
Expand Down
24 changes: 23 additions & 1 deletion api/schemas/flow-segment.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,33 @@
"description": "The count of samples in the Segment (which may be fewer than in the Media Object). The count could be less than expected given the Segment duration and rate if there are gaps. If not set, every sample from sample_offset onwards is used. Note that a sample is a video frame or audio sample. A (coded) audio frame has multiple audio samples. DEPRECATED: Use object_timerange instead - see AppNote 0036. Service implementations SHOULD continue to store and return it if set.",
"type": "integer",
"deprecated": true
},
"init_object": {
"title": "Initialisation Object",
"description": "The Object containing the initialisation segment required to decode the parent Media Object.",
"unevaluatedProperties": false,
"allOf": [
{
"type": "object",
"required": [
"object_id"
],
"properties": {
"object_id": {
"description": "The identifier of the initialisation Object.",
"type": "string"
}
}
},
{
"$ref": "object-core.json"
}
]
}
}
},
{
"$ref": "object-core.json"
"$ref": "object-media-core.json"
}
]
}
6 changes: 5 additions & 1 deletion api/schemas/flow-storage-post.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"type": "object",
"properties": {
"limit": {
"description": "Limit the number of Media Objects in each response page. Service implementations may specify their own default and maximum for the limit",
"description": "Limit the number of Objects in each response page. Service implementations may specify their own default and maximum for the limit",
"type": "integer"
},
"object_ids": {
Expand All @@ -17,6 +17,10 @@
"storage_id": {
"description": "The Storage Backend to allocate storage in. A Storage Backend identifier as advertised at the [/service/storage-backends](#/operations/GET_storage-backends) endpoint. If not set the default, as advertised at the [/service/storage-backends](#/operations/GET_storage-backends) endpoint, will be used if available. An invalid Storage Backend identifier will result in a 400 error.",
"$ref": "uuid.json"
},
"content_type": {
"description": "The `content_type` to use for the Objects. This parameter MUST only be set where requesting storage for initialisation segments in media formats which require them, and where the mime-type of those initialisation segments differs to that of the media segments. Assumed to be the `container` type of the Flow if not set.",
"$ref": "mime-type.json"
}
},
"not": {
Expand Down
8 changes: 4 additions & 4 deletions api/schemas/flow-storage.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"type": "object",
"title": "Media Bucket Object Store",
"description": "Gives information on storage for Media Objects. This schema is for the `http_object_store` Storage Backend type which provides URLs for storing Media Objects in object store buckets, and is the only Storage Backend type currently implemented. URLs SHOULD support the inclusion of checksums in headers as supported by advertised Storage Backend product. See AppNote 0048 for more details.",
"description": "Gives information on storage for Objects. This schema is for the `http_object_store` Storage Backend type which provides URLs for storing Objects in object store buckets, and is the only Storage Backend type currently implemented. URLs SHOULD support the inclusion of checksums in headers as supported by advertised Storage Backend product. See AppNote 0048 for more details. Where included in the response, `content-type` MUST match the corresponding value in the request.",
"properties": {
"media_objects": {
"type": "array",
"description": "List of information for identifying and uploading Media Objects",
"description": "List of information for identifying and uploading Objects",
"items": {
"type": "object",
"description": "Information for a Media Object",
"description": "Information for a Object",
"required": [
"object_id",
"put_url"
],
"properties": {
"object_id": {
"description": "The object store identifier for the Media Object.",
"description": "The object store identifier for the Object.",
"type": "string"
},
"put_url": {
Expand Down
4 changes: 4 additions & 0 deletions api/schemas/flow-video.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
"description": "If `true`, the frame rate of the Flow is variable and `frame_rate` MUST NOT be set. If `false` or omitted, the frame rate of the Flow is fixed and `frame_rate` MUST be set.",
"type": "boolean",
"default": false
},
"init_segments": {
"description": "Whether the Flow makes use of initialisation segments. This parameter MUST be set to `true` if Media Objects have `init_object` populated. If set to `true`, all Media Objects MUST have `init_object` populated. Assume `false` if omitted.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on whether this should be a Flow property? Since it's kinda an artifact of how the container and splitting into objects works (and lines up with the Flow GET example in this PR, which seems neater IMO)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've partly placed this here due to how the Profiles proposal has been architected. I think it would need to be at this level to allow the profile to match on this property which has a significant role in compatibility of media.

"type": "boolean"
}
},
"if": {
Expand Down
Loading
Loading