Skip to content

Commit 98d0331

Browse files
committed
perf(playback): show generated poster immediately
1 parent 0b4f540 commit 98d0331

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

services/rend-api/src/main.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,10 @@ struct PlaybackBootstrapResponse {
11581158
manifest_url: Option<String>,
11591159
#[serde(skip_serializing_if = "Option::is_none")]
11601160
manifest_content_type: Option<String>,
1161+
#[serde(skip_serializing_if = "Option::is_none")]
1162+
poster_url: Option<String>,
1163+
#[serde(skip_serializing_if = "Option::is_none")]
1164+
poster_content_type: Option<String>,
11611165
prefetch_hints: Vec<PlaybackPrefetchHint>,
11621166
}
11631167

@@ -4714,6 +4718,7 @@ fn playback_bootstrap_response(
47144718
let manifest_artifact = (asset.playable_state == "hls_ready")
47154719
.then(|| find_playback_artifact(&playback_artifacts, "hls/master.m3u8").cloned())
47164720
.flatten();
4721+
let poster_artifact = find_playback_artifact(&playback_artifacts, "thumbnail.jpg").cloned();
47174722
let primary_artifact = primary_playback_artifact(
47184723
&asset.playable_state,
47194724
opener_artifact.as_ref(),
@@ -4734,6 +4739,8 @@ fn playback_bootstrap_response(
47344739
&asset.asset_id,
47354740
manifest_artifact.as_ref(),
47364741
);
4742+
let (poster_url, poster_content_type) =
4743+
artifact_fields(playback_base_url, &asset.asset_id, poster_artifact.as_ref());
47374744
let prefetch_hints = if asset.playable_state == "hls_ready" {
47384745
first_segment_prefetch_hints(
47394746
playback_base_url,
@@ -4758,6 +4765,8 @@ fn playback_bootstrap_response(
47584765
opener_content_type,
47594766
manifest_url,
47604767
manifest_content_type,
4768+
poster_url,
4769+
poster_content_type,
47614770
prefetch_hints,
47624771
})
47634772
}
@@ -5092,7 +5101,11 @@ fn render_api_fast_embed_html(
50925101
let auto_play_attr = if auto_play { " autoplay" } else { "" };
50935102
let controls_attr = if controls { " controls" } else { "" };
50945103
let muted_attr = if muted { " muted" } else { "" };
5095-
let poster_attr = "";
5104+
let poster_attr = response
5105+
.poster_url
5106+
.as_ref()
5107+
.map(|url| format!(r#" poster="{}""#, html_escape(url)))
5108+
.unwrap_or_default();
50965109
let selected_label = if inline_startup.is_some() {
50975110
"mse_inline"
50985111
} else {
@@ -5137,7 +5150,7 @@ fn render_api_fast_embed_html(
51375150
)
51385151
})
51395152
.unwrap_or_default()
5140-
} else if selection.content_type == "video/mp4" {
5153+
} else if auto_play && selection.content_type == "video/mp4" {
51415154
format!(
51425155
r#"<link rel="preload" as="video" href="{}" type="{}"{} fetchpriority="high">"#,
51435156
html_escape(&selection.url),
@@ -5148,6 +5161,7 @@ fn render_api_fast_embed_html(
51485161
String::new()
51495162
};
51505163
let inline_startup_json = inline_startup_script_json(inline_startup);
5164+
let video_preload = if auto_play { "auto" } else { "metadata" };
51515165
let fallback_json = script_json(serde_json::json!({
51525166
"artifactPath": selection.artifact_path,
51535167
"contentType": selection.content_type,
@@ -5175,7 +5189,7 @@ body{{overflow:hidden}}
51755189
</head>
51765190
<body>
51775191
<main class="rend-fast" aria-label="Video player" data-rend-player-state="ready" data-rend-player-selected="{label}" data-rend-player-artifact="{artifact_path}" data-rend-ready-status="ready" data-rend-source-state="{source_state}" data-rend-playable-state="{playable_state}" data-rend-playback-engine="{playback_engine}" data-rend-document-start-ms="0" data-rend-bootstrap-ms="0" data-rend-asset-id="{asset_id}">
5178-
<video class="rend-fast__video"{source_attrs}{poster_attr}{auto_play_attr}{controls_attr}{muted_attr} playsinline preload="auto"{cross_origin_attr}></video>
5192+
<video class="rend-fast__video"{source_attrs}{poster_attr}{auto_play_attr}{controls_attr}{muted_attr} playsinline preload="{video_preload}"{cross_origin_attr}></video>
51795193
<div class="rend-fast__message" role="status" aria-live="polite">Ready</div>
51805194
</main>
51815195
<script>
@@ -5195,6 +5209,7 @@ body{{overflow:hidden}}
51955209
playback_engine = html_escape(playback_engine),
51965210
source_attrs = source_attrs,
51975211
source_state = html_escape(&response.source_state),
5212+
video_preload = video_preload,
51985213
)
51995214
}
52005215

@@ -5220,6 +5235,7 @@ fn playback_artifact_from_record(
52205235
"opener" => artifact_path == "opener.mp4",
52215236
"manifest" => is_hls_manifest_artifact_path(artifact_path),
52225237
"segment" => is_hls_media_artifact_path(artifact_path),
5238+
"thumbnail" => artifact_path == "thumbnail.jpg",
52235239
_ => false,
52245240
};
52255241

services/rend-api/src/tests.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,14 @@ fn opener_artifact_records() -> Vec<PlaybackArtifactRecord> {
324324
)]
325325
}
326326

327+
fn thumbnail_artifact_records() -> Vec<PlaybackArtifactRecord> {
328+
vec![artifact_record(
329+
"thumbnail",
330+
"videos/asset-123/thumbnail.jpg",
331+
"image/jpeg",
332+
)]
333+
}
334+
327335
async fn route_response(app: Router, path: &str, auth: Option<&str>) -> Response {
328336
route_response_with_method(app, "GET", path, auth).await
329337
}
@@ -1604,6 +1612,34 @@ fn playback_bootstrap_opener_ready_returns_opener_primary_without_manifest() {
16041612
assert!(response.prefetch_hints.is_empty());
16051613
}
16061614

1615+
#[test]
1616+
fn playback_bootstrap_includes_thumbnail_as_poster() {
1617+
let mut artifacts = opener_artifact_records();
1618+
artifacts.extend(thumbnail_artifact_records());
1619+
let response = playback_bootstrap_response(
1620+
Some(asset_record("opener_ready")),
1621+
&artifacts,
1622+
"https://video.rend.so",
1623+
&test_issuer(),
1624+
2,
1625+
NOW,
1626+
)
1627+
.unwrap();
1628+
1629+
assert_eq!(
1630+
response.poster_url.as_deref(),
1631+
Some("https://video.rend.so/v/asset-123/thumbnail.jpg")
1632+
);
1633+
assert_eq!(response.poster_content_type.as_deref(), Some("image/jpeg"));
1634+
1635+
let selection = fast_embed_playback_selection(&response, "opener").unwrap();
1636+
let html =
1637+
render_api_fast_embed_html(&response, &selection, None, false, true, false, "include");
1638+
assert!(html.contains(r#"poster="https://video.rend.so/v/asset-123/thumbnail.jpg""#));
1639+
assert!(html.contains(r#"preload="metadata""#));
1640+
assert!(!html.contains(r#"rel="preload" as="video""#));
1641+
}
1642+
16071643
#[test]
16081644
fn playback_bootstrap_urls_are_tokenless_and_cookie_carries_playback_token() {
16091645
let response = playback_bootstrap_response(

0 commit comments

Comments
 (0)