Parsed: 112358

  public function ParseRIFFAMV($startoffset, $maxoffset) {
    // AMV files are RIFF-AVI files with parts of the spec deliberately broken, such as chunk size fields hardcoded to zero (because players known in hardware that these fields are always a certain size

    // https://code.google.com/p/amv-codec-tools/wiki/AmvDocumentation
    //typedef struct _amvmainheader {
    //FOURCC fcc; // 'amvh'
    //DWORD cb;
    //DWORD dwMicroSecPerFrame;
    //BYTE reserve[28];
    //DWORD dwWidth;
    //DWORD dwHeight;
    //DWORD dwSpeed;
    //DWORD reserve0;
    //DWORD reserve1;
    //BYTE bTimeSec;
    //BYTE bTimeMin;
    //WORD wTimeHour;
    //} AMVMAINHEADER;

    $info = &$this->getid3->info;
    $RIFFchunk = false;

    try {

      $this->fseek($startoffset);
      $maxoffset = min($maxoffset, $info['avdataend']);
      $AMVheader = $this->fread(284);
      if (substr($AMVheader,   0,  8) != 'hdrlamvh') {
        throw new Exception('expecting "hdrlamv" at offset '.($startoffset +   0).', found "'.substr($AMVheader,   0, 8).'"');
      }
      if (substr($AMVheader,   8,  4) != "\x38\x00\x00\x00") { // "amvh" chunk size, hardcoded to 0x38 = 56 bytes
        throw new Exception('expecting "0x38000000" at offset '.($startoffset +   8).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader,   8, 4)).'"');
      }
      $RIFFchunk = array();
      $RIFFchunk['amvh']['us_per_frame']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  12,  4));
      $RIFFchunk['amvh']['reserved28']     =                              substr($AMVheader,  16, 28);  // null? reserved?
      $RIFFchunk['amvh']['resolution_x']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  44,  4));
      $RIFFchunk['amvh']['resolution_y']   = getid3_lib::LittleEndian2Int(substr($AMVheader,  48,  4));
      $RIFFchunk['amvh']['frame_rate_int'] = getid3_lib::LittleEndian2Int(substr($AMVheader,  52,  4));
      $RIFFchunk['amvh']['reserved0']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  56,  4)); // 1? reserved?
      $RIFFchunk['amvh']['reserved1']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  60,  4)); // 0? reserved?
      $RIFFchunk['amvh']['runtime_sec']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  64,  1));
      $RIFFchunk['amvh']['runtime_min']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  65,  1));
      $RIFFchunk['amvh']['runtime_hrs']    = getid3_lib::LittleEndian2Int(substr($AMVheader,  66,  2));

      $info['video']['frame_rate']   = 1000000 / $RIFFchunk['amvh']['us_per_frame'];
      $info['video']['resolution_x'] = $RIFFchunk['amvh']['resolution_x'];
      $info['video']['resolution_y'] = $RIFFchunk['amvh']['resolution_y'];
      $info['playtime_seconds']      = ($RIFFchunk['amvh']['runtime_hrs'] * 3600) + ($RIFFchunk['amvh']['runtime_min'] * 60) + $RIFFchunk['amvh']['runtime_sec'];

      // the rest is all hardcoded(?) and does not appear to be useful until you get to audio info at offset 256, even then everything is probably hardcoded

      if (substr($AMVheader,  68, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x38\x00\x00\x00") {
        throw new Exception('expecting "LIST<0x00000000>strlstrh<0x38000000>" at offset '.($startoffset +  68).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader,  68, 20)).'"');
      }
      // followed by 56 bytes of null: substr($AMVheader,  88, 56) -> 144
      if (substr($AMVheader, 144,  8) != 'strf'."\x24\x00\x00\x00") {
        throw new Exception('expecting "strf<0x24000000>" at offset '.($startoffset + 144).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 144,  8)).'"');
      }
      // followed by 36 bytes of null: substr($AMVheader, 144, 36) -> 180

      if (substr($AMVheader, 188, 20) != 'LIST'."\x00\x00\x00\x00".'strlstrh'."\x30\x00\x00\x00") {
        throw new Exception('expecting "LIST<0x00000000>strlstrh<0x30000000>" at offset '.($startoffset + 188).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 188, 20)).'"');
      }
      // followed by 48 bytes of null: substr($AMVheader, 208, 48) -> 256
      if (substr($AMVheader, 256,  8) != 'strf'."\x14\x00\x00\x00") {
        throw new Exception('expecting "strf<0x14000000>" at offset '.($startoffset + 256).', found "'.getid3_lib::PrintHexBytes(substr($AMVheader, 256,  8)).'"');
      }
      // followed by 20 bytes of a modified WAVEFORMATEX:
      // typedef struct {
      // WORD wFormatTag;       //(Fixme: this is equal to PCM's 0x01 format code)
      // WORD nChannels;        //(Fixme: this is always 1)
      // DWORD nSamplesPerSec;  //(Fixme: for all known sample files this is equal to 22050)
      // DWORD nAvgBytesPerSec; //(Fixme: for all known sample files this is equal to 44100)
      // WORD nBlockAlign;      //(Fixme: this seems to be 2 in AMV files, is this correct ?)
      // WORD wBitsPerSample;   //(Fixme: this seems to be 16 in AMV files instead of the expected 4)
      // WORD cbSize;           //(Fixme: this seems to be 0 in AMV files)
      // WORD reserved;
      // } WAVEFORMATEX;
      $RIFFchunk['strf']['wformattag']      = getid3_lib::LittleEndian2Int(substr($AMVheader,  264,  2));
      $RIFFchunk['strf']['nchannels']       = getid3_lib::LittleEndian2Int(substr($AMVheader,  266,  2));
      $RIFFchunk['strf']['nsamplespersec']  = getid3_lib::LittleEndian2Int(substr($AMVheader,  268,  4));
      $RIFFchunk['strf']['navgbytespersec'] = getid3_lib::LittleEndian2Int(substr($AMVheader,  272,  4));
      $RIFFchunk['strf']['nblockalign']     = getid3_lib::LittleEndian2Int(substr($AMVheader,  276,  2));
      $RIFFchunk['strf']['wbitspersample']  = getid3_lib::LittleEndian2Int(substr($AMVheader,  278,  2));
      $RIFFchunk['strf']['cbsize']          = getid3_lib::LittleEndian2Int(substr($AMVheader,  280,  2));
      $RIFFchunk['strf']['reserved']        = getid3_lib::LittleEndian2Int(substr($AMVheader,  282,  2));


      $info['audio']['lossless']        = false;
      $info['audio']['sample_rate']     = $RIFFchunk['strf']['nsamplespersec'];
      $info['audio']['channels']        = $RIFFchunk['strf']['nchannels'];
      $info['audio']['bits_per_sample'] = $RIFFchunk['strf']['wbitspersample'];
      $info['audio']['bitrate']         = $info['audio']['sample_rate'] * $info['audio']['channels'] * $info['audio']['bits_per_sample'];
      $info['audio']['bitrate_mode']    = 'cbr';


    } catch (getid3_exception $e) {
      if ($e->getCode() == 10) {
        $this->warning('RIFFAMV parser: '.$e->getMessage());
      } else {
        throw $e;
      }
    }

    return $RIFFchunk;
  }