-
Notifications
You must be signed in to change notification settings - Fork 31
feat: Extend Lottie specification with VideoFrame asset support #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
a6a7029
37e5f89
6ede4d5
aa467d2
a2b0026
b31bc96
3d75b48
cf72cae
04831c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,3 +30,25 @@ Even if an image asset does not have any intrinsic size, its contents MUST | |
| still stay within the `w`-`h` bounds when rendered. | ||
|
|
||
| Authoring tools SHOULD export files where `w` and `h` match the physical size of the assets. | ||
|
|
||
| <h2 id="video">Video</h2> | ||
|
|
||
| {schema_string:assets/video/description} | ||
|
|
||
| Video formats supported vary depending on the player. Some commonly supported formats are MP4, WebM, and MOV. | ||
|
|
||
| {schema_object:assets/video} | ||
|
|
||
| Video assets define the source video content that can be referenced by video frame assets. | ||
| This enables the use of video compression techniques to reduce the overall animation size, when working with raster-based animations. | ||
|
|
||
| Players MUST support extracting individual frames from video assets at specified timestamps when initializing animations. | ||
|
|
||
| <h2 id="video-frame">Video Frame</h2> | ||
|
|
||
| {schema_string:assets/video-frame/description} | ||
|
|
||
| {schema_object:assets/video-frame} | ||
|
|
||
| Video frame assets reference a video asset by ID and specify a timestamp to extract a specific frame. Players MUST extract the frame at the exact timestamp specified, or the closest available frame if the exact timestamp is not available. The extracted frame should be treated as a static image for rendering purposes. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the same functionality be achieved using time remapping to lock a video layer's time? IOW do we actually need video frame assets, or would video assets be sufficient? I can think of some advantages to having a dedicated asset type, mostly on the implementation side (caching) - but there are also some downsides (more verbose than video asset + time-locked video layer).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main motivation of having VideoFrame is ability to set time or frame number, I didn't find better way to express it with existing lottie primitives |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| "assets": [ | ||
| { | ||
| "id": "comp_0", | ||
| "ty": 0, | ||
| "nm": "time_remap_precomp", | ||
| "fr": 60, | ||
| "layers": [ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| "assets": [ | ||
| { | ||
| "id": "comp_0", | ||
| "ty": 0, | ||
| "nm": "swing", | ||
| "fr": 60, | ||
| "layers": [ | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "type": "object", | ||
| "title": "Video Frame", | ||
| "description": "Asset representing a frame from a video at a specific timestamp.", | ||
| "allOf": [ | ||
| { | ||
| "$ref": "#/$defs/assets/asset" | ||
| }, | ||
| { | ||
| "$ref": "#/$defs/helpers/slottable-object" | ||
| }, | ||
| { | ||
| "type": "object", | ||
| "properties": { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should introduce a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mbasaglia , I agree. Added |
||
| "ty": { | ||
| "title": "Type", | ||
| "description": "Asset type", | ||
| "type": "integer", | ||
| "const": 3 | ||
| }, | ||
| "vsid": { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Naming bike-shed: normally How about
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, refId makes more sense, updated |
||
| "title": "Video Asset ID", | ||
| "description": "ID of the video asset to extract frame from", | ||
| "type": "string" | ||
| }, | ||
| "t": { | ||
| "title": "Timestamp", | ||
| "description": "Timestamp in seconds where to extract the frame from the video", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this interact with start-time/time-stretch/general time remapping in referencing layers? Also actual time units seem a bit odd since everything else in Lottie is expressed as frame indices (ip, op, etc).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, you are right, we can define time in frames here |
||
| "type": "number", | ||
| "minimum": 0 | ||
| }, | ||
| "w": { | ||
| "title": "Width", | ||
| "description": "Width of the frame", | ||
| "type": "number" | ||
| }, | ||
| "h": { | ||
| "title": "Height", | ||
| "description": "Height of the frame", | ||
| "type": "number" | ||
| } | ||
|
Comment on lines
+33
to
+42
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The video asset schema also has
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mbasaglia I was thinking about extracting a frame from VideoResource, specifying width, height, x, y from the top left corner. But you are right, as a first iteration we can drop w/h from frame |
||
| }, | ||
| "required": ["ty"], | ||
| "if": { | ||
| "required": ["sid"] | ||
| }, | ||
| "else": { | ||
| "required": ["ty", "vsid", "t"] | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "type": "object", | ||
| "title": "Video", | ||
| "description": "Asset containing a video that can be referenced by layers or video frame assets.", | ||
| "allOf": [ | ||
| { | ||
| "$ref": "#/$defs/assets/asset" | ||
| }, | ||
| { | ||
| "$ref": "#/$defs/helpers/slottable-object" | ||
| }, | ||
| { | ||
| "$ref": "#/$defs/helpers/file-asset" | ||
| }, | ||
| { | ||
| "type": "object", | ||
| "properties": { | ||
| "ty": { | ||
| "title": "Type", | ||
| "description": "Asset type", | ||
| "type": "integer", | ||
| "const": 2 | ||
| }, | ||
| "w": { | ||
| "title": "Width", | ||
| "description": "Width of the video", | ||
| "type": "number" | ||
| }, | ||
| "h": { | ||
| "title": "Height", | ||
| "description": "Height of the video", | ||
| "type": "number" | ||
| }, | ||
| "duration": { | ||
| "title": "Duration", | ||
| "description": "Duration of the video in seconds", | ||
| "type": "number", | ||
| "minimum": 0 | ||
| } | ||
| }, | ||
| "required": ["ty"], | ||
| "if": { | ||
| "required": ["sid"] | ||
| }, | ||
| "else": { | ||
| "required": ["ty", "w", "h", "duration", "p"] | ||
| } | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "type": "object", | ||
| "title": "File Asset", | ||
| "description": "Common properties for file-based assets like images and videos", | ||
| "properties": { | ||
| "p": { | ||
| "title": "File Name", | ||
| "description": "Name of the file or a data url", | ||
| "type": "string" | ||
| }, | ||
| "u": { | ||
| "title": "File Path", | ||
| "description": "Path to the file", | ||
| "type": "string" | ||
| }, | ||
| "e": { | ||
| "title": "Embedded", | ||
| "description": "If '1', 'p' is a Data URL", | ||
| "$ref": "#/$defs/values/int-boolean" | ||
| } | ||
| }, | ||
| "allOf": [ | ||
| { | ||
| "if": { | ||
| "properties": { | ||
| "e": {"const": 1} | ||
| }, | ||
| "required": ["e"] | ||
| }, | ||
| "then": { | ||
| "properties": { | ||
| "p": {"$ref": "#/$defs/values/data-url"} | ||
| } | ||
| } | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| "assets": [ | ||
| { | ||
| "id": "image_0", | ||
| "ty": 1, | ||
| "w": 1024, | ||
| "h": 1024, | ||
| "u": "images/", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a SHOULD or a MUST?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This point is up for discussion.
The advantage of using MUST is that Lottie players could extract frames on the fly, which speeds up animation initialization and reduces memory usage before playback starts. At the same time, if players don’t treat the extracted frame as a static image but instead process it in real time, it could impose constraints on the video itself - for example, requiring more keyframes to ensure fast seeking, which would increase file size.
That’s why I would vote for MUST.
In the future, if we want to support optimization for initialization we could extend videoFrame with initialization: static | dynamic param to achieve that.