Skip to content

Commit 00ebcef

Browse files
authored
Merge pull request #2249 from tf/bg-video-720p
Prevent blurry background videos
2 parents 1fa131f + 1b7e87b commit 00ebcef

File tree

13 files changed

+473
-122
lines changed

13 files changed

+473
-122
lines changed

app/models/pageflow/video_file.rb

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ class VideoFile < ApplicationRecord
99
has_attached_file(:poster, Pageflow.config.paperclip_s3_default_options
1010
.merge(default_url: ':pageflow_placeholder',
1111
styles: Pageflow.config.thumbnail_styles
12-
.merge(medium: ['1920x1920>', :JPG],
13-
large: ['1024x1024>', :JPG],
12+
.merge(medium: ['1024x1024>', :JPG],
13+
large: ['1920x1920>', :JPG],
1414
ultra: ['3840x3840>', :JPG],
1515
print: ['300x300>', :JPG]),
1616
convert_options: {
@@ -24,8 +24,8 @@ class VideoFile < ApplicationRecord
2424
.merge(:default_url => ':pageflow_placeholder',
2525
:default_style => :thumbnail,
2626
:styles => Pageflow.config.thumbnail_styles
27-
.merge(:medium => ['1920x1920>', :JPG],
28-
:large => ['1024x1024>', :JPG]),
27+
.merge(medium: ['1024x1024>', :JPG],
28+
large: ['1920x1920>', :JPG]),
2929
:convert_options => {
3030
:medium => "-quality 60 -interlace Plane",
3131
:large => "-quality 60 -interlace Plane"
@@ -111,6 +111,10 @@ def dash_playlist
111111
ZencoderAttachment.new(self, 'dash/manifest.mpd')
112112
end
113113

114+
def dash_playlist_high_and_up
115+
ZencoderAttachment.new(self, 'dash/manifest-high-and-up.mpd')
116+
end
117+
114118
def hls_playlist
115119
if Pageflow.config.zencoder_options[:hls_smil_suffix].present?
116120
ZencoderAttachment.new(self,
@@ -125,6 +129,10 @@ def hls_playlist
125129
end
126130
end
127131

132+
def hls_playlist_high_and_up
133+
ZencoderAttachment.new(self, 'hls-playlist-high-and-up.m3u8', host: :hls)
134+
end
135+
128136
def smil
129137
ZencoderAttachment.new(self, 'hls-playlist.smil')
130138
end

app/models/pageflow/video_file_url_templates.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ def call
1010
:'4k' => url_template(:mp4_4k),
1111

1212
:'hls-playlist' => url_template(:hls_playlist),
13+
:'hls-playlist-high-and-up' => url_template(:hls_playlist_high_and_up),
1314
:'dash-playlist' => url_template(:dash_playlist),
15+
:'dash-playlist-high-and-up' => url_template(:dash_playlist_high_and_up),
1416

1517
poster_medium: url_template(:poster, :medium),
1618
poster_large: url_template(:poster, :large),

app/models/pageflow/zencoder_attachment.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ def url(url_options = {})
4545
url_options)
4646
end
4747

48-
def url_relative_to(attachment)
49-
dir_path = File.dirname(URI.parse(attachment.url).path)
48+
def relative_path_to(other_path)
49+
dir_path = File.dirname(path)
5050

51-
unless URI.parse(url).path.start_with?(dir_path)
52-
raise("Could not generate relative url for #{url} based on #{attachment.url}.")
51+
unless URI.parse(other_path).path.start_with?(dir_path)
52+
raise("Could not generate relative path for #{other_path} based on #{url}.")
5353
end
5454

55-
url.split("#{dir_path}/", 2).last
55+
other_path.split("#{dir_path}/", 2).last
5656
end
5757

5858
private

entry_types/scrolled/package/spec/frontend/VideoPlayer-spec.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ describe('VideoPlayer', () => {
2424
fileUrlTemplates: {
2525
videoFiles: {
2626
medium: ':id_partition/medium/:basename.mp4',
27-
high: ':id_partition/high/:basename.mp4'
27+
high: ':id_partition/high/:basename.mp4',
28+
'hls-playlist': ':id_partition/hls-playlist.m3u8',
29+
'hls-playlist-high-and-up': ':id_partition/hls-playlist-high-and-up.m3u8'
2830
}
2931
},
3032
videoFiles: [
@@ -70,6 +72,53 @@ describe('VideoPlayer', () => {
7072
});
7173

7274
it('passes sources according to setting to media API', () => {
75+
const spyMedia = jest.spyOn(media, 'getPlayer');
76+
settings.set('videoQuality', 'auto');
77+
78+
renderInEntry(
79+
() => <VideoPlayer {...requiredProps()}
80+
videoFile={useFile({collectionName: 'videoFiles', permaId: 100})} />,
81+
{
82+
seed: getVideoFileSeed({
83+
basename: 'video',
84+
id: 1,
85+
permaId: 100
86+
})
87+
}
88+
);
89+
90+
expect(spyMedia).toHaveBeenCalledWith(
91+
[{type: 'application/x-mpegURL', src: '000/000/001/hls-playlist.m3u8'},
92+
{type: 'video/mp4', src: '000/000/001/high/video.mp4'}],
93+
expect.anything()
94+
);
95+
});
96+
97+
it('support adaptiveMinQuality prop', () => {
98+
const spyMedia = jest.spyOn(media, 'getPlayer');
99+
settings.set('videoQuality', 'auto');
100+
101+
renderInEntry(
102+
() => <VideoPlayer {...requiredProps()}
103+
videoFile={useFile({collectionName: 'videoFiles', permaId: 100})}
104+
adaptiveMinQuality="high" />,
105+
{
106+
seed: getVideoFileSeed({
107+
basename: 'video',
108+
id: 1,
109+
permaId: 100
110+
})
111+
}
112+
);
113+
114+
expect(spyMedia).toHaveBeenCalledWith(
115+
[{type: 'application/x-mpegURL', src: '000/000/001/hls-playlist-high-and-up.m3u8'},
116+
{type: 'video/mp4', src: '000/000/001/high/video.mp4'}],
117+
expect.anything()
118+
);
119+
});
120+
121+
it('uses quality from settings', () => {
73122
const spyMedia = jest.spyOn(media, 'getPlayer');
74123
settings.set('videoQuality', 'medium');
75124

entry_types/scrolled/package/spec/frontend/VideoPlayer/sources-spec.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,54 @@ describe('VideoPlayer sources', () => {
4747
expect(result.map(s => s.type)).toContain('application/x-mpegURL');
4848
});
4949

50+
it('ignores high playlist by default', () => {
51+
const videoFile = {urls: {
52+
'hls-playlist': 'http://example.com/4/hls-playlist.m3u8',
53+
'hls-playlist-high-and-up': 'http://example.com/4/hls-playlist-high-and-up.m3u8',
54+
'dash-playlist': 'http://example.com/4/dash-playlist.m3u8',
55+
'dash-playlist-high-and-up': 'http://example.com/4/dash-playlist-high-and-up.m3u8'
56+
}};
57+
58+
const result = sources(videoFile);
59+
60+
expect(result.map(s => s.src)).toContain('http://example.com/4/hls-playlist.m3u8');
61+
expect(result.map(s => s.src)).not.toContain('http://example.com/4/hls-playlist-high-and-up.m3u8');
62+
expect(result.map(s => s.src)).toContain('http://example.com/4/dash-playlist.m3u8');
63+
expect(result.map(s => s.src)).not.toContain('http://example.com/4/dash-playlist-high-and-up.m3u8');
64+
});
65+
66+
it('supports ensuring adaptive min quality high', () => {
67+
const videoFile = {urls: {
68+
'hls-playlist': 'http://example.com/4/hls-playlist.m3u8',
69+
'hls-playlist-high-and-up': 'http://example.com/4/hls-playlist-high-and-up.m3u8',
70+
'dash-playlist': 'http://example.com/4/dash-playlist.m3u8',
71+
'dash-playlist-high-and-up': 'http://example.com/4/dash-playlist-high-and-up.m3u8'
72+
}};
73+
74+
const result = sources(videoFile, {adaptiveMinQuality: 'high'});
75+
76+
expect(result.map(s => s.src)).not.toContain('http://example.com/4/hls-playlist.m3u8');
77+
expect(result.map(s => s.src)).toContain('http://example.com/4/hls-playlist-high-and-up.m3u8');
78+
expect(result.map(s => s.src)).not.toContain('http://example.com/4/dash-playlist.m3u8');
79+
expect(result.map(s => s.src)).toContain('http://example.com/4/dash-playlist-high-and-up.m3u8');
80+
});
81+
82+
it('falls back to default playlist if high-and-up playlist is not present', () => {
83+
const videoFile = {urls: {
84+
'hls-playlist': 'http://example.com/4/hls-playlist.m3u8',
85+
'dash-playlist': 'http://example.com/4/dash-playlist.m3u8',
86+
}};
87+
88+
const result = sources(videoFile, {adaptiveMinQuality: 'high'});
89+
90+
expect(result.map(s => s.src)).toContain('http://example.com/4/hls-playlist.m3u8');
91+
expect(result.map(s => s.src)).toContain('http://example.com/4/dash-playlist.m3u8');
92+
});
93+
5094
it('uses medium quality if requested', () => {
5195
const videoFile = {urls: {'medium': 'http://example.com/4/medium.mp4'}};
5296

53-
const result = sources(videoFile, 'medium');
97+
const result = sources(videoFile, {quality: 'medium'});
5498

5599
expect(result.length).toBe(1);
56100
expect(result[0].src).toContain('medium.mp4');
@@ -62,7 +106,7 @@ describe('VideoPlayer sources', () => {
62106
'fullhd': 'http://example.com/4/fullhd.mp4'
63107
}};
64108

65-
const result = sources(videoFile, 'fullhd');
109+
const result = sources(videoFile, {quality: 'fullhd'});
66110

67111
expect(result.length).toBe(1);
68112
expect(result[0].src).toContain('fullhd.mp4');
@@ -75,7 +119,7 @@ describe('VideoPlayer sources', () => {
75119
'high': 'http://example.com/4/high.mp4'
76120
}};
77121

78-
const result = sources(videoFile, 'fullhd');
122+
const result = sources(videoFile, {quality: 'fullhd'});
79123

80124
expect(result.length).toBe(1);
81125
expect(result[0].src).toContain('high.mp4');
@@ -113,7 +157,7 @@ describe('VideoPlayer sources', () => {
113157
'4k': 'http://example.com/4/4k.mp4'
114158
}};
115159

116-
const result = sources(videoFile, '4k');
160+
const result = sources(videoFile, {quality: '4k'});
117161

118162
expect(result.length).toBe(1);
119163
expect(result[0].src).toContain('4k.mp4');
@@ -124,7 +168,7 @@ describe('VideoPlayer sources', () => {
124168
'high': 'http://example.com/4/high.mp4'
125169
}};
126170

127-
const result = sources(videoFile, '4k');
171+
const result = sources(videoFile, {quality: '4k'});
128172

129173
expect(result.length).toBe(1);
130174
expect(result[0].src).toContain('high.mp4');

entry_types/scrolled/package/src/frontend/VideoPlayer/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ import {VideoStructuredData} from './VideoStructuredData';
1616
* @param {number} [props.defaultTextTrackFileId] - Perma id of default text track file.
1717
* @param {string} [props.load] - Control lazy loading. `"auto"` (default), `"poster"` or `"none"`.
1818
* @param {String} [props.fit] - `"contain"` (default) or `"cover"`.
19+
* @param {String} [props.adaptiveMinQuality] - Pass "high" or "fullhd" to use HLS/Dash playlists
20+
* with at least given quality.
1921
*/
20-
export function VideoPlayer({videoFile, posterImageFile, ...props}) {
22+
export function VideoPlayer({videoFile, posterImageFile, adaptiveMinQuality, ...props}) {
2123
const [activeQuality] = useVideoQualitySetting();
2224
const textTracks = useTextTracks({
2325
file: videoFile,
@@ -32,7 +34,7 @@ export function VideoPlayer({videoFile, posterImageFile, ...props}) {
3234
fit={props.fit}
3335
textTracks={textTracks}
3436
filePermaId={videoFile.permaId}
35-
sources={sources(videoFile, activeQuality)}
37+
sources={sources(videoFile, {quality: activeQuality, adaptiveMinQuality})}
3638
textTracksInset={true}
3739
posterImageUrl={posterImageFile && posterImageFile.isReady ?
3840
posterImageFile.urls.large : videoFile.urls.posterLarge}

entry_types/scrolled/package/src/frontend/VideoPlayer/sources.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ browser.feature('dash', () => true);
44
browser.feature('video', () => true);
55
browser.feature('highdef', () => true);
66

7-
export function sources(videoFile, quality = 'auto') {
7+
export function sources(videoFile, {quality = 'auto', adaptiveMinQuality} = {}) {
88
if (typeof window !== 'undefined') {
99
if (!browser.has('video')) {
1010
return [];
@@ -41,7 +41,7 @@ export function sources(videoFile, quality = 'auto') {
4141
let result = [
4242
{
4343
type: 'application/x-mpegURL',
44-
src: videoFile.urls['hls-playlist']
44+
src: getPlaylistSrc(videoFile, 'hls', adaptiveMinQuality)
4545
},
4646
{
4747
type: 'video/mp4',
@@ -53,7 +53,7 @@ export function sources(videoFile, quality = 'auto') {
5353
result = [
5454
{
5555
type: 'application/dash+xml',
56-
src: videoFile.urls['dash-playlist']
56+
src: getPlaylistSrc(videoFile, 'dash', adaptiveMinQuality)
5757
}
5858
].concat(result);
5959
}
@@ -73,3 +73,14 @@ export function sources(videoFile, quality = 'auto') {
7373
];
7474
}
7575
}
76+
77+
function getPlaylistSrc(videoFile, format, adaptiveMinQuality) {
78+
const key = adaptiveMinQuality ? `${format}-playlist-${adaptiveMinQuality}-and-up` : `${format}-playlist`;
79+
const result = videoFile.urls[key];
80+
81+
if (!result && adaptiveMinQuality) {
82+
return getPlaylistSrc(videoFile, format);
83+
}
84+
85+
return result;
86+
}

entry_types/scrolled/package/src/frontend/v1/Backdrop/BackgroundVideo.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function BackgroundVideo({video, onMotifAreaUpdate, containerDimension})
5555
playerActions={playerActions}
5656
videoFile={video}
5757
textTracksDisabled={true}
58+
adaptiveMinQuality="high"
5859
fit="cover"
5960
loop={true}
6061
playsInline={true} />

0 commit comments

Comments
 (0)