"use strict";
/** HEIF file reader object.
* @constructor
* @param {string} url URL of the HEIF file for reading. */
function HEIFReader (url) {
var self = this;
var _name = "HEIFReader";
this._url = url;
this._readerWrapper = null;
this._testModeUrl = "testMode";
// **** PUBLIC API BEGINS ****
/** Common file info structure for both meta and trak.
* @constructor
* @param fileFeature FileFeature structure.
* @param trackProperties TrackProperties structure if present.
* @param rootLevelMetaBoxProperties RootLevelMetaBoxProperties structure if present.
* @param moovProperties MoovProperties structure if present.
* @param fileFeature FileFeature structure.
* @See requestFileInfo(). */
function FileInfo (
fileFeature,
trackProperties,
rootLevelMetaBoxProperties,
moovProperties) {
var self = this;
this.fileFeature = fileFeature;
this.trackProperties = trackProperties;
this.rootLevelMetaBoxProperties = rootLevelMetaBoxProperties;
this.moovProperties = moovProperties;
/** Convenience method to get the type of the given contextId. */
this.getContextType = function (contextId) {
if (trackProperties && trackProperties.length) {
for (var i in trackProperties) {
if (trackProperties[i].trackId === contextId) {
return "trak";
}
}
} else if (rootLevelMetaBoxProperties && rootLevelMetaBoxProperties.contextId === contextId) {
return "meta";
} else if (moovProperties && moovProperties.moovId === contextId) {
return "moov";
}
return null;
};
}
/** @return {string} The url. */
this.url = function () {
return self._url;
}
/** @return {FileInfo} FileInfo object as a callback parameter. */
this.requestFileInfo = function (callback) {
if (this._url !== this._testModeUrl) {
if (this._url.indexOf("http:") === -1 &&
this._url.indexOf("https:") === -1 &&
this._url[0] !== '/') {
this._url = "/" + this._url;
}
console.log(_name + ": REQUEST FILE INFO: " + this._url);
var xhr = new XMLHttpRequest();
xhr.open('GET', this._url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function (e) {
self._buildFileInfoObject(new Uint8Array(this.response));
var payload = self._fileInfo;
payload.success = true;
callback(payload);
};
xhr.send();
} else {
console.log(_name + ": REQUEST TEST FILE INFO: " + this._url);
self._buildFileInfoObject(null);
}
};
/** @return {string} Major brand from the File Type Box */
this.getMajorBrand = function() {
return self._readerWrapper.getMajorBrand();
};
/** @return {Array.<string>} Compatible brands list from the File Type Box */
this.getCompatibleBrands = function () {
return JSON.parse(self._readerWrapper.getCompatibleBrandsInJSON());
};
/** Get the ID of the primary frame of the frame source. This is the default frame to display.
* @param {number} contextId Context ID of a track / root-level metabox.
* @return {number} Item ID of the primary frame of the frame source. */
this.getCoverImageItemId = function (contextId) {
return self._readerWrapper.getCoverImageItemId(contextId);
};
/** Get VPS, SPS and PPS of given item concatenated as a sigle Uint8Array.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId Item ID of the requested item.
* @return {Uint8Array} Decoder parameter data. */
this.getDecoderParameters = function (contextId, itemId) {
return self._readerWrapper.getDecoderParameters(contextId, itemId);
};
/** Get maximum display width from track headers.
* @param {number} contextId Context ID of a track.
* @return {number} Maximum display width in pixels. */
this.getDisplayWidth = function (contextId) {
return self._readerWrapper.getDisplayWidth(contextId);
};
/** Get maximum display height from track headers.
* @param {number} contextId Context ID of a track.
* @return {number} Maximum display height in pixels. */
this.getDisplayHeight = function (contextId) {
return self._readerWrapper.getDisplayHeight(contextId);
};
/** @return {Uint8Array} Item data of given item.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId ItemId can be any image item/sample in the metabox/track designated by the contextId. */
this.getItemData = function (contextId, itemId) {
return self._readerWrapper.getItemData(contextId, itemId);
};
/** @return {Uint8Array} Item data with decoder parameters of given item.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId ItemId can be any image item/sample in the metabox/track designated by the contextId. */
this.getItemDataWithDecoderParameters = function (contextId, itemId) {
return self._readerWrapper.getItemDataWithDecoderParameters(contextId, itemId);
};
/** @return {Uint32Array} Decode dependencies of given item.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId ItemId can be any image item/sample in the metabox/track designated by the contextId. */
this.getItemDecodeDependencies = function (contextId, itemId) {
return self._readerWrapper.getItemDecodeDependencies(contextId, itemId);
};
/** Get data for item of type Image grid for given (contextId, itemId) pair.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId ItemId can be any image item/sample in the metabox/track designated by the contextId.
* @see getItemIovl() */
this.getItemGrid = function (contextId, itemId) {
return JSON.parse(self._readerWrapper.getItemGridInJSON(contextId, itemId));
};
/** @return list of items in the container with the ID contextId having the requested itemType as Uint32Array.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {string} type If contextId refers to a MetaBox then itemType can be the following:
* 'master' , 'hidden', 'pre-computed', 'hvc1', 'iovl', 'grid', 'Exif', 'mime', 'hvt1'
* ('master' is ('hvc1' - iref('thmb') or iref('auxl')))
* If the contextId refers to a media track; then itemType can be the following:
* 'out_ref' : output reference frames
* 'out_non_ref' : output non-reference frames
* 'non_out_ref' : non-output reference frame
* 'display' : all frame samples in the track which are displayed and in display order
* 'samples' : all samples in the track in track's entry order
* @return Found items. The order of the itemIds are as present in the file.
* An empty vector if no items are found. */
this.getItemListByType = function (contextId, type) {
return self._readerWrapper.getItemListByType(contextId, type);
};
/** Get data for item of type Image overlay for given (contextId, itemId) pair.
* @param {number} contextId Meta box context id
* @param {number} itemId Id of Image overlay item
* @return IovlItem struct with requested data. */
this.getItemIovl = function (contextId, itemId) {
return JSON.parse(self._readerWrapper.getItemIovlInJSON(contextId, itemId));
};
/** @return {number} Height of the given item.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId itemId can be any image item/sample in the metabox/track designated by the contextId. */
this.getItemHeight = function (contextId, itemId) {
return self._readerWrapper.getItemHeight(contextId, itemId);
};
/** @return {number} Width of the given item.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId itemId can be any image item/sample in the metabox/track designated by the contextId. */
this.getItemWidth = function (contextId, itemId) {
return self._readerWrapper.getItemWidth(contextId, itemId);
};
/**
* @typedef {object} HEIFReader~ItemTimestamp
* @property {number} t - Timestamp in milliseconds.
* @property {number} id - Id of the item. */
/** Get display timestamp for each item.
* If the resource pointed by contextId is a media track, then timestamps are read from the track sample data.
* If the resource is a metabox, a forced generation of timestamps can be done by defining a forced FPS and
* a forced tickspersecond.
* @param {number} contextId Context ID of a track.
* @return {Array.<HEIFReader~ItemTimestamp>} ItemTimestamp array sorted by timestamps. */
this.getItemTimestamps = function (contextId) {
return JSON.parse(self._readerWrapper.getItemTimestampsInJSON(contextId));
};
/** Get properties of an item
* @param {number} contextId Context ID to operate in. Must be a meta box.
* @param {number} itemId Item ID which properties to get. */
this.getItemProperties = function(contextId, itemId) {
return JSON.parse(self._readerWrapper.getItemPropertiesInJSON(contextId, itemId));
};
/** Get items in decoding order.
* If the resource pointed by contextId is a media track, then timestamp is read from the track with ID contextId
* and sample with ID equal to itemId.
* If the resource is a metabox, a forced generation of timestamps can be done by defining a forced FPS.
* @param {number} contextId Track or metabox context id.
* @return {Array.<HEIFReader~ItemTimestamp>} ItemTimestamp array sorted in decoding order.
* Also complete decoding dependencies are listed here. If an item ID is present
* as a decoding dependency for a succeeding frame, its timestamp is set to 0xffffffff. */
this.getItemsInDecodingOrder = function (contextId) {
return JSON.parse(self._readerWrapper.getItemsInDecodingOrderInJSON(contextId));
};
/** Get itemType of the item pointed by (contextId, itemId) pair
* The order of the itemIds are as present in the file
* @param {number} contextId Track or metabox context id.
* @param {number} itemId Track or metabox sample/item id.
* @return The itemType of the item pointed by (contextId, itemId) pair.
* If the context is a metabox, can be the following:
* 'master' , 'hidden', 'pre-computed', 'hvc1', 'iovl', 'grid', 'Exif', 'mime', 'hvt1', 'iden'
* ('master' is ('hvc1' - iref('thmb') or iref('auxl')))'
* If the context is a media track sample description entry type is returned. */
this.getItemType = function(contextId, itemId) {
return self._readerWrapper.getItemType(contextId, itemId);
};
/** Get playback duration of image sequence or media track in seconds.
* This considers also edit lists.
* @param {number} contextId Context ID of a track.
* @return {number} The playback duration of image sequence or media track in seconds.
* Returns (# of displayable master images)/(forced framerate), if a forced sequential playback is
* signaled by calling SetForcedTimedPlayback() for a metabox.
* Returns 0 for an image collection or other items. */
this.getPlaybackDurationInSecs = function (contextId) {
return self._readerWrapper.getPlaybackDurationInSecs(contextId);
};
/** Get item property Auxiliary ('auxC')
* @see getPropertyAuxc() */
this.getPropertyAuxc = function (contextId, index) {
return JSON.parse(self._readerWrapper.getPropertyAuxcInJSON(contextId, index));
};
/** Get item property Clean aperture ('clap')
* @see getPropertyIrot() */
this.getPropertyClap = function (contextId, index) {
return JSON.parse(self._readerWrapper.getPropertyClapInJSON(contextId, index));
};
/** Get item property Image Rotation ('irot')
* @param {number} contextId Meta box context id.
* @param {number} index Index of the property. This value is given by getItemProperties(). */
this.getPropertyIrot = function (contextId, index) {
return JSON.parse(self._readerWrapper.getPropertyIrotInJSON(contextId, index));
};
/** Get item property Relative Location ('rloc')
* @see getPropertyIrot() */
this.getPropertyRloc = function (contextId, index) {
return JSON.parse(self._readerWrapper.getPropertyRlocInJSON(contextId, index));
};
/** Get list of referenced items for item with (contextId, itemId) pair having the requested referenceType.
* @param {number} contextId The context id.
* @param {number} fromItemId Id of the item referenced from.
* @param {string} referenceType Can be the following: 'thmb', 'cdcs', 'auxl', 'dimg', 'base'
* ('master' is ('hvc1' - iref('thmb') or iref('auxl')))
* @return itemIds List of referenced items for item with (contextId, itemId) pair having the
* requested referenceType.
* The order of the itemIds are as present in the file.
* An empty vector if no items are found. */
this.getReferencedFromItemListByType = function (contextId, fromItemId, referenceType) {
var refs = JSON.parse(self._readerWrapper.getReferencedFromItemListByTypeInJSON(contextId, fromItemId, referenceType));
if (refs && refs.constructor !== Array) {
return [refs];
}
return refs;
};
/** Get the list of referenced items for item with (contextId, itemId) pair having the requested referenceType.
* @param {number} contextId The context id.
* @param {number} toItemId Id of the item referenced to.
* @param {string} referenceType Can be the following: 'thmb', 'cdcs', 'auxl', 'dimg', 'base'
* ('master' is ('hvc1' - iref('thmb') or iref('auxl')))
* @param itemIds Found items. The order of the itemIds are as present in the file.
* An empty vector if no items are found. */
this.getReferencedToItemListByType = function (contextId, toItemId, referenceType) {
var refs = JSON.parse(self._readerWrapper.getReferencedToItemListByTypeInJSON(contextId, toItemId, referenceType));
if (refs && refs.constructor !== Array) {
return [refs];
}
return refs;
};
/** Get display timestamps for an item. An item may be displayed many times based on the edit list.
* If the resource pointed by contextId is a media track, then timestamp is read from the track with ID contextId
* and sample with ID equal to itemId.
* If the resource is a metabox, a forced generation of timestamps can be done by defining a forced FPS and
* a forced tickspersecond.
* @param {number} contextId Context ID of a track / root-level metabox.
* @param {number} itemId Id of the item. */
this.getTimestampsOfItem = function (contextId, itemId) {
return JSON.parse(self._readerWrapper.getTimestampsOfItemInJSON(contextId, itemId));
};
/** Set the forced loop playback mode for an image sequence/samples in metabox/track.
* @param {number} contextId Context ID of a track.
* @param {boolean} forceLoopPlayback True = set forced loop playback on, false = set forced loop playback off. */
this.setForcedLoopPlayback = function (contextId, forceLoopPlayback) {
self._readerWrapper.setForcedLoopPlayback(contextId, forceLoopPlayback);
};
/** Set the forced timed playback mode for an image sequence in metabox.
* If the resource pointed by contextId is a media track, then the values are ignored.
* If the resource is a metabox, a forced generation of timestamps can be done by defining a forced FPS.
* @param {number} contextId Context ID of a track.
* @param {number} forcedFps Frames per second. */
this.setForcedTimedPlayback = function (contextId, forcedFps) {
self._readerWrapper.setForcedTimedPlayback(contextId, forcedFps);
};
// **** PUBLIC API ENDS ****
this._buildFileInfoObject = function (uInt8Array) {
if (uInt8Array !== null) {
// Implement DataSourceIf. The "C++"-code calls DataSource.fetch() when
// readerWrapper.getFileProperties() is called.
var DataSource = new hevcReaderModule.DataSourceIf.extend("DataSourceIf", {
bytesRead: 0,
fetch: function(numBytes) {
if (numBytes === -1) {
return uInt8Array;
} else {
var data = uInt8Array.subarray(bytesRead, numBytes);
bytesRead += data.length;
}
}
});
} else {
DataSource = new hevcReaderModule.DataSourceIf.extend("DataSourceIf", {
bytesRead: 0,
fetch: function(numBytes) {
return "";
}
});
}
var dataSource = new DataSource;
self._readerWrapper = new hevcReaderModule.ReaderWrapper(self._url === self._testModeUrl);
self._readerWrapper.setDataSource(dataSource);
var fileProperties = JSON.parse(self._readerWrapper.getFilePropertiesInJSON());
self._fileInfo = new FileInfo(
fileProperties.fileFeature,
fileProperties.trackProperties,
fileProperties.rootLevelMetaBoxProperties,
fileProperties.moovProperties);
console.log(self._fileInfo);
console.log("Major brand: " + self.getMajorBrand());
console.log("Compatible brands: " + self.getCompatibleBrands());
};
}