blob: 5502b5680e25646f020a42d77e62d15b8e83af72 [file] [log] [blame]
import {logError} from '../components/error_logger';
import {download} from './downloader';
/** A class that holds media metadata. */
export class Media {
readonly url: string;
private metadata?: MediaMetadata;
constructor(filename: string) {
/** @type {string} Asset url. */
this.url = `assets/${filename}`;
}
async getMetadata(): Promise<MediaMetadata> {
if (!this.metadata) {
this.metadata = await this.fetchMetadata();
}
return this.metadata;
}
/**
* @return Whether the video is a progressive video.
*/
async isProgressive(): Promise<boolean> {
const metadata = await this.getMetadata();
return metadata.type === 'mp42';
}
/** Downloads the head and builds metadata. */
private async fetchMetadata(): Promise<MediaMetadata> {
if (this.url.substr(-3) !== 'mp4') {
// TODO: Add more media type support.
return {};
}
// Metadata should be included in the first 10K.
const buffer = await download(this.url, 0, 1024 * 10);
if (buffer.byteLength === 0) {
return {};
}
const headBuffer = buffer.slice(0, getSize(buffer));
const type = getFtypType(headBuffer);
return {
type,
};
}
}
interface MediaMetadata {
/** The ftyp type. */
type?: string;
}
/**
* Gets the buffer size from the first 4 bytes.
* @return Buffer size.
*/
function getSize(buffer: ArrayBuffer): number {
const dv = new DataView(buffer.slice(0, 4));
return dv.getUint32(0);
}
/**
* Turns the buffer into a string.
* @return The generated string.
*/
function stringifyBuffer(
buffer: ArrayBuffer, begin: number, end: number): string {
return String.fromCharCode.apply(
null, Array.from(new Uint8Array(buffer.slice(begin, end))));
}
/**
* Gets the ftyp type from the buffer.
* @return The ftyp type. Undefined if there is an error.
*/
function getFtypType(buffer: ArrayBuffer): string|undefined {
const code = stringifyBuffer(buffer, 4, 8);
if (code !== 'ftyp') {
logError(`unknown type code ${code}`);
return;
}
return stringifyBuffer(buffer, 8, 12);
}