rx-player
Version:
Canal+ HTML5 Video Player
495 lines (472 loc) • 18.3 kB
text/typescript
import { describe, it, expect, vi } from "vitest";
import type { IParsedRepresentation } from "../../../parsers/manifest";
import CodecSupportCache from "../codec_support_cache";
import Representation from "../representation";
import type { IRepresentationIndex } from "../representation_index";
const minimalIndex: IRepresentationIndex = {
getInitSegment() {
return null;
},
getSegments() {
return [];
},
isInitialized() {
return false;
},
addPredictedSegments() {
return;
},
initialize() {
return;
},
shouldRefresh() {
return false;
},
getFirstAvailablePosition(): undefined {
return;
},
getLastAvailablePosition(): undefined {
return;
},
getEnd(): undefined {
return;
},
awaitSegmentBetween(): undefined {
return;
},
checkDiscontinuity() {
return null;
},
isSegmentStillAvailable(): undefined {
return;
},
isStillAwaitingFutureSegments(): boolean {
return true;
},
canBeOutOfSyncError(): true {
return true;
},
getTargetSegmentDuration() {
return undefined;
},
_replace() {
return;
},
_update() {
return;
},
};
describe("Manifest - Representation", () => {
const cache = new CodecSupportCache([]);
it("should be able to create Representation with the minimum arguments given", () => {
const args = {
bitrate: 12,
id: "test",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual([]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe(undefined);
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe(';codecs=""');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
expect(spyCodecCache).toHaveBeenCalledWith("", "", false);
spyCodecCache.mockClear();
});
it("should be able to add a height attribute", () => {
const args = {
bitrate: 12,
id: "test",
height: 57,
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "video", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual([]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(57);
expect(representation.mimeType).toBe(undefined);
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe(';codecs=""');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a width attribute", () => {
const args = {
bitrate: 12,
id: "test",
width: 2,
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "video", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual([]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe(undefined);
expect(representation.width).toBe(2);
expect(representation.getMimeTypeString()).toBe(';codecs=""');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a codecs attribute", () => {
const args = {
bitrate: 12,
id: "test",
codecs: "vp9",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual(["vp9"]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe(undefined);
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe(';codecs="vp9"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a supplementalCodecs attribute", () => {
const args = {
bitrate: 12,
id: "test",
codecs: "vp9",
supplementalCodecs: "test",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual(["test", "vp9"]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe(undefined);
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe(';codecs="test"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a mimeType attribute", () => {
const args = {
bitrate: 12,
id: "test",
mimeType: "audio/mp4",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual([]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe("audio/mp4");
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe('audio/mp4;codecs=""');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a contentProtections attribute", () => {
const args = {
bitrate: 12,
id: "test",
index: minimalIndex,
mimeType: "video/mp4",
codecs: "vp12",
contentProtections: {
keyIds: [{ keyId: new Uint8Array([45]) }],
initData: {
cenc: [
{
systemId: "EDEF",
data: new Uint8Array([78]),
},
],
},
},
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "video", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual(["vp12"]);
expect(representation.contentProtections).toBe(args.contentProtections);
expect(representation.frameRate).toBe(undefined);
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe("video/mp4");
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe('video/mp4;codecs="vp12"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to add a frameRate attribute", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "audio/mp4",
codecs: "mp4a.40.2",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual(["mp4a.40.2"]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe("1/60");
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe("audio/mp4");
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe('audio/mp4;codecs="mp4a.40.2"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should be able to return an exploitable codecs + mimeType string", () => {
const args1 = {
bitrate: 12,
id: "test",
index: minimalIndex,
} as unknown as IParsedRepresentation;
expect(new Representation(args1, "audio", cache).getMimeTypeString()).toBe(
';codecs=""',
);
const args2 = {
bitrate: 12,
id: "test",
mimeType: "foo",
index: minimalIndex,
} as unknown as IParsedRepresentation;
expect(new Representation(args2, "audio", cache).getMimeTypeString()).toBe(
'foo;codecs=""',
);
const args3 = {
bitrate: 12,
id: "test",
codecs: "bar",
index: minimalIndex,
} as unknown as IParsedRepresentation;
expect(new Representation(args3, "audio", cache).getMimeTypeString()).toBe(
';codecs="bar"',
);
const args4 = {
bitrate: 12,
id: "test",
mimeType: "foo",
codecs: "bar",
index: minimalIndex,
} as unknown as IParsedRepresentation;
expect(new Representation(args4, "audio", cache).getMimeTypeString()).toBe(
'foo;codecs="bar"',
);
});
it("should set `isSupported` of non-supported codecs or mime-type to `false`", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "audio/mp4",
codecs: "mp4a.40.2",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const customCache = new CodecSupportCache([
{
mimeType: "audio/mp4",
codec: "mp4a.40.2",
supported: false,
supportedIfEncrypted: false,
},
]);
const spyCodecCache = vi.spyOn(customCache, "isSupported");
const representation = new Representation(args, "audio", customCache);
expect(representation.id).toBe("test");
expect(representation.bitrate).toBe(12);
expect(representation.index).toBe(minimalIndex);
expect(representation.codecs).toEqual(["mp4a.40.2"]);
expect(representation.contentProtections).toBe(undefined);
expect(representation.frameRate).toBe("1/60");
expect(representation.height).toBe(undefined);
expect(representation.mimeType).toBe("audio/mp4");
expect(representation.width).toBe(undefined);
expect(representation.getMimeTypeString()).toBe('audio/mp4;codecs="mp4a.40.2"');
expect(representation.isSupported).toBe(false);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
expect(spyCodecCache).toHaveBeenCalledWith("audio/mp4", "mp4a.40.2", false);
spyCodecCache.mockClear();
});
it("should not check support for a text media buffer", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
spyCodecCache.mockReset(); // TODO: find out why this is necessary
const representation = new Representation(args, "text", cache);
expect(representation.codecs).toEqual(["boop"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="boop"');
expect(representation.isSupported).toBe(true);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(0);
spyCodecCache.mockClear();
});
it("should not set isSupported if codec cache returns undefined", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.codecs).toEqual(["boop"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="boop"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should have both supplementalCodecs and codecs if codec cache returns undefined", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
supplementalCodecs: "bap",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi.spyOn(cache, "isSupported");
const representation = new Representation(args, "audio", cache);
expect(representation.codecs).toEqual(["bap", "boop"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="bap"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
it("should only have codecs if codec cache returns false for the supplementalCodecs and true for the codecs", () => {
const spyCodecCache = vi
.spyOn(cache, "isSupported")
.mockImplementation((_mimeType: string, codec: string, _encrypted: boolean) => {
return codec === "boop";
});
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
supplementalCodecs: "bap",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const representation = new Representation(args, "audio", cache);
expect(representation.codecs).toEqual(["boop"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="boop"');
expect(representation.isSupported).toBe(true);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalled();
spyCodecCache.mockClear();
});
it("should have both supplementalCodecs and codecs if codec cache returns undefined for the former and true for the latter", () => {
const spyCodecCache = vi
.spyOn(cache, "isSupported")
.mockImplementation((_mimeType: string, codec: string, _encrypted: boolean) => {
return codec === "boop" ? true : undefined;
});
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
supplementalCodecs: "bap",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const representation = new Representation(args, "audio", cache);
expect(representation.codecs).toEqual(["bap", "boop"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="bap"');
expect(representation.isSupported).toBe(undefined);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalled();
spyCodecCache.mockClear();
});
it("should only have supplementalCodecs if codec cache returns true for the former", () => {
const args = {
bitrate: 12,
id: "test",
frameRate: "1/60",
mimeType: "bip",
codecs: "boop",
supplementalCodecs: "bap",
index: minimalIndex,
} as unknown as IParsedRepresentation;
const spyCodecCache = vi
.spyOn(cache, "isSupported")
.mockImplementation((_mimeType: string, codec: string, _encrypted: boolean) => {
return codec === "bap" ? true : undefined;
});
const representation = new Representation(args, "audio", cache);
expect(representation.codecs).toEqual(["bap"]);
expect(representation.mimeType).toBe("bip");
expect(representation.getMimeTypeString()).toBe('bip;codecs="bap"');
expect(representation.isSupported).toBe(true);
expect(representation.decipherable).toBe(undefined);
expect(spyCodecCache).toHaveBeenCalledTimes(1);
spyCodecCache.mockClear();
});
});