Skip to content

IV. HEIF Reader JavaScript Implementation

Nokia Technologies edited this page Oct 21, 2015 · 9 revisions

HEIF Reader JavaScript Implementation:

This is a pre-built JavaScript port of the HEIF Reader library and API using Emscripten and asm.js. It can be used with an HEVC/H.265 decoder JavaScript implementation (such as libde265.JS)

All the JavaScript files can be found under the /JS directory of "gh-pages" branch.

Complete JsDoc documentation of the JavaScript API can be found here.

Please follow this link to see HEIF file examples that use the JavaScript implementation.

HEIF Reader JavaScript Interface

This section describes the usage of heif-api.js interface. The interface provides all the needed functions to read properties and items from a HEIF (*.heic) file. In addition, the documentation briefly describes other JavaScript files used in the HEIF web site to decode image data and display or animate it in many different layouts.

heif-api.js

This javascript file provides HEIFReader object. User first creates a HEIFReader object giving file URL as construction parameter. After that user calls requestFileInfo(callback) function to request file information. FileInfo may be requested as follows:

HEIFReader reader = new HEIFReader(url);
reader.requestFileInfo(function(payload) {
  if(payload.success !== true) {
    console.error("Could not read file:", url);
  } else {
    var fileInfo = payload;
    console.log("FileInfo contents:", fileInfo);
  }
});

In case the file contains single images, rootLevelMetaBoxProperties item is present in the FileInfo. The example code below shows how rootLevelMetaBox master item ids can be requested using the API.

// META file containing still images
if (fileInfo.rootLevelMetaBoxProperties) {
  var masterContextId = fileInfo.rootLevelMetaBoxProperties.contextId;
  var masterItemIds = [];
  var imageFeaturesMap = fileInfo.rootLevelMetaBoxProperties.imageFeaturesMap;
  for (i in imageFeaturesMap) {
    if (imageFeaturesMap.hasOwnProperty(i) && imageFeaturesMap[i].isMasterImage === true) {
      masterItemIds.push(parseInt(i));
    }
  }

  console.log("Master images in the file:", masterItemIds);
}

The next step is to retrieve hevc encoded bitstream data for a specific master image item. The item may have dependencies to other items that has to be resolved. Image data for all the master image items in the file can be requested as follows:

/** Request decoded image data for given item id's.
 *  @param {number} contextId Id of the context.
 *  @param {Array.<number>} itemIds Array of item id's to be decoded.
 *  @param {function} callback Callback function that receives the payload as a parameter. */
this.requestImageData = function (contextId, itemIds, callback) {
  console.log(_name + ": REQUEST IMAGE DATA: " + itemIds);

  if (itemIds.constructor !== Array) {
    itemIds = [itemIds];
  }

  if (itemIds.length < 1) {
    return;
  }

  var stream = new Uint8Array();
  var decodeIds = [];
  var dependencies = {};
  var displayDependencies = {};
  for (var i = 0; i < itemIds.length; i++) {
    var refs = self._heifReader.getItemDecodeDependencies(contextId, itemIds[i]);
    for (var j = 0; j < refs.length; j++) {
      if (refs[j] !== itemIds[i]) {
        // Add dependency data to the stream if the id is not already listed in dependencies
        if(!(refs[j] in dependencies) && !(refs[j] in displayDependencies)) {
          dependencies[refs[j]] = true;
          stream = _appendBuffer(stream, new Uint8Array(self._heifReader.getItemDataWithDecoderParameters(contextId, refs[j])));
          decodeIds.push(refs[j]);
        }
      } else {
        // Item has itself as dependency => we want to display that item despite being a dependency
        displayDependencies[refs[j]] = true;
      }
    }
  }

  // Add data of the actual items to the stream (that already contains dependency data)
  for (i = 0; i < itemIds.length; i++) {
    stream = _appendBuffer(stream, new Uint8Array(self._heifReader.getItemDataWithDecoderParameters(contextId, itemIds[i])));
    decodeIds.push(itemIds[i]);
  }

  var payload = new Payload(true, [], 0, 0);
  self._hevcDecoder.pushRequestContext(new RequestContext(callback, decodeIds, stream, dependencies, payload));
  self._hevcDecoder.decode();
};

The stream object contains the hevc encoded binary data retrieved by the getItemDataWithDecoderParameters() call. HevcDecoder, RequestContext and Payload objects are helper classes used in the HEIF website examples. HevcDecoder decodes hevc encoded stream to RGBA image data.

Example sequence diagram is shown below: Sequence diagram

Other Javascript files used in the examples

The examples presented in the HEIF file format web site include the following JavaScript files to implement full HEIF file format support from parsing to rendering:

  • heif-api.js
    • described in the previous chapter.
  • libde265.js
  • hevc-decoder.js
    • interface for using libde265.js H265 decoder.
  • image-provider.js
    • contains helper functions to use heif-api.js and hevc-decoder.js
  • heif-extension.js
    • walks through all the <img> and <video> HTML elements and replaces them by HEIF canvas elements.

The overall architecture for the examples is shown below <TODO: Insert image> Overall architecture