diff --git a/karma.conf.js b/karma.conf.js
index 3206cb2111..cd044c6af0 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -261,6 +261,8 @@ module.exports = (config) => {
{pattern: 'test/test/assets/dash-aes-128/*', included: false},
{pattern: 'test/test/assets/dash-clearkey/*', included: false},
{pattern: 'test/test/assets/dash-vr/*', included: false},
+ {pattern: 'test/test/assets/dv-p8-hevc/*', included: false},
+ {pattern: 'test/test/assets/dv-p10-av1/*', included: false},
{pattern: 'test/test/assets/hls-aes-256/*', included: false},
{pattern: 'test/test/assets/hls-interstitial/*', included: false},
{pattern: 'test/test/assets/hls-raw-aac/*', included: false},
diff --git a/test/player_dolby_vision_integration.js b/test/player_dolby_vision_integration.js
new file mode 100644
index 0000000000..133a5f3b9d
--- /dev/null
+++ b/test/player_dolby_vision_integration.js
@@ -0,0 +1,135 @@
+/*! @license
+ * Shaka Player
+ * Copyright 2016 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+describe('Player Dolby Vision', () => {
+ const Util = shaka.test.Util;
+
+ /** @type {!jasmine.Spy} */
+ let onErrorSpy;
+
+ /** @type {!HTMLVideoElement} */
+ let video;
+ /** @type {shaka.Player} */
+ let player;
+ /** @type {!shaka.util.EventManager} */
+ let eventManager;
+
+ let compiledShaka;
+
+ /** @type {!shaka.test.Waiter} */
+ let waiter;
+
+ beforeAll(async () => {
+ video = shaka.test.UiUtils.createVideoElement();
+ document.body.appendChild(video);
+ compiledShaka =
+ await shaka.test.Loader.loadShaka(getClientArg('uncompiled'));
+ });
+
+ beforeEach(async () => {
+ await shaka.test.TestScheme.createManifests(compiledShaka, '_compiled');
+ player = new compiledShaka.Player();
+ await player.attach(video);
+
+ player.configure('streaming.useNativeHlsOnSafari', false);
+
+ // Disable stall detection, which can interfere with playback tests.
+ player.configure('streaming.stallEnabled', false);
+
+ // Grab event manager from the uncompiled library:
+ eventManager = new shaka.util.EventManager();
+ waiter = new shaka.test.Waiter(eventManager);
+ waiter.setPlayer(player);
+
+ onErrorSpy = jasmine.createSpy('onError');
+ onErrorSpy.and.callFake((event) => fail(event.detail));
+ eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy));
+ });
+
+ afterEach(async () => {
+ eventManager.release();
+ await player.destroy();
+ });
+
+ afterAll(() => {
+ document.body.removeChild(video);
+ });
+
+ /**
+ * @param {string} uri
+ * @return {!Promise}
+ */
+ async function testPlayback(uri) {
+ await player.load(uri);
+ await video.play();
+ expect(player.isLive()).toBe(false);
+
+ // Wait for the video to start playback. If it takes longer than 10
+ // seconds, fail the test.
+ await waiter.waitForMovementOrFailOnTimeout(video, 10);
+
+ // Play for 2 seconds, but stop early if the video ends. If it takes
+ // longer than 10 seconds, fail the test.
+ await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 2, 10);
+
+ await player.unload();
+ }
+
+ describe('P8 with fallback to HEVC', () => {
+ it('with DASH', async () => {
+ if (!await Util.isTypeSupported('video/mp4; codecs="hvc1.2.4.L90.90"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec HEVC is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p8-hevc/manifest.mpd');
+ });
+
+ it('with master playlist (HLS)', async () => {
+ if (!await Util.isTypeSupported('video/mp4; codecs="hvc1.2.4.L90.90"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec HEVC is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p8-hevc/master.m3u8');
+ });
+
+ it('with media playlist (HLS)', async () => {
+ if (!await Util.isTypeSupported('video/mp4; codecs="hvc1.2.4.L90.90"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec HEVC is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p8-hevc/media.m3u8');
+ });
+ });
+
+ describe('P10 with fallback to AV1', () => {
+ it('with DASH', async () => {
+ if (!await Util.isTypeSupported(
+ 'video/mp4; codecs="av01.0.04M.10.0.111.09.16.09.0"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec AV1 is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p10-av1/manifest.mpd');
+ });
+
+ it('with master playlist (HLS)', async () => {
+ if (!await Util.isTypeSupported(
+ 'video/mp4; codecs="av01.0.04M.10.0.111.09.16.09.0"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec AV1 is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p10-av1/master.m3u8');
+ });
+
+ it('with media playlist (HLS)', async () => {
+ if (!await Util.isTypeSupported(
+ 'video/mp4; codecs="av01.0.04M.10.0.111.09.16.09.0"',
+ /* width= */ 640, /* height= */ 360)) {
+ pending('Codec AV1 is not supported by the platform.');
+ }
+ await testPlayback('/base/test/test/assets/dv-p10-av1/media.m3u8');
+ });
+ });
+});
diff --git a/test/test/assets/dv-p10-av1/dovi_10-video.mp4 b/test/test/assets/dv-p10-av1/dovi_10-video.mp4
new file mode 100644
index 0000000000..b4cd29e4ab
Binary files /dev/null and b/test/test/assets/dv-p10-av1/dovi_10-video.mp4 differ
diff --git a/test/test/assets/dv-p10-av1/manifest.mpd b/test/test/assets/dv-p10-av1/manifest.mpd
new file mode 100644
index 0000000000..af3bf4d2cf
--- /dev/null
+++ b/test/test/assets/dv-p10-av1/manifest.mpd
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+ dovi_10-video.mp4
+
+
+
+
+
+
+
diff --git a/test/test/assets/dv-p10-av1/master.m3u8 b/test/test/assets/dv-p10-av1/master.m3u8
new file mode 100644
index 0000000000..89a13b201e
--- /dev/null
+++ b/test/test/assets/dv-p10-av1/master.m3u8
@@ -0,0 +1,5 @@
+#EXTM3U
+#EXT-X-INDEPENDENT-SEGMENTS
+
+#EXT-X-STREAM-INF:BANDWIDTH=550702,AVERAGE-BANDWIDTH=577484,CODECS="av01.0.04M.10.0.111.09.16.09.0",SUPPLEMENTAL-CODECS="dav1.10.01/db1p",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ,CLOSED-CAPTIONS=NONE
+media.m3u8
diff --git a/test/test/assets/dv-p10-av1/media.m3u8 b/test/test/assets/dv-p10-av1/media.m3u8
new file mode 100644
index 0000000000..41cb50aa7b
--- /dev/null
+++ b/test/test/assets/dv-p10-av1/media.m3u8
@@ -0,0 +1,12 @@
+#EXTM3U
+#EXT-X-VERSION:6
+#EXT-X-TARGETDURATION:6
+#EXT-X-PLAYLIST-TYPE:VOD
+#EXT-X-MAP:URI="dovi_10-video.mp4",BYTERANGE="871@0"
+#EXTINF:5.355,
+#EXT-X-BYTERANGE:368650@927
+dovi_10-video.mp4
+#EXTINF:0.667,
+#EXT-X-BYTERANGE:66100
+dovi_10-video.mp4
+#EXT-X-ENDLIST
diff --git a/test/test/assets/dv-p8-hevc/dovi_8-video.mp4 b/test/test/assets/dv-p8-hevc/dovi_8-video.mp4
new file mode 100644
index 0000000000..ce05290a78
Binary files /dev/null and b/test/test/assets/dv-p8-hevc/dovi_8-video.mp4 differ
diff --git a/test/test/assets/dv-p8-hevc/manifest.mpd b/test/test/assets/dv-p8-hevc/manifest.mpd
new file mode 100644
index 0000000000..806669a506
--- /dev/null
+++ b/test/test/assets/dv-p8-hevc/manifest.mpd
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+ dovi_8-video.mp4
+
+
+
+
+
+
+
diff --git a/test/test/assets/dv-p8-hevc/master.m3u8 b/test/test/assets/dv-p8-hevc/master.m3u8
new file mode 100644
index 0000000000..95424cb2c2
--- /dev/null
+++ b/test/test/assets/dv-p8-hevc/master.m3u8
@@ -0,0 +1,5 @@
+#EXTM3U
+#EXT-X-INDEPENDENT-SEGMENTS
+
+#EXT-X-STREAM-INF:BANDWIDTH=807837,AVERAGE-BANDWIDTH=748074,CODECS="hvc1.2.4.L90.90",SUPPLEMENTAL-CODECS="dvh1.08.01/db2g",RESOLUTION=640x360,FRAME-RATE=59.940,VIDEO-RANGE=PQ,CLOSED-CAPTIONS=NONE
+media.m3u8
diff --git a/test/test/assets/dv-p8-hevc/media.m3u8 b/test/test/assets/dv-p8-hevc/media.m3u8
new file mode 100644
index 0000000000..a38025e649
--- /dev/null
+++ b/test/test/assets/dv-p8-hevc/media.m3u8
@@ -0,0 +1,18 @@
+#EXTM3U
+#EXT-X-VERSION:6
+#EXT-X-TARGETDURATION:3
+#EXT-X-PLAYLIST-TYPE:VOD
+#EXT-X-MAP:URI="dovi_8-video.mp4",BYTERANGE="992@0"
+#EXTINF:2.002,
+#EXT-X-BYTERANGE:172013@1072
+dovi_8-video.mp4
+#EXTINF:2.002,
+#EXT-X-BYTERANGE:186781
+dovi_8-video.mp4
+#EXTINF:2.002,
+#EXT-X-BYTERANGE:202161
+dovi_8-video.mp4
+#EXTINF:0.017,
+#EXT-X-BYTERANGE:2221
+dovi_8-video.mp4
+#EXT-X-ENDLIST