Media
On this page
The media types describe images, video, audio and captions: an image's size, a frame rate, a timecode, a subtitle cue, a film's age rating. Each follows its own standard. As every standard type does, each is read only in its canonical text. None is ordered, and none is personal data.
Here a video library makes thumbnails, reads a film's age rating, shows where an edit starts and writes captions:
type FormText = Text {}type NotARating = Failure { kind: FailureKind.invalid_value}/// A still's thumbnail: scaled to fit a 320-pixel square, never enlarged.function thumbnail(video_still: ImageDimensions) -> ImageDimensions { var tile: ImageDimensions = ImageDimensions { width: 320 height: 320 } return media.fit(video_still, within: tile)}/// The age rating a film's form names.function film_rating(raw: FormText) fails NotARating -> ContentRating { var typed_rating: ContentRating = attempt text.parse(raw, as: ContentRating) { malformed: NotARating rating_of_scheme: NotARating } return typed_rating}/// Where an edit starts, as the edit list shows it.function edit_start(edit_point: Timecode) -> PresentationText { return text.format(edit_point, locale: Locale.en_gb)}/// A caption as a WebVTT file writes it.function caption_text(opening_caption: SubtitleCue) -> FormText { return FormText(text.from(opening_caption))}
test "a thumbnail fits its tile and is never enlarged" { var landscape: ImageDimensions = ImageDimensions { width: 4000 height: 3000 } var wide_still: ImageDimensions = ImageDimensions { width: 1000 height: 333 } var icon: ImageDimensions = ImageDimensions { width: 200 height: 100 } assert text.from(thumbnail(landscape)) == "320x240" assert text.from(thumbnail(wide_still)) == "320x106" assert thumbnail(icon) == icon}test "a rating is one of its body's categories" { var given_rating: ContentRating = attempt film_rating("BBFC 12A") assert given_rating.scheme == ContentRatingScheme.bbfc assert film_rating("BBFC PG-13") fails NotARating assert film_rating("12A") fails NotARating}test "a timecode is shown in SMPTE's notation, a cue as WebVTT writes it" { var edit_point: Timecode = Timecode { hours: 1 minutes: 10 seconds: 0 frames: 0 rate: FrameRate { numerator: 30000 denominator: 1001 } drop_frame: true } assert edit_start(edit_point) == "01:10:00;00" var opening_caption: SubtitleCue = SubtitleCue { start: 1s end: 4s text: "Mind the gap." } assert caption_text(opening_caption) == "00:00:01.000 --> 00:00:04.000\nMind the gap."}
Fitting an image into a box
| Operation | Gives | Fails | SQL form |
|---|---|---|---|
media.fit(dimensions, within:) |
ImageDimensions scaled to fit inside within, keeping the aspect ratio, never larger than the original, rounded down to whole pixels and never below 1 |
none |
media.fit works as CSS's object-fit: scale-down does, in whole
numbers. The side that limits the fit becomes exactly the box's size. The
other side is scaled with it and rounded down, never below 1. An image
that already fits is kept as it is, never enlarged.
Sizes, ratios and frame rates
- An
ImageDimensionsand aVideoResolutionare awidthand aheightin whole pixels, from 1 to 2,147,483,647. That is PNG's limit, the largest of the common formats'. Each is written1920x1080, width first, with a lower-casex. Each is sent as named fields, so nothing reads them the wrong way round, and stored as a column each. - A
VideoResolutionhas the same shape under its own name. It names a resolution by its pixels, never by a name like "4K", which means both 3840 and 4096 pixels across. - An
AspectRatiois awidthand aheight, whole numbers from 1, written16:9. It is held as written, as aRatiois, so16:9and32:18are two different values. It is sent as its text and stored as a column each. A cinema's 2.39:1 is written239:100. A later release can accept decimals without changing any value. - A
FrameRateis anumeratorand adenominator, each from 1 to 2,147,483,647, written30000/1001or25/1. The denominator is always written, and the rate is held as written. It is sent as its text and stored as a column each.
Boxes, paper and orientation
- A
Rectis anxand ay, each anyDecimal, and awidthand aheight, never below 0. Its text is SVG'sviewBox:0 0 300 200. - An
Insetsis atop,right,bottomandleft, each never below 0, so a negative margin is refused. Its text is the CSS box model's four sides:8 16 8 16. - In both texts each number keeps the places it was given, has no leading zero, and is one space from the next, and every part is always written. Each is sent as named fields and stored as a column per part.
- A
PaperSizeis one of ISO 216's A0 to A10 and B0 to B10, or ANSI/ASME Y14.1's letter, legal and tabloid, codedA4orLetter. ISO 269's envelope sizes and ledger (tabloid on its side) are left out. - An
ExifOrientationsays how a stored image must be turned to show it upright. Its variants are named for where the stored image's first row and column are seen, as libtiff names TIFF 6.0's values:top_lefttoleft_bottom. They are coded1to8and sent as that text, as every enum is sent as its code. - An
AltTextis one line of an image's text alternative, as WCAG describes it: 1 to 1,000 characters, with no space at either end. An image with nothing to say hasnonein a field that may be empty, never empty text.
Codecs
A VideoCodec and an AudioCodec are the codec strings the W3C
WebCodecs Codec Registry lists, in RFC 6381's form, such as avc1.42001E
or opus:
- video:
av01,avc1,avc3,hev1,hvc1andvp09, each with its elements, andvp8; - audio:
mp4awith its elements,flac,mp3,opus,vorbis,ulaw,alawand the fivepcm-forms.
The elements after the first aren't checked further. Case is kept, as the registry writes them.
Timecodes and subtitles
A Timecode is a position in a video as SMPTE ST 12-1 writes it:
hours (0 to 23), minutes and seconds (0 to 59) and frames (0 to
119, room for 120 frames a second), with its rate and whether it counts
drop_frame. Drop-frame timecode skips some frame labels so that the
clock keeps time with a 29.97 frames-a-second video. A timecode has three
rules:
frames_within_rate: its frames are below its rate's frames a second, rounded up to a whole number;drop_frame_rate: drop-frame is used only at 30000/1001 and 60000/1001 frames a second, compared by value;frame_not_dropped: a drop-frame timecode never names a label that drop-frame skips, frames 0 and 1 (or 0 to 3 at the higher rate) at the start of each minute except every tenth.
Its notation, 01:00:00;00, doesn't say its rate, so a timecode has no
single text form. It is sent as its parts, stored as a column per part
(its rate in two), and shown in that notation.
A SubtitleCue is a WebVTT cue: a start and an end, each a
Duration from the start of the media to the millisecond, and its
text, lines with no control character and no blank line. Its rules are
start_not_negative, end_after_start and text_without_arrow, which
refuses --> in the text. Its canonical text is the cue's timings line,
its hours in two digits or more, then a line feed and its text, with no
identifier or settings: 00:00:01.000 --> 00:00:04.000 and its text. It
is sent as named fields and stored as a column per part.
When you write a Timecode or a SubtitleCue out as a literal, its rules
are checked while compiling. A value read from JSON is checked as it is
read. One built in code from other values is built with attempt, which
fails by the rule it breaks.
Age ratings
A ContentRating is a scheme and a rating, one of that body's
categories as it writes them, its rule rating_of_scheme. It is written
BBFC 12A or PEGI 16, the body's code read ignoring case. It is sent
as that text and stored as a column each.
The bodies are a standard enum, ContentRatingScheme:
- BBFC: U, PG, 12A, 12, 15, 18, R18;
- the MPA, the MPAA's name since 2019: G, PG, PG-13, R, NC-17;
- PEGI: 3, 7, 12, 16, 18;
- ESRB: E, E10+, T, M, AO, RP.
Other bodies, such as USK, the ACB and US television's, come with a later release.
Markdown for agents: /docs/media.md.Built from Jadpo main at a707efb.