15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)'use strict';
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {MetadataDispatcher} parent Parent object.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @constructor
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function MpegParser(parent) {
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MetadataParser.call(this, parent, 'mpeg', /\.(mp4|m4v|m4a|mpe?g4?)$/i);
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.mimeType = 'video/mpeg';
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype = {__proto__: MetadataParser.prototype};
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Size of the atom header.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.HEADER_SIZE = 8;
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ByteReader} br ByteReader instance.
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_end End of atom position.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {number} Atom size.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.readAtomSize = function(br, opt_end) {
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var pos = br.tell();
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_end) {
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Assert that opt_end <= buffer end.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When supplied, opt_end is the end of the enclosing atom and is used to
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // check the correct nesting.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.validateRead(opt_end - pos);
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var size = br.readScalar(4, false, opt_end);
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (size < MpegParser.HEADER_SIZE)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw 'atom too short (' + size + ') @' + pos;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (opt_end && pos + size > opt_end)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throw 'atom too long (' + size + '>' + (opt_end - pos) + ') @' + pos;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return size;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ByteReader} br ByteReader instance.
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) * @param {number=} opt_end End of atom position.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {string} Atom name.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.readAtomName = function(br, opt_end) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return br.readString(4, opt_end).toLowerCase();
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} metadata Metadata object.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @return {Object} Root of the parser tree.
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.createRootParser = function(metadata) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function findParentAtom(atom, name) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (;;) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      atom = atom.parent;
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!atom) return null;
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (atom.name == name) return atom;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseFtyp(br, atom) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.brand = br.readString(4, atom.end);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseMvhd(br, atom) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var version = br.readScalar(4, false, atom.end);
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var offset = (version == 0) ? 8 : 16;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(offset, ByteReader.SEEK_CUR);
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var timescale = br.readScalar(4, false, atom.end);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var duration = br.readScalar(4, false, atom.end);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.duration = duration / timescale;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseHdlr(br, atom) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(8, ByteReader.SEEK_CUR);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    findParentAtom(atom, 'trak').trackType = br.readString(4, atom.end);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseStsd(br, atom) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var track = findParentAtom(atom, 'trak');
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (track && track.trackType == 'vide') {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.seek(40, ByteReader.SEEK_CUR);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      metadata.width = br.readScalar(2, false, atom.end);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      metadata.height = br.readScalar(2, false, atom.end);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseDataString(name, br, atom) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(8, ByteReader.SEEK_CUR);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata[name] = br.readString(atom.end - br.tell(), atom.end);
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  function parseCovr(br, atom) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(8, ByteReader.SEEK_CUR);
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    metadata.thumbnailURL = br.readImage(atom.end - br.tell(), atom.end);
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // 'meta' atom can occur at one of the several places in the file structure.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var parseMeta = {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ilst: {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '©nam': { data: parseDataString.bind(null, 'title') },
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '©alb': { data: parseDataString.bind(null, 'album') },
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      '©art': { data: parseDataString.bind(null, 'artist') },
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'covr': { data: parseCovr }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    versioned: true
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // main parser for the entire file structure.
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return {
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ftyp: parseFtyp,
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    moov: {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      mvhd: parseMvhd,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      trak: {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        mdia: {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          hdlr: parseHdlr,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          minf: {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            stbl: {
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              stsd: parseStsd
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            }
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        meta: parseMeta
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      udta: {
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        meta: parseMeta
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      },
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      meta: parseMeta
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    },
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    meta: parseMeta
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {File} file File.
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} metadata Metadata.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(Object)} callback Success callback.
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onError Error callback.
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype.parse = function(file, metadata, callback, onError) {
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.rootParser_ = MpegParser.createRootParser(metadata);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Kick off the processing by reading the first atom's header.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.requestRead(file, 0, MpegParser.HEADER_SIZE, null,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      onError, callback.bind(null, metadata));
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(ByteReader, Object)|Object} parser Parser tree node.
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ByteReader} br ByteReader instance.
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} atom Atom descriptor.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} filePos File position of the atom start.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype.applyParser = function(parser, br, atom, filePos) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (this.verbose) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var path = atom.name;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (var p = atom.parent; p && p.name; p = p.parent) {
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      path = p.name + '.' + path;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var action;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!parser) {
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      action = 'skipping ';
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (parser instanceof Function) {
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      action = 'parsing  ';
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      action = 'recursing';
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var start = atom.start - MpegParser.HEADER_SIZE;
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.vlog(path + ': ' +
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              '@' + (filePos + start) + ':' + (atom.end - start),
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              action);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (parser) {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (parser instanceof Function) {
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.pushSeek(atom.start);
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      parser(br, atom);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.popSeek();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (parser.versioned) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        atom.start += 4;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.parseMpegAtomsInRange(parser, br, atom, filePos);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function(ByteReader, Object)|Object} parser Parser tree node.
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ByteReader} br ByteReader instance.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {Object} parentAtom Parent atom descriptor.
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} filePos File position of the atom start.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype.parseMpegAtomsInRange = function(
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    parser, br, parentAtom, filePos) {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var count = 0;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (var offset = parentAtom.start; offset != parentAtom.end;) {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (count++ > 100) // Most likely we are looping through a corrupt file.
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw 'too many child atoms in ' + parentAtom.name + ' @' + offset;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    br.seek(offset);
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var size = MpegParser.readAtomSize(br, parentAtom.end);
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var name = MpegParser.readAtomName(br, parentAtom.end);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.applyParser(
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        parser[name],
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        br,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        { start: offset + MpegParser.HEADER_SIZE,
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          end: offset + size,
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          name: name,
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          parent: parentAtom
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        },
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filePos
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    );
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    offset += size;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {File} file File.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} filePos Start position in the file.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} size Atom size.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Atom name.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onError Error callback.
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onSuccess Success callback.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype.requestRead = function(
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    file, filePos, size, name, onError, onSuccess) {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var self = this;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var reader = new FileReader();
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.onerror = onError;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.onload = function(event) {
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.processTopLevelAtom(
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        reader.result, file, filePos, size, name, onError, onSuccess);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.vlog('reading @' + filePos + ':' + size);
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reader.readAsArrayBuffer(file.slice(filePos, filePos + size));
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {ArrayBuffer} buf Data buffer.
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {File} file File.
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} filePos Start position in the file.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {number} size Atom size.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {string} name Atom name.
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onError Error callback.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * @param {function} onSuccess Success callback.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MpegParser.prototype.processTopLevelAtom = function(
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    buf, file, filePos, size, name, onError, onSuccess) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var br = new ByteReader(buf);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the header has already been read.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var atomEnd = size - MpegParser.HEADER_SIZE;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    var bufLength = buf.byteLength;
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Check the available data size. It should be either exactly
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // what we requested or HEADER_SIZE bytes less (for the last atom).
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (bufLength != atomEnd && bufLength != size) {
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throw 'Read failure @' + filePos + ', ' +
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'requested ' + size + ', read ' + bufLength;
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Process the top level atom.
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (name) { // name is null only the first time.
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.applyParser(
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          this.rootParser_[name],
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          br,
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          {start: 0, end: atomEnd, name: name},
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          filePos
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      );
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    filePos += bufLength;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (bufLength == size) {
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The previous read returned everything we asked for, including
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // the next atom header at the end of the buffer.
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Parse this header and schedule the next read.
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      br.seek(-MpegParser.HEADER_SIZE, ByteReader.SEEK_END);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var nextSize = MpegParser.readAtomSize(br);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      var nextName = MpegParser.readAtomName(br);
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If we do not have a parser for the next atom, skip the content and
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // read only the header (the one after the next).
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (!this.rootParser_[nextName]) {
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filePos += nextSize - MpegParser.HEADER_SIZE;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        nextSize = MpegParser.HEADER_SIZE;
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.requestRead(file, filePos, nextSize, nextName, onError, onSuccess);
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // The previous read did not return the next atom header, EOF reached.
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.vlog('EOF @' + filePos);
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      onSuccess();
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } catch (e) {
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    onError(e.toString());
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)MetadataDispatcher.registerParserClass(MpegParser);
318