mmplugins/lib3gp/impl/src/atom.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 11 Jun 2010 15:13:33 +0300
changeset 24 2672ba96448e
parent 0 40261b775718
child 23 545d349d14da
permissions -rw-r--r--
Revision: 201023 Kit: 2010123

// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include <3gplibrary/mp4config.h>
#include <3gplibrary/mp4lib.h>
#include "mp4atom.h"
#include "mp4memwrap.h"
#include "mp4buffer.h"
#include "mp4endian.h"
#include "mp4file.h"
#include "mp4utils.h"

// MACROS
// Debug print macro
#ifdef _DEBUG
#include <e32svr.h>
//#define PRINT(x) RDebug::Print x
#define PRINT(x)
#else
#define PRINT(x)
#endif

mp4_i32 readBoxHeader(MP4HandleImp handle, mp4_i64* size, mp4_u32* type);
 
/*
 * Function:
 *
 *   mp4_i32 metaDataAvailable(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function determines whether meta data is available for reading
 *   or not.
 *
 *   Meta data is available if the input is in a file.
 *
 *   When reading from a stream, meta data is considered available if
 *   it is in the beginning of the stream and the entire Moov atom has
 *   been received. FTYP atom is allowed before MOOV atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *
 * Return value:
 *
 *   0                Meta data is not available because enough data has not
 *                    been inserted into the library
 *   1                Meta data is available
 *   Negative value   Meta data is not available because of fatal error
 *
 */
mp4_i32 metaDataAvailable(MP4HandleImp handle)
{
  mp4_u32 size;
  mp4_u32 type;


  /* Meta data is available if the input is in a file or if a complete file has been inputted as a stream*/

  if (handle->file)
    return 1;

  /* When reading from a stream, meta data is considered available if
     it is in the beginning of the stream and the entire MOOV atom has
     been received. FTYP atom is allowed before MOOV atom. */

  if (!handle->ftypRead)
  {
    if (peekData(handle, handle->buf, 8) < 0) /* 8 bytes are not available */
        return 0;

    size = u32endian(*((mp4_u32 *)handle->buf));
    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    if (type == ATOMTYPE_FTYP)
    {
      if (getBufferedBytes(handle) - handle->position < size) /* FTYP is not available */
        return 0;
      if ((handle->ftyp = (fileTypeAtom *)mp4malloc(sizeof(fileTypeAtom))) == NULL)
        return -100;
      if (readFTYP(handle, handle->ftyp) < 0)
        return -2;
    }
  }

  // Now the ftyp is read. No need to chedk MOOV presence for full files in the memory.
  if (handle->LastWriteDataCalled == MP4TRUE)
      return 1;

  if (handle->LastWriteDataCalled == MP4FALSE)
  {//Whole stream is not fed to the internal memory yet. 
   for (;;)
    {
      if (peekData(handle, handle->buf, 8) < 0)
        return 0;

      size = u32endian(*((mp4_u32 *)handle->buf));
      type = u32endian(*((mp4_u32 *)(handle->buf+4)));

      if (type == ATOMTYPE_MOOV)
        {
        if (getBufferedBytes(handle) - handle->absPosition >= size) /* Entire Moov is available */
            {
            return 1;        
            }
        else
            {
            return 0;
            }
        }

      if ((mp4_i32)size <= 0)
        return -1;

      handle->absPosition+=size;
    }
  }
  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 readFTYP(MP4HandleImp handle,
 *                    fileTypeAtom *ftyp)
 *
 * Description:
 *
 *   This function parses one FTYP atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   ftyp               FTYP pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readFTYP(MP4HandleImp handle, fileTypeAtom *ftyp)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 n = 0;
  mp4_i32 compatiblebrandsize = 0;


  if ((ftyp->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, ftyp->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (ftyp->atomhdr->type != ATOMTYPE_FTYP)
    return -1;

  if (ftyp->atomhdr->size < 16) // 8(header)+8bytes needed for major and minor brand
    return -1;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ftyp->majorBrand = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ftyp->minorVersion = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if (ftyp->atomhdr->size == (mp4_u32)totalbytesread)
    return totalbytesread;

  compatiblebrandsize = (mp4_i32)ftyp->atomhdr->size - totalbytesread;
  if ( compatiblebrandsize < 4 )   // at this point we must have at least 1 compatible brand
    return -1;
  if ( compatiblebrandsize > 20*4) // maximum of 20 compatible brands 4byte entries
    return -1;
  if ( compatiblebrandsize % 4 ) // must be able to divide by 4
    return -1;

  ftyp->compatibleBrands = (mp4_u32 *)mp4malloc( compatiblebrandsize );
  if (ftyp->compatibleBrands == NULL)
    return -1;

  while ((mp4_u32)totalbytesread < ftyp->atomhdr->size)
  {
    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;

    ftyp->compatibleBrands[n] = u32endian(*((mp4_u32 *)handle->buf));

    n++;
    totalbytesread += bytesread;
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMetaData(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function reads the meta data from the file/stream and stores
 *   the information in the atom structures available via handle.
 *
 * Parameters:
 *
 *   handle  MP4 library handle
 *
 * Return value:
 *
 *   Negative value   Error
 *   >= 0             Success. Value tells the number of bytes read.
 *
 */
mp4_i32 readMetaData(MP4HandleImp handle)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if (handle->file)
  {
    mp4_u64 size;
    mp4_u32 type;


    if (seekFileAbs(handle, 0) < 0)
      return -1;

    /* Seek to the start of FTYP atom */

    for (;;)
    {
      if (readBoxHeader(handle, &size, &type) <0)
        return -1;

      if (type == ATOMTYPE_FTYP)
      {
        /* Read FTYP */

        if ((handle->ftyp = (fileTypeAtom *)mp4malloc(sizeof(fileTypeAtom))) == NULL)
          return -100;
        bytesread = readFTYP(handle, handle->ftyp);
        if (bytesread < 0)
          return -3;
        totalbytesread += bytesread;

        break;
      }

      if (size <= 0)
        return -1;

      if (seekFile(handle, size) != 0)
        return -1;
    }

    if (seekFileAbs(handle, 0) < 0)
      return -1;

    /* Seek to the start of MOOV atom */
    for (;;)
    {
      if (readBoxHeader(handle, &size, &type) <0)
        return -1;

      if (type == ATOMTYPE_MOOV)
        break;

      if (size <= 0)
        return -1;

      if (seekFile(handle, size) != 0)
        return -1;
    }
  }

  // If all data of a file is in the memory and the file does not have MOOV box right after FTYP, 
  // then we need to seek for the location of the MOOV first

  if (handle->LastWriteDataCalled == MP4TRUE)
  { 
    mp4_u32 size;
    mp4_u32 type;
    // Seek until the beginning of MOOV box.
    for(;;)
    {
        if (peekData(handle, handle->buf, 8) < 0)
            return -1;

        size = u32endian(*((mp4_u32 *)handle->buf));
        type = u32endian(*((mp4_u32 *)(handle->buf+4)));
    
        if (type == ATOMTYPE_MOOV)
          break;

        if ((mp4_i32)size <= 0)
          return -1;

        if (discardData(handle, size) < 0)
          return -1;
    }
  }

  if ((handle->moov = (movieAtom *)mp4malloc(sizeof(movieAtom))) == NULL)
    return -100;

  bytesread = readMoov(handle, handle->moov);
  if (bytesread < 0)
    return -3;
  totalbytesread += bytesread;

  if (handle->moov->trakAudio)
  {
    mp4_u32 audiotype;
	mp4_i32 errorAudio = 0;

	errorAudio = determineAudioType(handle, &audiotype);
    if ( errorAudio == 0 )
    {
	   	handle->type |= audiotype;    	
    
	    /* Move to the beginning of the 1st audio frame */
	    switch (advanceAudioSample(handle, handle->moov->trakAudio))
	    {
	    case -1:
	      return -1;
	    case -2:
	      handle->audioLast = MP4TRUE;
	      break;
	    default:
	      break;
	    }
	}
	else if (errorAudio == -2)
	{
		handle->type |= audiotype;	
	}
	else
	{
		return -1;
	}
  }

  if (handle->moov->trakVideo)
  {
    mp4_u32 videotype;
    mp4_i32 errorVideo = 0;

	errorVideo = determineVideoType(handle, &videotype);
    if ( errorVideo == 0 )
    {
	   	handle->type |= videotype;    	
    
    	/* Move to the beginning of the 1st video frame */
	    switch (advanceVideoFrame(handle, handle->moov->trakVideo))
	    {
	    case -1:
	      return -1;
	    case -2:
	       handle->videoLast = MP4TRUE;
	      break;
	    default:
	      break;
	    }
	}
	else if (errorVideo == -2)
	{
	   	handle->type |= videotype;	
	}
	else
	{
		return -1;
	}
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMoov(MP4HandleImp handle,
 *                    movieAtom *moov)
 *
 * Description:
 *
 *   This function parses one MOOV atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   moov               MOOV pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMoov(MP4HandleImp handle, movieAtom *moov)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((moov->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, moov->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (moov->atomhdr->type != ATOMTYPE_MOOV)
    return -1;

  while ((mp4_u32)totalbytesread < handle->moov->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_MVHD:

      if (moov->mvhd) /* MVHD has already been read, more than one is not allowed */
        return -1;

      if ((moov->mvhd = (movieHeaderAtom *)mp4malloc(sizeof(movieHeaderAtom))) == NULL)
        return -100;

      bytesread = readMVHD(handle, moov->mvhd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_IODS:

      if (moov->iods) /* IODS has already been read, more than one is not allowed */
        return -1;

      if ((moov->iods = (objectDescriptorAtom *)mp4malloc(sizeof(objectDescriptorAtom))) == NULL)
        return -100;

      bytesread = readIODS(handle, moov->iods);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_TRAK:

      {
        trackAtom *ta;

        if ((ta = (trackAtom *)mp4malloc(sizeof(trackAtom))) == NULL)
          return -100;

        bytesread = readTRAK(handle, ta);
        if (bytesread < 0)
        {
          if (freeTRAK(ta) < 0)
            return -1;
          return -1;
        }
        totalbytesread += bytesread;

        if (!ta->mdia)
        {
          if (freeTRAK(ta) < 0)
            return -1;
          return -1;
        }
        if (!ta->mdia->hdlr)
        {
          if (freeTRAK(ta) < 0)
            return -1;
          return -1;
        }

        if (ta->mdia->hdlr->handlerType != HANDLERTYPE_VIDE &&
            ta->mdia->hdlr->handlerType != HANDLERTYPE_SOUN) /* Track is neither video nor audio */
        {
          /* Do nothing with the unknown track */
          if (freeTRAK(ta) < 0)
            return -1;
          break;
        }

        if (ta->mdia->hdlr->handlerType == HANDLERTYPE_VIDE)
        {
	        if (moov->trakVideo) /* Video Track already read */
	        {
	          if (freeTRAK(ta) < 0)
	            return -1;
	        }
	        else
	        {
	          moov->trakVideo = ta;
	        }
        }
		else if (ta->mdia->hdlr->handlerType == HANDLERTYPE_SOUN)
        {
	        if (moov->trakAudio) /* Audio Track already read */
	        {
	          if (freeTRAK(ta) < 0)
	            return -1;
	        }
	        else
	        {
	          moov->trakAudio = ta;
	        }
        }
        else
        {
        }
        break;
      }

    case ATOMTYPE_UDTA:

      {
      if (moov->udta) /* UDTA has already been read */
          {
          bytesread = readUnknown(handle);
          if (bytesread < 0)
            return -1;
          totalbytesread += bytesread;
          break;
          }


      if ((moov->udta = (userDataAtom *)mp4malloc(sizeof(userDataAtom))) == NULL)
        return -100;

      bytesread = readUDTA(handle, moov->udta);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
      }

    default:

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readAtomheader(MP4HandleImp handle,
 *                          atomHeader *ah)
 *
 * Description:
 *
 *   This function reads an atom header and stores the information
 *   in ah.
 *
 * Parameters:
 *
 *   handle           MP4 library handle
 *   ah               atomHeader structure that is used to store the
 *                    information
 *
 * Return value:
 *
 *   Negative value   Error
 *   >= 0             Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readAtomHeader(MP4HandleImp handle, atomHeader *ah)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;

  //PRINT((_L("readAtomHeader")));
  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ah->size = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ah->type = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if (ah->size == 1)
  {
    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    ah->largeSize = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;
  }

  if (ah->type == ATOMTYPE_UUID)
  {
    bytesread = readData(handle, handle->buf, 16);
    if (bytesread < 0)
      return -1;
    mp4memcpy(ah->extendedType, handle->buf, 16);
    totalbytesread += bytesread;
  }

  //PRINT((_L("   size %u, size %Lu, type %c%c%c%c"), ah->size, ah->largeSize,((unsigned char *)(&ah->type))[3], ((unsigned char *)(&ah->type))[2], ((unsigned char *)(&ah->type))[1], ((unsigned char *)(&ah->type))[0]));
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readFullAtomHeader(MP4HandleImp handle,
 *                              atomHeader *ah)
 *
 * Description:
 *
 *   This function reads a full atom header and stores the information
 *   in ah.
 *
 * Parameters:
 *
 *   handle           MP4 library handle
 *   ah               atomHeader structure that is used to store the
 *                    information
 *
 * Return value:
 *
 *   Negative value   Error
 *   >= 0             Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readFullAtomHeader(MP4HandleImp handle, atomHeader *ah)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;

  //PRINT((_L("readFullAtomHeader")));

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ah->size = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ah->type = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  ah->version = *(handle->buf);
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 3);
  if (bytesread < 0)
    return -1;
  mp4memcpy(ah->flags, handle->buf, 3);
  totalbytesread += bytesread;

  //PRINT((_L("   size %u, size %Lu, type %c%c%c%c"), ah->size, ah->largeSize,((unsigned char *)(&ah->type))[3], ((unsigned char *)(&ah->type))[2], ((unsigned char *)(&ah->type))[1], ((unsigned char *)(&ah->type))[0]));
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMVHD(MP4HandleImp handle,
 *                    movieHeaderAtom *mvhd)
 *
 * Description:
 *
 *   This function parses one MVHD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mvhd               MVHD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMVHD(MP4HandleImp handle, movieHeaderAtom *mvhd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mvhd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, mvhd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mvhd->atomhdr->type != ATOMTYPE_MVHD)
    return -1;

  if (mvhd->atomhdr->version == 1) /* 64 bit */
  {
    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mvhd->creationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mvhd->modificationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mvhd->timeScale = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mvhd->duration64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;
  }
  else /* 32 bit */
  {
    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mvhd->creationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mvhd->modificationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mvhd->timeScale = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mvhd->duration = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;
  }

  bytesread = discardData(handle, 76);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  mvhd->nextTrackID = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readIODS(MP4HandleImp handle,
 *                    objectDescriptorAtom *iods)
 *
 * Description:
 *
 *   This function parses one IODS atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   iods               IODS pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readIODS(MP4HandleImp handle, objectDescriptorAtom *iods)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((iods->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, iods->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (iods->atomhdr->type != ATOMTYPE_IODS)
    return -1;

  bytesread = discardData(handle, iods->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readTRAK(MP4HandleImp handle,
 *                    trackAtom *trak)
 *
 * Description:
 *
 *   This function parses one TRAK atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   trak               TRAK pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readTRAK(MP4HandleImp handle, trackAtom *trak)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((trak->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, trak->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (trak->atomhdr->type != ATOMTYPE_TRAK)
    return -1;


  while ((mp4_u32)totalbytesread < trak->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_TKHD:

      if (trak->tkhd) /* MVHD has already been read, more than one is not allowed */
        return -1;

      if ((trak->tkhd = (trackHeaderAtom *)mp4malloc(sizeof(trackHeaderAtom))) == NULL)
        return -100;

      bytesread = readTKHD(handle, trak->tkhd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_TREF:

      if (trak->tref) /* TREF has already been read, more than one is not allowed */
        return -1;

      if ((trak->tref = (trackReferenceAtom *)mp4malloc(sizeof(trackReferenceAtom))) == NULL)
        return -100;

      bytesread = readTREF(handle, trak->tref);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_EDTS:

      if (trak->edts) /* EDTS has already been read, more than one is not allowed */
        return -1;

      if ((trak->edts = (editListContainerAtom *)mp4malloc(sizeof(editListContainerAtom))) == NULL)
        return -100;

      bytesread = readEDTS(handle, trak->edts);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_MDIA:

      if (trak->mdia) /* MDIA has already been read, more than one is not allowed */
        return -1;

      if ((trak->mdia = (mediaAtom *)mp4malloc(sizeof(mediaAtom))) == NULL)
        return -100;

      bytesread = readMDIA(handle, trak->mdia);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_UDTA:
      {
      if (trak->udta) /* UDTA has already been read */
          {
          bytesread = readUnknown(handle);
          if (bytesread < 0)
            return -1;
          totalbytesread += bytesread;
          break;
          }

      if ((trak->udta = (userDataAtom *)mp4malloc(sizeof(userDataAtom))) == NULL)
        return -100;

      bytesread = readUDTA(handle, trak->udta);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
      }

    default:

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readUnknown(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function reads one atom of unknown type. Atom contents are
 *   discarded.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readUnknown(MP4HandleImp handle)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 size;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  size = u32endian(*((mp4_u32 *)handle->buf));
  if ( size < 4 )
  {
  	return -1;
  }

  if ( handle->file )
  {
  	if ( seekFile(handle, size - totalbytesread) < 0 )
  	{
  	   return -1;	
  	}
  	else
  	{
  	   return size;	
  	}
  }
  else
  {
  	bytesread = discardData(handle, size - totalbytesread);
	  if (bytesread < 0)
    	return -1;
  	totalbytesread += bytesread;
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readTKHD(MP4HandleImp handle,
 *                    trackHeaderAtom *tkhd)
 *
 * Description:
 *
 *   This function parses one TKHD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   tkhd               TKHD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readTKHD(MP4HandleImp handle, trackHeaderAtom *tkhd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((tkhd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, tkhd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (tkhd->atomhdr->type != ATOMTYPE_TKHD)
    return -1;

  if (tkhd->atomhdr->version == 1) /* 64 bit */
  {
    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    tkhd->creationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    tkhd->modificationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->trackID = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->reserved = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    tkhd->duration64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;
  }
  else /* 32 bit */
  {
    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->creationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->modificationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->trackID = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->reserved = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    tkhd->duration = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;
  }

  bytesread = discardData(handle, 52);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  tkhd->width = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  tkhd->height = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readTREF(MP4HandleImp handle,
 *                    trackReferenceAtom *tref)
 *
 * Description:
 *
 *   This function parses one TREF atom and discards the contents.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   tref               TREF pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readTREF(MP4HandleImp handle, trackReferenceAtom *tref)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((tref->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, tref->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (tref->atomhdr->type != ATOMTYPE_TREF)
    return -1;

  bytesread = discardData(handle, tref->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readEDTS(MP4HandleImp handle,
 *                    editListContainerAtom *edts)
 *
 * Description:
 *
 *   This function parses one EDTS atom and discards the contents.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   edts               EDTS pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readEDTS(MP4HandleImp handle, editListContainerAtom *edts)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((edts->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, edts->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (edts->atomhdr->type != ATOMTYPE_EDTS)
    return -1;

  bytesread = discardData(handle, edts->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMDIA(MP4HandleImp handle,
 *                    mediaAtom *mdia)
 *
 * Description:
 *
 *   This function parses one MDIA atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mdia               MDIA pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMDIA(MP4HandleImp handle, mediaAtom *mdia)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mdia->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, mdia->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mdia->atomhdr->type != ATOMTYPE_MDIA)
    return -1;


  while ((mp4_u32)totalbytesread < mdia->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_MDHD:

      if (mdia->mdhd) /* MDHD has already been read, more than one is not allowed */
        return -1;

      if ((mdia->mdhd = (mediaHeaderAtom *)mp4malloc(sizeof(mediaHeaderAtom))) == NULL)
        return -100;

      bytesread = readMDHD(handle, mdia->mdhd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_HDLR:

      if (mdia->hdlr) /* HDLR has already been read, more than one is not allowed */
        return -1;

      if ((mdia->hdlr = (handlerAtom *)mp4malloc(sizeof(handlerAtom))) == NULL)
        return -100;

      bytesread = readHDLR(handle, mdia->hdlr);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_MINF:

      if (mdia->minf) /* MINF has already been read, more than one is not allowed */
        return -1;

      if ((mdia->minf = (mediaInformationAtom *)mp4malloc(sizeof(mediaInformationAtom))) == NULL)
        return -100;

      bytesread = readMINF(handle, mdia->minf);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    default:

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMDHD(MP4HandleImp handle,
 *                    mediaHeaderAtom *mdhd)
 *
 * Description:
 *
 *   This function parses one MDHD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mdhd               MDHD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMDHD(MP4HandleImp handle, mediaHeaderAtom *mdhd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mdhd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, mdhd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mdhd->atomhdr->type != ATOMTYPE_MDHD)
    return -1;


  if (mdhd->atomhdr->version == 1) /* 64 bit */
  {
    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mdhd->creationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mdhd->modificationTime64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mdhd->timeScale = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 8);
    if (bytesread < 0)
      return -1;
    mdhd->duration64 = u64endian(*((mp4_u64 *)handle->buf));
    totalbytesread += bytesread;
  }
  else /* 32 bit */
  {
    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mdhd->creationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mdhd->modificationTime = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mdhd->timeScale = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;

    bytesread = readData(handle, handle->buf, 4);
    if (bytesread < 0)
      return -1;
    mdhd->duration = u32endian(*((mp4_u32 *)handle->buf));
    totalbytesread += bytesread;
  }

  bytesread = discardData(handle, 4);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readHDLR(MP4HandleImp handle,
 *                    handlerAtom *hdlr)
 *
 * Description:
 *
 *   This function parses one HDLR atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   hdlr               HDLR pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readHDLR(MP4HandleImp handle, handlerAtom *hdlr)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((hdlr->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, hdlr->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (hdlr->atomhdr->type != ATOMTYPE_HDLR)
    return -1;


  bytesread = discardData(handle, 4);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  hdlr->handlerType = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, hdlr->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMINF(MP4HandleImp handle,
 *                    mediaInformationAtom *minf)
 *
 * Description:
 *
 *   This function parses one MINF atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   minf               MINF pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMINF(MP4HandleImp handle, mediaInformationAtom *minf)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((minf->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, minf->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (minf->atomhdr->type != ATOMTYPE_MINF)
    return -1;


  while ((mp4_u32)totalbytesread < minf->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_VMHD:

      if (minf->vmhd || minf->smhd) /* VMHD or SMHD has already been read, more than one is not allowed */
        return -1;

      if ((minf->vmhd = (videoMediaHeaderAtom *)mp4malloc(sizeof(videoMediaHeaderAtom))) == NULL)
        return -100;

      bytesread = readVMHD(handle, minf->vmhd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_SMHD:

      if (minf->smhd || minf->vmhd) /* SMHD or VMHD has already been read, more than one is not allowed */
        return -1;

      if ((minf->smhd = (soundMediaHeaderAtom *)mp4malloc(sizeof(soundMediaHeaderAtom))) == NULL)
        return -100;

      bytesread = readSMHD(handle, minf->smhd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_DINF:

      if (minf->dinf) /* DINF has already been read, more than one is not allowed */
        return -1;

      if ((minf->dinf = (dataInformationAtom *)mp4malloc(sizeof(dataInformationAtom))) == NULL)
        return -100;

      bytesread = readDINF(handle, minf->dinf);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STBL:

      if (minf->stbl) /* STBL has already been read, more than one is not allowed */
        return -1;

      if ((minf->stbl = (sampleTableAtom *)mp4malloc(sizeof(sampleTableAtom))) == NULL)
        return -100;

      bytesread = readSTBL(handle, minf->stbl);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    default:

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readVMHD(MP4HandleImp handle,
 *                    videoMediaHeaderAtom *vmhd)
 *
 * Description:
 *
 *   This function parses one VMHD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   vmhd               VMHD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readVMHD(MP4HandleImp handle, videoMediaHeaderAtom *vmhd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((vmhd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, vmhd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (vmhd->atomhdr->type != ATOMTYPE_VMHD)
    return -1;


  bytesread = discardData(handle, vmhd->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSMHD(MP4HandleImp handle,
 *                    soundMediaHeaderAtom *smhd)
 *
 * Description:
 *
 *   This function parses one SMHD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   smhd               SMHD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSMHD(MP4HandleImp handle, soundMediaHeaderAtom *smhd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((smhd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, smhd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (smhd->atomhdr->type != ATOMTYPE_SMHD)
    return -1;


  bytesread = discardData(handle, smhd->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readDINF(MP4HandleImp handle,
 *                    dataInformationAtom *dinf)
 *
 * Description:
 *
 *   This function parses one DINF atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   dinf               DINF pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readDINF(MP4HandleImp handle, dataInformationAtom *dinf)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((dinf->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, dinf->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (dinf->atomhdr->type != ATOMTYPE_DINF)
    return -1;


  while ((mp4_u32)totalbytesread < dinf->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_DREF:

      if (dinf->dref) /* DINF has already been read, more than one is not allowed */
        return -1;

      if ((dinf->dref = (dataReferenceAtom *)mp4malloc(sizeof(dataReferenceAtom))) == NULL)
        return -100;

      bytesread = readDREF(handle, dinf->dref);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    default:

      return -1;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readDREF(MP4HandleImp handle,
 *                    dataReferenceAtom *dref)
 *
 * Description:
 *
 *   This function parses one DREF atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   dref               DREF pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readDREF(MP4HandleImp handle, dataReferenceAtom *dref)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((dref->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, dref->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (dref->atomhdr->type != ATOMTYPE_DREF)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  dref->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if (dref->entryCount != 1)
    return -1;

  while ((mp4_u32)totalbytesread < dref->atomhdr->size)
  {
    bytesread = readUnknown(handle);
    if (bytesread < 0)
      return -1;
    totalbytesread += bytesread;
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readURL(MP4HandleImp handle,
 *                   dataEntryURLAtom *url)
 *
 * Description:
 *
 *   This function parses one URL atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   url                URL pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readURL(MP4HandleImp handle, dataEntryURLAtom *url)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((url->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, url->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (url->atomhdr->type != ATOMTYPE_URL)
    return -1;


  if (!(url->atomhdr->flags[0] == 0x00 &&
        url->atomhdr->flags[1] == 0x00 &&
        url->atomhdr->flags[2] == 0x01))
    return -1;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readURN(MP4HandleImp handle,
 *                   dataEntryURNAtom *urn)
 *
 * Description:
 *
 *   This function parses one URN atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   urn                URN pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readURN(MP4HandleImp handle, dataEntryURNAtom *urn)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((urn->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, urn->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (urn->atomhdr->type != ATOMTYPE_URN)
    return -1;


  if (!(urn->atomhdr->flags[0] == 0x00 &&
        urn->atomhdr->flags[1] == 0x00 &&
        urn->atomhdr->flags[2] == 0x01))
    return -1;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTBL(MP4HandleImp handle,
 *                    sampleTableAtom *stbl)
 *
 * Description:
 *
 *   This function parses one STBL atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stbl               STBL pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTBL(MP4HandleImp handle, sampleTableAtom *stbl)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((stbl->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, stbl->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stbl->atomhdr->type != ATOMTYPE_STBL)
    return -1;


  while ((mp4_u32)totalbytesread < stbl->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_STTS:

      if (stbl->stts) /* STTS has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stts = (timeToSampleAtom *)mp4malloc(sizeof(timeToSampleAtom))) == NULL)
        return -100;

      bytesread = readSTTS(handle, stbl->stts);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_CTTS:

      if (stbl->ctts) /* CTTS has already been read, more than one is not allowed */
        return -1;

      if ((stbl->ctts = (compositionTimeToSampleAtom *)mp4malloc(sizeof(compositionTimeToSampleAtom))) == NULL)
        return -100;

      bytesread = readCTTS(handle, stbl->ctts);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STSS:

      if (stbl->stss) /* STSS has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stss = (syncSampleAtom *)mp4malloc(sizeof(syncSampleAtom))) == NULL)
        return -100;

      bytesread = readSTSS(handle, stbl->stss);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STSD:

      if (stbl->stsd) /* STSD has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stsd = (sampleDescriptionAtom *)mp4malloc(sizeof(sampleDescriptionAtom))) == NULL)
        return -100;

      bytesread = readSTSD(handle, stbl->stsd);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STSZ:

      if (stbl->stsz) /* STSZ or STZ2 has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stsz = (sampleSizeAtom *)mp4malloc(sizeof(sampleSizeAtom))) == NULL)
        return -100;

      bytesread = readSTSZ(handle, stbl->stsz);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STZ2:

      if (stbl->stsz) /* STSZ or STZ2 has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stsz = (sampleSizeAtom *)mp4malloc(sizeof(sampleSizeAtom))) == NULL)
        return -100;

      bytesread = readSTZ2(handle, stbl->stsz);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STSC:

      if (stbl->stsc) /* STSC has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stsc = (sampleToChunkAtom *)mp4malloc(sizeof(sampleToChunkAtom))) == NULL)
        return -100;

      bytesread = readSTSC(handle, stbl->stsc);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_STCO:

      if (stbl->stco) /* STCO or CO64 has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stco = (chunkOffsetAtom *)mp4malloc(sizeof(chunkOffsetAtom))) == NULL)
        return -100;
      
      stbl->is32BitOffsets = ETrue;
      bytesread = readSTCO(handle, stbl->stco);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_CO64:

      if (stbl->stco64) /* STCO or CO64 has already been read, more than one is not allowed */
        return -1;

      if ((stbl->stco64 = (chunkOffset64Atom *)mp4malloc(sizeof(chunkOffset64Atom))) == NULL)
        return -100;
      
      stbl->is32BitOffsets = EFalse;
      bytesread = readCO64(handle, stbl->stco64);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    case ATOMTYPE_SDTP:
      if (stbl->sdtp) /* SDTP has already been read, more than one is not allowed */
        return -1;
      
      if ((stbl->sdtp = (sampleDependencyAtom *)mp4malloc(sizeof(sampleDependencyAtom))) == NULL)
        return -100;

      if (!stbl->stsz)
    	  {
    	  return -1;
    	  }

	  // sample_count of SDTP is taken from the sample_count in the Sample Size Box ('stsz') or
	  // Compact Sample Size Box (‘stz2’). 
	  bytesread = readSDTP(handle, stbl->sdtp, stbl->stsz->sampleCount);
    	  
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;
      break;

    default: /* Other atoms are not needed */

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTTS(MP4HandleImp handle,
 *                    timeToSampleAtom *stts)
 *
 * Description:
 *
 *   This function parses one STTS atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stts               STTS pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTTS(MP4HandleImp handle, timeToSampleAtom *stts)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i;


  if ((stts->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stts->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stts->atomhdr->type != ATOMTYPE_STTS)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stts->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;
  
  if ( stts->entryCount )
  {
	  stts->sampleCount = (mp4_u32 *)mp4malloc(stts->entryCount * sizeof(mp4_u32));
	  if (stts->sampleCount == NULL)
	    return -1;
	  stts->sampleDelta = (mp4_i32 *)mp4malloc(stts->entryCount * sizeof(mp4_i32));
	  if (stts->sampleDelta == NULL)
	    return -1;

	  for (i = 0; i < stts->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 8);
	    if (bytesread < 0)
	      return -1;

	    stts->sampleCount[i] = u32endian(*((mp4_u32 *)handle->buf));
	    stts->sampleDelta[i] = i32endian(*((mp4_i32 *)(handle->buf+4)));

	    totalbytesread += bytesread;
	  }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readCTTS(MP4HandleImp handle,
 *                    compositionTimeToSampleAtom *ctts)
 *
 * Description:
 *
 *   This function parses one CTTS atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   ctts               CTTS pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readCTTS(MP4HandleImp handle, compositionTimeToSampleAtom *ctts)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i;


  if ((ctts->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, ctts->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (ctts->atomhdr->type != ATOMTYPE_CTTS)
    return -1;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  ctts->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if ( ctts->entryCount )
  {
	  ctts->sampleCount = (mp4_u32 *)mp4malloc(ctts->entryCount * sizeof(mp4_u32));
	  if (ctts->sampleCount == NULL)
	    return -1;
	  ctts->sampleOffset = (mp4_u32 *)mp4malloc(ctts->entryCount * sizeof(mp4_u32));
	  if (ctts->sampleOffset == NULL)
	    return -1;

	  for (i = 0; i < ctts->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 8);
	    if (bytesread < 0)
	      return -1;

	    ctts->sampleCount[i] = u32endian(*((mp4_u32 *)handle->buf));
	    ctts->sampleOffset[i] = u32endian(*((mp4_u32 *)(handle->buf+4)));

	    totalbytesread += bytesread;
	  }
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTSS(MP4HandleImp handle,
 *                    syncSampleAtom *stss)
 *
 * Description:
 *
 *   This function parses one STSS atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stss               STSS pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTSS(MP4HandleImp handle, syncSampleAtom *stss)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i;


  if ((stss->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stss->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stss->atomhdr->type != ATOMTYPE_STSS)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stss->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if ( stss->entryCount )
  {
	  stss->sampleNumber = (mp4_u32 *)mp4malloc(stss->entryCount * sizeof(mp4_u32));
	  if (stss->sampleNumber == NULL)
	    return -1;

	  for (i = 0; i < stss->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 4);
	    if (bytesread < 0)
	      return -1;

	    stss->sampleNumber[i] = u32endian(*((mp4_u32 *)handle->buf));

	    totalbytesread += bytesread;
	  }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTSD(MP4HandleImp handle,
 *                    sampleDescriptionAtom *stsd)
 *
 * Description:
 *
 *   This function parses one STSD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stsd               STSD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTSD(MP4HandleImp handle, sampleDescriptionAtom *stsd)
	{
	mp4_i32 bytesread;
	mp4_i32 totalbytesread = 0;
	mp4_u32 totalsampleentriesread = 0;
	mp4_u32 unknownsampleentriesread = 0;
	mp4_bool skipentries = 0;

	if ((stsd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
        {
        // for memory cleanup set entrycount to allocated num of entries.
        stsd->entryCount = totalsampleentriesread;
	    return -100;
        }

	bytesread = readFullAtomHeader(handle, stsd->atomhdr);
	if (bytesread < 0)
	    {
	    // for memory cleanup set entrycount to allocated num of entries.
	    stsd->entryCount = totalsampleentriesread;
	    return -1;
	    }
	totalbytesread += bytesread;

	if (stsd->atomhdr->type != ATOMTYPE_STSD)
	    {
	    // for memory cleanup set entrycount to allocated num of entries.
	    stsd->entryCount = totalsampleentriesread;
	    return -1;
	    }

	bytesread = readData(handle, handle->buf, 4);
	if (bytesread < 0)
	    {
	    // for memory cleanup set entrycount to allocated num of entries.
	    stsd->entryCount = totalsampleentriesread;
	    return -1;
	    }
	stsd->entryCount = u32endian(*((mp4_u32 *)handle->buf));
	totalbytesread += bytesread;

	mp4_u32 type;
	while ((mp4_u32)totalbytesread < stsd->atomhdr->size)
		{	
		// if the number of entries read already surpasses the number of entries specified 
		// within the STSD atom, the file is corrupted.
		if ((totalsampleentriesread + unknownsampleentriesread) >= stsd->entryCount)
			{
			// for memory cleanup set entrycount to allocated num of entries.
			stsd->entryCount = totalsampleentriesread;
  	  		return -1;    	    	
			}

		// read the next sample type
		if (peekData(handle, handle->buf, 8) < 0)
	        {
	        // for memory cleanup set entrycount to allocated num of entries.
	        stsd->entryCount = totalsampleentriesread;
		    return -1;
	        }
		type = u32endian(*((mp4_u32 *)(handle->buf+4)));
    
		// if the max sample entiries supported by the library has been reached 
		if ((stsd->entryCount > STSDMAXSAMPLEENTRYCOUNT) && (totalsampleentriesread == STSDMAXSAMPLEENTRYCOUNT))
			{
			// skip reading the rest of the entries to make sure no more than max count of sample entries 
			// will be processed, so that cleanup will always work. 
			type = 0;
			skipentries = 1;
			}

		switch (type)
			{
			case ATOMTYPE_MP4V:
				{
				if (stsd->mp4v[totalsampleentriesread]) /* MP4V[totalsampleentriesread] has already been read, more than one is not allowed */ 
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
		
				if ((stsd->mp4v[totalsampleentriesread] = (visualSampleEntry *)mp4malloc(sizeof(visualSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }
		
				bytesread = readMP4V(handle, stsd->mp4v[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				break;
				}

			case ATOMTYPE_MP4A:
				{	
				if (stsd->mp4a[totalsampleentriesread]) /* MP4A[totalsampleentriesread] has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }

				if ((stsd->mp4a[totalsampleentriesread] = (audioSampleEntry *)mp4malloc(sizeof(audioSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readMP4A(handle, stsd->mp4a[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				break;
				}

			case ATOMTYPE_MP4S:
				{
				if (stsd->mp4s[totalsampleentriesread]) /* MP4S has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }

				if ((stsd->mp4s[totalsampleentriesread] = (mpegSampleEntry *)mp4malloc(sizeof(mpegSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readMP4S(handle, stsd->mp4s[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			case ATOMTYPE_S263:
				{
				if (stsd->s263[totalsampleentriesread]) /* MP4S has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }

				if ((stsd->s263[totalsampleentriesread] = (h263SampleEntry *)mp4malloc(sizeof(h263SampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readS263(handle, stsd->s263[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			case ATOMTYPE_SAMR:
				{
				if (stsd->samr[totalsampleentriesread]) /* SAMR has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }

				if ((stsd->samr[totalsampleentriesread] = (amrSampleEntry *)mp4malloc(sizeof(amrSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readSAMR(handle, stsd->samr[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			case ATOMTYPE_SAWB:
				{
				if (stsd->sawb[totalsampleentriesread]) /* SAWB has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }

				if ((stsd->sawb[totalsampleentriesread] = (amrSampleEntry *)mp4malloc(sizeof(amrSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readSAWB(handle, stsd->sawb[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			case ATOMTYPE_AVC1:
				{
				if (stsd->avc1[totalsampleentriesread]) /* AVC1 has already been read, more than one is not allowed */
					{
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
					}
				if ((stsd->avc1[totalsampleentriesread] = (avcSampleEntry *)mp4malloc(sizeof(avcSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readAVC1(handle, stsd->avc1[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
					{
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			case ATOMTYPE_SQCP:
				{
				if (stsd->sqcp[totalsampleentriesread]) /* SQCP has already been read, more than one is not allowed */
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				if ((stsd->sqcp[totalsampleentriesread] = (qcelpSampleEntry *)mp4malloc(sizeof(qcelpSampleEntry))) == NULL)
			        {
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -100;
			        }

				bytesread = readSQCP(handle, stsd->sqcp[totalsampleentriesread]);
				totalsampleentriesread++;
				if (bytesread < 0)
					{
			        // for memory cleanup set entrycount to allocated num of entries.
			        stsd->entryCount = totalsampleentriesread;
				    return -1;
			        }
				totalbytesread += bytesread;
				}
				break;

			default: /* Other atoms are not needed */
				// no need to increment totalsampleentriesread as no memory is allocated for unsupported 
				// or unrecognized sample types.  Alternatively, increment the count of unknown samples.   
				// This is for ensure if a non-audio/video track can properly be parsed without being 
				// recongized as an invalid format file.
				unknownsampleentriesread++;
				bytesread = readUnknown(handle);
				if (bytesread < 0)
		    		{
		    		// for memory cleanup set entrycount to allocated num of entries.
		    		stsd->entryCount = totalsampleentriesread;
		    		return -1;
		    		}
				totalbytesread += bytesread;
				break;
			}
		}

	// if the STSD atom's entry count is NOT the same as the number (supported & unsupported) entries parsed,
	// the atom is likely a corrupted one. 
	if ((totalsampleentriesread + unknownsampleentriesread) != stsd->entryCount)
		{
		// for memory cleanup set entrycount to allocated num of entries.
		stsd->entryCount = totalsampleentriesread;
		return -1;
		}
	else 
		{
		// if the STSD atom's entry count is the same as the number of (supported & unsupported) entries 
		// parsed, check if some entries are skipped because the max sample entry count has been reached  
		if (skipentries) 
			{
			// if STSDMAXSAMPLEENTRYCOUNT was reached edit entrycount to make sure cleanup works.
			stsd->entryCount = STSDMAXSAMPLEENTRYCOUNT;
			}
		else if (unknownsampleentriesread > 0)
			{
			// unknown (unsupported) sample entries present, set the STSD entry count to the actual 
			// number of supported sample entries detected
			stsd->entryCount = totalsampleentriesread;
			}
		}
  
	return totalbytesread;
	}


/*
 * Function:
 *
 *   mp4_i32 readSTSZ(MP4HandleImp handle,
 *                    sampleSizeAtom *stsz)
 *
 * Description:
 *
 *   This function parses one STSZ atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stsz               STSZ pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTSZ(MP4HandleImp handle, sampleSizeAtom *stsz)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((stsz->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stsz->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stsz->atomhdr->type != ATOMTYPE_STSZ)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stsz->sampleSize = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stsz->sampleCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  // zero size samplesize means samples have different sizes, and those sizes are stored in sampleSizeEntries.
  if ((stsz->sampleCount) && (stsz->sampleSize == 0))
  {
    mp4_u32 i;

    // check validity of stsz->sampleCount before allocating entrysize table.
    if ( handle->moov->mvhd )
        {
        if ( handle->moov->mvhd->timeScale > 0 )
            {
            TUint magicNumberMaxSamplesInSec = MAXSAMPLESPERSECOND;
            TUint maxSampleCount;

            if ( handle->moov->mvhd->atomhdr->version == 1 ) // 64bit duration
                {
                maxSampleCount = I64INT(handle->moov->mvhd->duration64 / TUint(handle->moov->mvhd->timeScale) + 1) * magicNumberMaxSamplesInSec;
                }
            else    // 32bit duration
                {
                maxSampleCount = TUint((TUint( handle->moov->mvhd->duration  ) / TUint(handle->moov->mvhd->timeScale) + 1)) * magicNumberMaxSamplesInSec;
                }

            if ( maxSampleCount < stsz->sampleCount )
                {
                // corrupted 
                return -1;
                }
            }
        }

    // allocate stsz->entrySize table
    stsz->entrySize = (mp4_u32 *)mp4malloc(stsz->sampleCount * sizeof(mp4_u32));
    if (stsz->entrySize == NULL)
      return -1;

    for (i = 0; i < stsz->sampleCount; i++)
    {
      bytesread = readData(handle, handle->buf, 4);
      if (bytesread < 0)
        return -1;

      stsz->entrySize[i] = u32endian(*((mp4_u32 *)handle->buf));

      totalbytesread += bytesread;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTZ2(MP4HandleImp handle,
 *                    sampleSizeAtom *stsz)
 *
 * Description:
 *
 *   This function parses one STZ2 atom.
 *
 *   The result is placed in STSZ structure.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stsz               STSZ pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTZ2(MP4HandleImp handle, sampleSizeAtom *stsz)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u8  fieldsize;


  if ((stsz->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stsz->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stsz->atomhdr->type != ATOMTYPE_STZ2)
    return -1;


  bytesread = discardData(handle, 3);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  fieldsize = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stsz->sampleCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  switch (fieldsize)
  {
  case 4: /* Two entries in each byte */

    {
      mp4_u32 i;

      stsz->entrySize = (mp4_u32 *)mp4malloc(stsz->sampleCount * sizeof(mp4_u32));
      if (stsz->entrySize == NULL)
        return -1;

      for (i = 0; i < (stsz->sampleCount + 1) / 2; i++)
      {
        bytesread = readData(handle, handle->buf, 1);
        if (bytesread < 0)
          return -1;

        totalbytesread += bytesread;

        stsz->entrySize[i * 2] = (mp4_u32)(handle->buf[0] >> 4);

        if (stsz->sampleCount % 2 == 0) /* Even number of samples */
        {
          stsz->entrySize[i * 2 + 1] = (mp4_u32)(handle->buf[0] & 0x0f);
          continue;
        }

        /* This condition is needed to avoid writing after the table */

        if (i == (stsz->sampleCount + 1) / 2 - 1) /* Last sample */
        {
        }
        else
          stsz->entrySize[i * 2 + 1] = (mp4_u32)(handle->buf[0] & 0x0f);
      }
    }

    break;

  case 8: /* One entry for each byte */

    {
      mp4_u32 i;

      stsz->entrySize = (mp4_u32 *)mp4malloc(stsz->sampleCount * sizeof(mp4_u32));
      if (stsz->entrySize == NULL)
        return -1;

      for (i = 0; i < stsz->sampleCount; i++)
      {
        bytesread = readData(handle, handle->buf, 1);
        if (bytesread < 0)
          return -1;

        stsz->entrySize[i] = (mp4_u32)handle->buf[0];

        totalbytesread += bytesread;
      }
    }

    break;

  case 16: /* Each entry in 2 bytes */

    {
      mp4_u32 i;

      stsz->entrySize = (mp4_u32 *)mp4malloc(stsz->sampleCount * sizeof(mp4_u32));
      if (stsz->entrySize == NULL)
        return -1;

      for (i = 0; i < stsz->sampleCount; i++)
      {
        bytesread = readData(handle, handle->buf, 2);
        if (bytesread < 0)
          return -1;

        stsz->entrySize[i] = (mp4_u32)u16endian(*((mp4_u16 *)handle->buf));

        totalbytesread += bytesread;
      }
    }

    break;

  default: /* Illegal fieldsize */

    return -1;
  }


  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTSC(MP4HandleImp handle,
 *                    sampleToChunkAtom *stsc)
 *
 * Description:
 *
 *   This function parses one STSC atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stsc               STSC pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTSC(MP4HandleImp handle, sampleToChunkAtom *stsc)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i;


  if ((stsc->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stsc->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stsc->atomhdr->type != ATOMTYPE_STSC)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stsc->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if (stsc->entryCount)
  {
	  stsc->firstChunk = (mp4_u32 *)mp4malloc(stsc->entryCount * sizeof(mp4_u32));
	  if (stsc->firstChunk == NULL)
	    return -1;
	  stsc->samplesPerChunk = (mp4_u32 *)mp4malloc(stsc->entryCount * sizeof(mp4_u32));
	  if (stsc->samplesPerChunk == NULL)
	    return -1;
	  stsc->sampleDescriptionIndex = (mp4_u32 *)mp4malloc(stsc->entryCount * sizeof(mp4_u32));
	  if (stsc->sampleDescriptionIndex == NULL)
	  {
		return -1;	  	
	  }

	  for (i = 0; i < stsc->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 12);
	    if (bytesread < 0)
	      return -1;

	    stsc->firstChunk[i] = u32endian(*((mp4_u32 *)handle->buf));
	    stsc->samplesPerChunk[i] = u32endian(*((mp4_u32 *)(handle->buf+4)));
	    stsc->sampleDescriptionIndex[i] = u32endian(*((mp4_u32 *)(handle->buf+8)));
	    if ( stsc->sampleDescriptionIndex[i] > stsc->entryCount)
	    {
	    	return -1;
	    }

	    totalbytesread += bytesread;
	  }
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSTCO(MP4HandleImp handle,
 *                    chunkOffsetAtom *stco)
 *
 * Description:
 *
 *   This function parses one STCO atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stco               STCO pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSTCO(MP4HandleImp handle, chunkOffsetAtom *stco)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32  i;


  if ((stco->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stco->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stco->atomhdr->type != ATOMTYPE_STCO)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stco->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if (stco->entryCount)
  {
	  // validate stco->entryCount before allocating chunkOffsetTable
	  if ( handle->moov->mvhd )
	    {
	    if ( handle->moov->mvhd->timeScale > 0 )
	        {
	        TUint magicNumberMaxSamplesInSec = MAXSAMPLESPERSECOND;
	        TUint maxSampleCount;

	        if ( handle->moov->mvhd->atomhdr->version == 1 ) // 64bit duration
	            {
	            maxSampleCount = I64INT(handle->moov->mvhd->duration64 / TUint(handle->moov->mvhd->timeScale) + 1) * magicNumberMaxSamplesInSec;
	            }
	        else    // 32bit duration
	            {
	            maxSampleCount = TUint((TUint( handle->moov->mvhd->duration  ) / TUint(handle->moov->mvhd->timeScale) + 1)) * magicNumberMaxSamplesInSec;
	            }

	        if ( maxSampleCount < stco->entryCount )
	            {
	            // corrupted 
	            return -1;
	            }
	        }
	    }

	  stco->chunkOffset = (mp4_u32 *)mp4malloc(stco->entryCount * sizeof(mp4_u32));
	  if (stco->chunkOffset == NULL)
	    return -1;

	  for (i = 0; i < stco->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 4);
	    if (bytesread < 0)
	      return -1;

	    stco->chunkOffset[i] = u32endian(*((mp4_u32 *)handle->buf));

	    totalbytesread += bytesread;
	  }
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readCO64(MP4HandleImp handle,
 *                    chunkOffset64Atom *stco64)
 *
 * Description:
 *
 *   This function parses one CO64 atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   stco64               CO64 pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readCO64(MP4HandleImp handle, chunkOffset64Atom *stco64)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i;


  if ((stco64->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, stco64->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (stco64->atomhdr->type != ATOMTYPE_CO64)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  stco64->entryCount = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  if ( stco64->entryCount )
  {
	  stco64->chunkOffset = (mp4_u64 *)mp4malloc(stco64->entryCount * sizeof(mp4_u64));
	  if (stco64->chunkOffset == NULL)
	    return -1;

	  for (i = 0; i < stco64->entryCount; i++)
	  {
	    bytesread = readData(handle, handle->buf, 8);
	    if (bytesread < 0)
	      return -1;
	    
	    stco64->chunkOffset[i] = u64endian(*((mp4_u64 *)(handle->buf)));

	    totalbytesread += bytesread;
	  }
  }
  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMP4V(MP4HandleImp handle,
 *                    visualSampleEntry *mp4v)
 *
 * Description:
 *
 *   This function parses one MP4V atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mp4v               MP4V pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMP4V(MP4HandleImp handle, visualSampleEntry *mp4v)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mp4v->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, mp4v->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mp4v->atomhdr->type != ATOMTYPE_MP4V)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4v->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4v->width = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4v->height = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 50);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((mp4v->esd = (ESDAtom *)mp4malloc(sizeof(ESDAtom))) == NULL)
    return -100;

  bytesread = readESD(handle, mp4v->esd);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < mp4v->atomhdr->size )
  	{
    bytesread = discardData(handle, mp4v->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}  

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMP4A(MP4HandleImp handle,
 *                    audioSampleEntry *mp4a)
 *
 * Description:
 *
 *   This function parses one MP4A atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mp4a               MP4A pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMP4A(MP4HandleImp handle, audioSampleEntry *mp4a)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mp4a->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, mp4a->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mp4a->atomhdr->type != ATOMTYPE_MP4A)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4a->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4a->timeScale = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 2);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((mp4a->esd = (ESDAtom *)mp4malloc(sizeof(ESDAtom))) == NULL)
    return -100;

  bytesread = readESD(handle, mp4a->esd);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < mp4a->atomhdr->size )
  	{
    bytesread = discardData(handle, mp4a->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readMP4S(MP4HandleImp handle,
 *                    mpegSampleEntry *mp4s)
 *
 * Description:
 *
 *   This function parses one MP4S atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   mp4s               MP4S pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readMP4S(MP4HandleImp handle, mpegSampleEntry *mp4s)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((mp4s->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, mp4s->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (mp4s->atomhdr->type != ATOMTYPE_MP4S)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  mp4s->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  if ((mp4s->esd = (ESDAtom *)mp4malloc(sizeof(ESDAtom))) == NULL)
    return -100;

  bytesread = readESD(handle, mp4s->esd);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < mp4s->atomhdr->size )
  	{
    bytesread = discardData(handle, mp4s->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}    

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readS263(MP4HandleImp handle,
 *                    h263SampleEntry *s263)
 *
 * Description:
 *
 *   This function parses one S263 atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   s263               S263 pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readS263(MP4HandleImp handle, h263SampleEntry *s263)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((s263->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, s263->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (s263->atomhdr->type != ATOMTYPE_S263)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  s263->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  s263->width = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  s263->height = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 50);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((s263->d263 = (h263SpecificAtom *)mp4malloc(sizeof(h263SpecificAtom))) == NULL)
    return -100;

  bytesread = readD263(handle, s263->d263);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < s263->atomhdr->size )
  	{
    bytesread = discardData(handle, s263->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}      

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSAMR(MP4HandleImp handle,
 *                    amrSampleEntry *samr)
 *
 * Description:
 *
 *   This function parses one SAMR atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   samr               SAMR pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSAMR(MP4HandleImp handle, amrSampleEntry *samr)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((samr->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, samr->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (samr->atomhdr->type != ATOMTYPE_SAMR)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  samr->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  samr->timeScale = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 2);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((samr->damr = (amrDecSpecStruc *)mp4malloc(sizeof(amrDecSpecStruc))) == NULL)
    return -100;

  bytesread = readDAMR(handle, samr->damr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < samr->atomhdr->size )
  	{
    bytesread = discardData(handle, samr->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}      

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readSAWB(MP4HandleImp handle,
 *                    amrSampleEntry *sawb)
 *
 * Description:
 *
 *   This function parses one SAWB atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   sawb               SAWB pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSAWB(MP4HandleImp handle, amrSampleEntry *sawb)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((sawb->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, sawb->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (sawb->atomhdr->type != ATOMTYPE_SAWB)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  sawb->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  sawb->timeScale = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 2);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((sawb->damr = (amrDecSpecStruc *)mp4malloc(sizeof(amrDecSpecStruc))) == NULL)
    return -100;

  bytesread = readDAMR(handle, sawb->damr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < sawb->atomhdr->size )
  	{
    bytesread = discardData(handle, sawb->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}      

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readESD(MP4HandleImp handle,
 *                   ESDAtom *esd)
 *
 * Description:
 *
 *   This function parses one ESD atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   esd                ESD pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readESD(MP4HandleImp handle, ESDAtom *esd)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;
  mp4_i32 decConfDescrBytesRead = 0;


  if ((esd->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readFullAtomHeader(handle, esd->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (esd->atomhdr->type != ATOMTYPE_ESD)
    return -1;


  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  esd->esDescrTag = handle->buf[0];
  totalbytesread += bytesread;
  if (esd->esDescrTag != 3) /* ES_DescrTag == 3 */
    return -1;

  esd->size = 0;
  do
  {
    mp4_u8 c;

    bytesread = readData(handle, handle->buf, 1);
    if (bytesread < 0)
      return -1;
    c = (mp4_u8)(handle->buf[0] & 0x7f);
    esd->size = (esd->size << 7) | c;
    totalbytesread += bytesread;
  }
  while (handle->buf[0] & 0x80);

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  esd->ESID = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  esd->flags = handle->buf[0];
  totalbytesread += bytesread;

  if (esd->flags & 0x80) /* Stream Dependence flag has been set */
  {
    bytesread = readData(handle, handle->buf, 2);
    if (bytesread < 0)
      return -1;
    esd->dependsOnESID = u16endian(*((mp4_u16 *)handle->buf));
    totalbytesread += bytesread;
  }

  if (esd->flags & 0x40) /* URL flag has been set */
  {
    bytesread = readData(handle, handle->buf, 1);
    if (bytesread < 0)
      return -1;
    esd->URLLength = handle->buf[0];
    totalbytesread += bytesread;

    bytesread = discardData(handle, esd->URLLength);
    if (bytesread < 0)
      return -1;
    totalbytesread += bytesread;
  }

  if (esd->flags & 0x20) /* OCR stream flag has been set */
  {
    bytesread = readData(handle, handle->buf, 2);
    if (bytesread < 0)
      return -1;
    esd->OCRESID = u16endian(*((mp4_u16 *)handle->buf));
    totalbytesread += bytesread;
  }

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  esd->decConfDescrTag = handle->buf[0];
  totalbytesread += bytesread;
  if (esd->decConfDescrTag != 4) /* DecoderConfigDescrTag == 4 */
    return -1;

  esd->decConfDescrSize = 0;
  do
  {
    mp4_u8 c;

    bytesread = readData(handle, handle->buf, 1);
    if (bytesread < 0)
      return -1;
    c = (mp4_u8)(handle->buf[0] & 0x7f);
    esd->decConfDescrSize = (esd->decConfDescrSize << 7) | c;
    totalbytesread += bytesread;
  }
  while (handle->buf[0] & 0x80);

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  esd->objectTypeIndication = handle->buf[0];
  totalbytesread += bytesread;
  decConfDescrBytesRead += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  esd->stream = handle->buf[0];
  totalbytesread += bytesread;
  decConfDescrBytesRead += bytesread;

  bytesread = readData(handle, handle->buf, 3);
  if (bytesread < 0)
    return -1;
  esd->bufferSizeDB = ((mp4_u32)handle->buf[0]) << 16 |
                      ((mp4_u32)handle->buf[1]) << 8 |
                      ((mp4_u32)handle->buf[2]);
  totalbytesread += bytesread;
  decConfDescrBytesRead += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  esd->maxBitrate = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;
  decConfDescrBytesRead += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  esd->avgBitrate = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;
  decConfDescrBytesRead += bytesread;

  if ((mp4_u32)decConfDescrBytesRead < esd->decConfDescrSize)
  {
    bytesread = readData(handle, handle->buf, 1);
    if (bytesread < 0)
      return -1;
    esd->decSpecificInfoTag = handle->buf[0];
    totalbytesread += bytesread;
    decConfDescrBytesRead += bytesread;
    if (esd->decSpecificInfoTag != 5) /* DecSpecificInfoTag == 5 */
    {
      bytesread = discardData(handle, esd->decConfDescrSize - decConfDescrBytesRead);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;
    }
    else
    {
      esd->decSpecificInfoSize = 0;
      do
      {
        mp4_u8 c;

        bytesread = readData(handle, handle->buf, 1);
        if (bytesread < 0)
          return -1;
        c = (mp4_u8)(handle->buf[0] & 0x7f);
        esd->decSpecificInfoSize = (esd->decSpecificInfoSize << 7) | c;
        totalbytesread += bytesread;
      }
      while (handle->buf[0] & 0x80);

      if(esd->decSpecificInfoSize)
      {
        if ((esd->decSpecificInfo = (mp4_u8 *)mp4malloc(esd->decSpecificInfoSize)) == NULL)
          return -100;

        bytesread = readData(handle, esd->decSpecificInfo, esd->decSpecificInfoSize);
        if (bytesread < 0)
          return -1;
        totalbytesread += bytesread;
      }
    }
  }

  bytesread = discardData(handle, esd->atomhdr->size - totalbytesread);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readD263(MP4HandleImp handle,
 *                    h263SpecificAtom *d263)
 *
 * Description:
 *
 *   This function parses one D263 atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   d263               D263 pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readD263(MP4HandleImp handle, h263SpecificAtom *d263)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((d263->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, d263->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (d263->atomhdr->type != ATOMTYPE_D263)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  d263->vendor = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  d263->decoderVersion = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  d263->h263Level = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  d263->h263Profile = handle->buf[0];
  totalbytesread += bytesread;

  /* Check for the bitrate atom */

  while ((mp4_u32)totalbytesread < d263->atomhdr->size)
  {
    mp4_u32 type;


    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_BITR:

      if (d263->bitr) /* BITR has already been read, more than one is not allowed */
        return -1;

      if ((d263->bitr = (bitrateAtom *)mp4malloc(sizeof(bitrateAtom))) == NULL)
        return -100;

      bytesread = readBITR(handle, d263->bitr);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;

    default: /* Other atoms are not needed */

      bytesread = readUnknown(handle);
      if (bytesread < 0)
        return -1;
      totalbytesread += bytesread;

      break;
    }
  }

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readBITR(MP4HandleImp handle,
 *                    bitrateAtom *d263)
 *
 * Description:
 *
 *   This function parses one BITR atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   bitr               BITR pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readBITR(MP4HandleImp handle, bitrateAtom *bitr)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((bitr->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, bitr->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (bitr->atomhdr->type != ATOMTYPE_BITR)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  bitr->avgBitrate = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  bitr->maxBitrate = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readDAMR(MP4HandleImp handle,
 *                    amrDecSpecStruc *damr)
 *
 * Description:
 *
 *   This function parses one DAMR atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   damr               DAMR pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readDAMR(MP4HandleImp handle, amrDecSpecStruc *damr)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((damr->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, damr->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (damr->atomhdr->type != ATOMTYPE_DAMR)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  damr->vendor = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  damr->decoderVersion = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  damr->modeSet = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  damr->modeChangePeriod = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  damr->framesPerSample = handle->buf[0];
  totalbytesread += bytesread;

  return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 freeFTYP(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function frees memory for FTYP atom.
 *
 * Parameters:
 *
 *   ftyp       FTYP atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeFTYP(fileTypeAtom *ftyp)
{
  if (ftyp)
  {
    if (freeAtomHeader(ftyp->atomhdr) < 0)
      return -1;
    if (ftyp->compatibleBrands)
      mp4free(ftyp->compatibleBrands);

    mp4free(ftyp);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMOOV(movieAtom *moov)
 *
 * Description:
 *
 *   This function frees memory for MOOV atom.
 *
 * Parameters:
 *
 *   moov       MOOV atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMOOV(movieAtom *moov)
{
  if (moov)
  {
    if (freeAtomHeader(moov->atomhdr) < 0)
      return -1;
    if (freeMVHD(moov->mvhd) < 0)
      return -1;
    if (freeTRAK(moov->trakAudio) < 0)
      return -1;
    if (freeTRAK(moov->trakVideo) < 0)
      return -1;
    if (freeIODS(moov->iods) < 0)
      return -1;
    if (freeUDTA(moov->udta) < 0)
      return -1;

    mp4free(moov);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeAtomHeader(atomHeader *atomhdr)
 *
 * Description:
 *
 *   This function frees memory for atom header.
 *
 * Parameters:
 *
 *   atomhdr    atom header pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeAtomHeader(atomHeader *atomhdr)
{
  if (atomhdr)
    mp4free(atomhdr);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMVHD(movieHeaderAtom *mvhd)
 *
 * Description:
 *
 *   This function frees memory for MVHD atom.
 *
 * Parameters:
 *
 *   mvhd       MVHD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMVHD(movieHeaderAtom *mvhd)
{
  if (mvhd)
  {
    if (freeAtomHeader(mvhd->atomhdr) < 0)
      return -1;

    mp4free(mvhd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeTRAK(trackAtom *trak)
 *
 * Description:
 *
 *   This function frees memory for TRAK atom.
 *
 * Parameters:
 *
 *   trak       TRAK atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeTRAK(trackAtom *trak)
{
  if (trak)
  {
    if (freeAtomHeader(trak->atomhdr) < 0)
      return -1;
    if (freeTKHD(trak->tkhd) < 0)
      return -1;
    if (freeTREF(trak->tref) < 0)
      return -1;
    if (freeEDTS(trak->edts) < 0)
      return -1;
    if (freeMDIA(trak->mdia) < 0)
      return -1;
    if (freeUDTA(trak->udta) < 0)
      return -1;

    mp4free(trak);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeTKHD(trackHeaderAtom *tkhd)
 *
 * Description:
 *
 *   This function frees memory for TKHD atom.
 *
 * Parameters:
 *
 *   tkhd       TKHD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeTKHD(trackHeaderAtom *tkhd)
{
  if (tkhd)
  {
    if (freeAtomHeader(tkhd->atomhdr) < 0)
      return -1;

    mp4free(tkhd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeTREF(trackReferenceAtom *tref)
 *
 * Description:
 *
 *   This function frees memory for TREF atom.
 *
 * Parameters:
 *
 *   tref       TREF atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeTREF(trackReferenceAtom *tref)
{
  if (tref)
  {
    if (freeAtomHeader(tref->atomhdr) < 0)
      return -1;

    mp4free(tref);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeEDTS(editListContainerAtom *edts)
 *
 * Description:
 *
 *   This function frees memory for EDTS atom.
 *
 * Parameters:
 *
 *   edts       EDTS atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeEDTS(editListContainerAtom *edts)
{
  if (edts)
  {
    if (freeAtomHeader(edts->atomhdr) < 0)
      return -1;

    mp4free(edts);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMDIA(mediaAtom *mdia)
 *
 * Description:
 *
 *   This function frees memory for MDIA atom.
 *
 * Parameters:
 *
 *   mdia       MDIA atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMDIA(mediaAtom *mdia)
{
  if (mdia)
  {
    if (freeAtomHeader(mdia->atomhdr) < 0)
      return -1;
    if (freeMDHD(mdia->mdhd) < 0)
      return -1;
    if (freeHDLR(mdia->hdlr) < 0)
      return -1;
    if (freeMINF(mdia->minf) < 0)
      return -1;

    mp4free(mdia);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMDHD(mediaHeaderAtom *mdhd)
 *
 * Description:
 *
 *   This function frees memory for MDHD atom.
 *
 * Parameters:
 *
 *   mdhd       MDHD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMDHD(mediaHeaderAtom *mdhd)
{
  if (mdhd)
  {
    if (freeAtomHeader(mdhd->atomhdr) < 0)
      return -1;

    mp4free(mdhd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeHDLR(handlerAtom *hdlr)
 *
 * Description:
 *
 *   This function frees memory for HDLR atom.
 *
 * Parameters:
 *
 *   hdlr       HDLR atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeHDLR(handlerAtom *hdlr)
{
  if (hdlr)
  {
    if (freeAtomHeader(hdlr->atomhdr) < 0)
      return -1;
    if (hdlr->name)
      mp4free(hdlr->name);

    mp4free(hdlr);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMINF(mediaInformationAtom *minf)
 *
 * Description:
 *
 *   This function frees memory for MINF atom.
 *
 * Parameters:
 *
 *   minf       MINF atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMINF(mediaInformationAtom *minf)
{
  if (minf)
  {
    if (freeAtomHeader(minf->atomhdr) < 0)
      return -1;
    if (freeVMHD(minf->vmhd) < 0)
      return -1;
    if (freeSMHD(minf->smhd) < 0)
      return -1;
    if (freeDINF(minf->dinf) < 0)
      return -1;
    if (freeSTBL(minf->stbl) < 0)
      return -1;

    mp4free(minf);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeVMHD(videoMediaHeaderAtom *vmhd)
 *
 * Description:
 *
 *   This function frees memory for VMHD atom.
 *
 * Parameters:
 *
 *   vmhd       VMHD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeVMHD(videoMediaHeaderAtom *vmhd)
{
  if (vmhd)
  {
    if (freeAtomHeader(vmhd->atomhdr) < 0)
      return -1;

    mp4free(vmhd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSMHD(soundMediaHeaderAtom *smhd)
 *
 * Description:
 *
 *   This function frees memory for SMHD atom.
 *
 * Parameters:
 *
 *   smhd       SMHD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSMHD(soundMediaHeaderAtom *smhd)
{
  if (smhd)
  {
    if (freeAtomHeader(smhd->atomhdr) < 0)
      return -1;

    mp4free(smhd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeDINF(dataInformationAtom *dinf)
 *
 * Description:
 *
 *   This function frees memory for DINF atom.
 *
 * Parameters:
 *
 *   dinf       DINF atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeDINF(dataInformationAtom *dinf)
{
  if (dinf)
  {
    if (freeAtomHeader(dinf->atomhdr) < 0)
      return -1;
    if (freeDREF(dinf->dref) < 0)
      return -1;

    mp4free(dinf);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeDREF(dataReferenceAtom *dref)
 *
 * Description:
 *
 *   This function frees memory for DREF atom.
 *
 * Parameters:
 *
 *   dref       DREF atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeDREF(dataReferenceAtom *dref)
{
  if (dref)
  {
    if (freeAtomHeader(dref->atomhdr) < 0)
      return -1;
    if (freeURL(dref->url) < 0)
      return -1;
    if (freeURN(dref->urn) < 0)
      return -1;

    mp4free(dref);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeURL(dataEntryURLAtom *url)
 *
 * Description:
 *
 *   This function frees memory for URL atom.
 *
 * Parameters:
 *
 *   url        URL atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeURL(dataEntryURLAtom *url)
{
  if (url)
  {
    if (freeAtomHeader(url->atomhdr) < 0)
      return -1;

    mp4free(url);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeURN(dataEntryURNAtom *urn)
 *
 * Description:
 *
 *   This function frees memory for URN atom.
 *
 * Parameters:
 *
 *   urn        URN atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeURN(dataEntryURNAtom *urn)
{
  if (urn)
  {
    if (freeAtomHeader(urn->atomhdr) < 0)
      return -1;

    mp4free(urn);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTBL(sampleTableAtom *stbl)
 *
 * Description:
 *
 *   This function frees memory for STBL atom.
 *
 * Parameters:
 *
 *   stbl       STBL atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTBL(sampleTableAtom *stbl)
{
  if (stbl)
  {
    if (freeAtomHeader(stbl->atomhdr) < 0)
      return -1;
    if (freeSTTS(stbl->stts) < 0)
      return -1;
    if (freeCTTS(stbl->ctts) < 0)
      return -1;
    if (freeSTSD(stbl->stsd) < 0)
      return -1;
    if (freeSTSZ(stbl->stsz) < 0)
      return -1;
    if (freeSTSC(stbl->stsc) < 0)
      return -1;
    if (stbl->is32BitOffsets)
    {
    
      if (freeSTCO(stbl->stco) < 0)
        return -1;
    }
    else
    {
    
      if (freeSTCO64(stbl->stco64) < 0)
        return -1;
    }
    if (freeSTSS(stbl->stss) < 0)
      return -1;
    if (freeSTSH(stbl->stsh) < 0)
      return -1;
    if (freeSDTP(stbl->sdtp) < 0)
      return -1;

    mp4free(stbl);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTTS(timeToSampleAtom *stts)
 *
 * Description:
 *
 *   This function frees memory for STTS atom.
 *
 * Parameters:
 *
 *   stts       STTS atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTTS(timeToSampleAtom *stts)
{
  if (stts)
  {
    if (freeAtomHeader(stts->atomhdr) < 0)
      return -1;
    if (stts->sampleCount)
      mp4free(stts->sampleCount);
    if (stts->sampleDelta)
      mp4free(stts->sampleDelta);

    mp4free(stts);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeCTTS(compositionTimeToSampleAtom *ctts)
 *
 * Description:
 *
 *   This function frees memory for CTTS atom.
 *
 * Parameters:
 *
 *   ctts       CTTS atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeCTTS(compositionTimeToSampleAtom *ctts)
{
  if (ctts)
  {
    if (freeAtomHeader(ctts->atomhdr) < 0)
      return -1;
    if (ctts->sampleCount)
      mp4free(ctts->sampleCount);
    if (ctts->sampleOffset)
      mp4free(ctts->sampleOffset);

    mp4free(ctts);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTSD(sampleDescriptionAtom *stsd)
 *
 * Description:
 *
 *   This function frees memory for STSD atom.
 *
 * Parameters:
 *
 *   stsd       STSD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTSD(sampleDescriptionAtom *stsd)
{
  mp4_u32 sampleentrycount = 0;
  mp4_u32 entryindex;
  
  if (stsd)
  {
	sampleentrycount = stsd->entryCount;

    if (freeAtomHeader(stsd->atomhdr) < 0)
      return -1;
	for (entryindex = 0; entryindex < sampleentrycount; entryindex++)
	{
		if (freeMP4V(stsd->mp4v[entryindex]) < 0)
		  return -1;
		if (freeMP4A(stsd->mp4a[entryindex]) < 0)
		  return -1;
		if (freeMP4S(stsd->mp4s[entryindex]) < 0)
		  return -1;
		if (freeS263(stsd->s263[entryindex]) < 0)
		  return -1;
		if (freeSAMR(stsd->samr[entryindex]) < 0)
		  return -1;
		if (freeSAWB(stsd->sawb[entryindex]) < 0)
		  return -1;
		if (freeAVC1(stsd->avc1[entryindex]) < 0)
			return -1;
        if (freeSQCP(stsd->sqcp[entryindex]) < 0)
          return -1;
	}
    mp4free(stsd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMP4V(visualSampleEntry *mp4v)
 *
 * Description:
 *
 *   This function frees memory for MP4V atom.
 *
 * Parameters:
 *
 *   mp4v       MP4V atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMP4V(visualSampleEntry *mp4v)
{
  if (mp4v)
  {
    if (freeAtomHeader(mp4v->atomhdr) < 0)
      return -1;
    if (freeESD(mp4v->esd) < 0)
      return -1;

    mp4free(mp4v);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeESD(ESDAtom *esd)
 *
 * Description:
 *
 *   This function frees memory for ESD atom.
 *
 * Parameters:
 *
 *   esd        ESD atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeESD(ESDAtom *esd)
{
  if (esd)
  {
    if (freeAtomHeader(esd->atomhdr) < 0)
      return -1;
    if (esd->URLString)
      mp4free(esd->URLString);
    if (esd->decSpecificInfo)
      mp4free(esd->decSpecificInfo);

    mp4free(esd);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMP4A(audioSampleEntry *mp4a)
 *
 * Description:
 *
 *   This function frees memory for MP4A atom.
 *
 * Parameters:
 *
 *   mp4a       MP4A atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMP4A(audioSampleEntry *mp4a)
{
  if (mp4a)
  {
    if (freeAtomHeader(mp4a->atomhdr) < 0)
      return -1;
    if (freeESD(mp4a->esd) < 0)
      return -1;

    mp4free(mp4a);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeMP4S(mpegSampleEntry *mp4s)
 *
 * Description:
 *
 *   This function frees memory for MP4S atom.
 *
 * Parameters:
 *
 *   mp4s       MP4S atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeMP4S(mpegSampleEntry *mp4s)
{
  if (mp4s)
  {
    if (freeAtomHeader(mp4s->atomhdr) < 0)
      return -1;
    if (freeESD(mp4s->esd) < 0)
      return -1;

    mp4free(mp4s);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeS263(h263SampleEntry *s263)
 *
 * Description:
 *
 *   This function frees memory for S263 atom.
 *
 * Parameters:
 *
 *   s263       S263 atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeS263(h263SampleEntry *s263)
{
  if (s263)
  {
    if (freeAtomHeader(s263->atomhdr) < 0)
      return -1;
    if (freeD263(s263->d263) < 0)
      return -1;

    mp4free(s263);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeD263(h263SpecificAtom *d263)
 *
 * Description:
 *
 *   This function frees memory for D263 atom.
 *
 * Parameters:
 *
 *   d263       D263 atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeD263(h263SpecificAtom *d263)
{
  if (d263)
  {
    if (freeAtomHeader(d263->atomhdr) < 0)
      return -1;
    if (freeBITR(d263->bitr) < 0)
      return -1;

    mp4free(d263);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeBITR(BitrateAtom *bitr)
 *
 * Description:
 *
 *   This function frees memory for BITR atom.
 *
 * Parameters:
 *
 *   bitr       BITR atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeBITR(bitrateAtom *bitr)
{
  if (bitr)
  {
    if (freeAtomHeader(bitr->atomhdr) < 0)
      return -1;

    mp4free(bitr);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSAMR(amrSampleEntry *samr)
 *
 * Description:
 *
 *   This function frees memory for SAMR atom.
 *
 * Parameters:
 *
 *   samr       SAMR atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSAMR(amrSampleEntry *samr)
{
  if (samr)
  {
    if (freeAtomHeader(samr->atomhdr) < 0)
      return -1;
    if (freeDAMR(samr->damr) < 0)
      return -1;

    mp4free(samr);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSAWB(amrSampleEntry *sawb)
 *
 * Description:
 *
 *   This function frees memory for SAWB atom.
 *
 * Parameters:
 *
 *   sawb       SAWB atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSAWB(amrSampleEntry *sawb)
{
  if (sawb)
  {
    if (freeAtomHeader(sawb->atomhdr) < 0)
      return -1;
    if (freeDAMR(sawb->damr) < 0)
      return -1;

    mp4free(sawb);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeDAMR(amrDecSpecStruc *damr)
 *
 * Description:
 *
 *   This function frees memory for DAMR atom.
 *
 * Parameters:
 *
 *   damr       DAMR atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeDAMR(amrDecSpecStruc *damr)
{
  if (damr)
  {
    if (freeAtomHeader(damr->atomhdr) < 0)
      return -1;

    mp4free(damr);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTSZ(sampleSizeAtom *stsz)
 *
 * Description:
 *
 *   This function frees memory for STSZ atom.
 *
 * Parameters:
 *
 *   stsz       STSZ atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTSZ(sampleSizeAtom *stsz)
{
  if (stsz)
  {
    if (freeAtomHeader(stsz->atomhdr) < 0)
      return -1;
    if (stsz->entrySize)
      mp4free(stsz->entrySize);

    mp4free(stsz);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTSC(sampleToChunkAtom *stsc)
 *
 * Description:
 *
 *   This function frees memory for STSC atom.
 *
 * Parameters:
 *
 *   stsc       STSC atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTSC(sampleToChunkAtom *stsc)
{
  if (stsc)
  {
    if (freeAtomHeader(stsc->atomhdr) < 0)
      return -1;
    if (stsc->firstChunk)
      mp4free(stsc->firstChunk);
    if (stsc->samplesPerChunk)
      mp4free(stsc->samplesPerChunk);
    if (stsc->sampleDescriptionIndex)
      mp4free(stsc->sampleDescriptionIndex);

    mp4free(stsc);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTCO(chunkOffsetAtom *stco)
 *
 * Description:
 *
 *   This function frees memory for STCO atom.
 *
 * Parameters:
 *
 *   stco       STCO atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTCO(chunkOffsetAtom *stco)
{
  if (stco)
  {
    if (freeAtomHeader(stco->atomhdr) < 0)
      return -1;
    if (stco->chunkOffset)
      mp4free(stco->chunkOffset);

    mp4free(stco);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 freeSTCO64(chunkOffset64Atom *stco64)
 *
 * Description:
 *
 *   This function frees memory for STCO64 atom.
 *
 * Parameters:
 *
 *   stco64       STCO64 atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTCO64(chunkOffset64Atom *stco64)
{
  if (stco64)
  {
    if (freeAtomHeader(stco64->atomhdr) < 0)
      return -1;
    if (stco64->chunkOffset)
      mp4free(stco64->chunkOffset);

    mp4free(stco64);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTSS(syncSampleAtom *stss)
 *
 * Description:
 *
 *   This function frees memory for STSS atom.
 *
 * Parameters:
 *
 *   stss       STSS atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTSS(syncSampleAtom *stss)
{
  if (stss)
  {
    if (freeAtomHeader(stss->atomhdr) < 0)
      return -1;
    if (stss->sampleNumber)
      mp4free(stss->sampleNumber);

    mp4free(stss);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSTSH(shadowSyncSampleAtom *stsh)
 *
 * Description:
 *
 *   This function frees memory for STSH atom.
 *
 * Parameters:
 *
 *   stsh       STSH atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSTSH(shadowSyncSampleAtom *stsh)
{
  if (stsh)
  {
    if (freeAtomHeader(stsh->atomhdr) < 0)
      return -1;

    mp4free(stsh);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 freeSDTP(sampleDependencyAtom *sdtp)
 *
 * Description:
 *
 *   This function frees memory for SDTP atom.
 *
 * Parameters:
 *
 *   sdtp       SDTP atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSDTP(sampleDependencyAtom *sdtp)
	{
	if (sdtp)
		{
		if (freeAtomHeader(sdtp->atomhdr) < 0)
			{
			return -1;
			}

		if (sdtp->dep)
			{
			mp4free(sdtp->dep);
			}		
    
		mp4free(sdtp);
		}

  	return 0;
	}

/*
 * Function:
 *
 *   mp4_i32 freeIODS(objectDescriptorAtom *iods)
 *
 * Description:
 *
 *   This function frees memory for IODS atom.
 *
 * Parameters:
 *
 *   iods       IODS atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeIODS(objectDescriptorAtom *iods)
{
  if (iods)
  {
    if (freeAtomHeader(iods->atomhdr) < 0)
      return -1;

    mp4free(iods);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 readUDTA(MP4HandleImp handle,
 *                    userDataAtom *udta)
 *
 * Description:
 *
 *   This function parses one UDTA atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   udta               UDTA pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readUDTA(MP4HandleImp handle, userDataAtom *udta)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;

  if ((udta->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, udta->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (udta->atomhdr->type != ATOMTYPE_UDTA)
    return -1;

  if ( handle->file )
      {
      udta->atomcontentloc = handle->diskReadBufStart + handle->diskReadBufPos;
      }
  else
      {
      udta->atomcontentloc = handle->absPosition;
      }

  if ( udta->atomhdr->size == 1 )
      {
      udta->atomcontentsize = I64INT(udta->atomhdr->largeSize) - (TInt)totalbytesread;
      }
  else
      {
      udta->atomcontentsize = (TInt)(udta->atomhdr->size - totalbytesread);
      }

  if ( handle->file )
  {
  	if ( seekFile(handle, udta->atomhdr->size - totalbytesread) < 0 )
  	{
  	   return -1;	
  	}
  	else
  	{
  	   return udta->atomhdr->size;	
  	}
  }
  else
  {
	  bytesread = discardData(handle, udta->atomhdr->size - totalbytesread);
	  if (bytesread < 0)
	    return -1;
	  totalbytesread += bytesread;
  }
  return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 freeUDTA(userDataAtom *udta)
 *
 * Description:
 *
 *   This function frees memory for UDTA atom.
 *
 * Parameters:
 *
 *   udta       UDTA atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeUDTA(userDataAtom *udta)
{
  if (udta)
  {
    if (freeAtomHeader(udta->atomhdr) < 0)
      return -1;
    mp4free(udta);
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineVideoLength(MP4HandleImp handle,
 *                                mp4_u32 *videolength)
 *
 * Description:
 *
 *   This function determines the length of video in milliseconds.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   videolength  Video length is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 determineVideoLength(MP4HandleImp handle, mp4_u32 *videolength)
{
  if (!handle->moov)
    return -1;

  if (!handle->moov->mvhd)
    return -1;

  if (!handle->moov->trakVideo)
    return -1;

  if (!handle->moov->trakVideo->tkhd)
    return -1;

  /* Is timescale set? */
  if (handle->moov->mvhd->timeScale == 0)
    return -1;

  *videolength = (mp4_u32)(((mp4_double)handle->moov->trakVideo->tkhd->duration / (mp4_double)handle->moov->mvhd->timeScale) * (mp4_double)1000);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineFrameRate(MP4HandleImp handle,
 *                              mp4_double *framerate)
 *
 * Description:
 *
 *   This function determines the frame rate of video.
 *
 *   Frame rate is calculated as the average frame rate of the entire video.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *   framerate  Frame rate is returned here
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 determineFrameRate(MP4HandleImp handle, mp4_double *framerate)
{
  mp4_double numberofframes;
  mp4_double length;


  if (!handle)
    return -1;
  if (!handle->moov)
    return -1;
  if (!handle->moov->trakVideo)
    return -1;
  if (!handle->moov->trakVideo->mdia)
    return -1;
  if (!handle->moov->trakVideo->mdia->minf)
    return -1;
  if (!handle->moov->trakVideo->mdia->minf->stbl)
    return -1;
  if (!handle->moov->trakVideo->mdia->minf->stbl->stsz)
    return -1;
  if (!handle->moov->trakVideo->mdia->mdhd)
    return -1;


  	if (handle->moov->trakVideo->mdia->mdhd->timeScale == 0)
    	return -1;
  	
  	if (handle->moov->trakVideo->mdia->mdhd->duration == 0)
  		{
  		*framerate = 0;
  		}
	else
		{
  		numberofframes = (mp4_double)handle->moov->trakVideo->mdia->minf->stbl->stsz->sampleCount;
  		length = (mp4_double)handle->moov->trakVideo->mdia->mdhd->duration /
           		 (mp4_double)handle->moov->trakVideo->mdia->mdhd->timeScale;

  		*framerate = numberofframes / length;			
		}    

  	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 determineVideoType(MP4HandleImp handle,
 *                              mp4_u32 *videotype)
 *
 * Description:
 *
 *   This function determines the video type of the MP4.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *   videotype  Video type is returned here
 *
 * Return value:
 *
 *   0          Success
 *   -1			Error
 *	 -2			Unknown video type
 *
 */
mp4_i32 determineVideoType(MP4HandleImp handle, mp4_u32 *videotype)
{
  *videotype = MP4_TYPE_NONE;

  if (!handle->moov)
    return -1;

  if (!handle->moov->trakVideo)
    return -1;

  if (!handle->moov->trakVideo->mdia)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf->stbl)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf->stbl->stsd)
    return -1;
  
  if (handle->moov->trakVideo->mdia->minf->stbl->stsd->entryCount == 0)
    return -1;  

/* Assume that the video type is the same for all sample entries. Just get the video type from the first one */

  if (handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0])
  {
    if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0]->esd)
      return -1;

    if ((handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0]->esd->objectTypeIndication == 0x20) &&
        ((handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0]->esd->stream >> 2) == 0x04))
      *videotype = MP4_TYPE_MPEG4_VIDEO;
  }
  else
  if (handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0])
  {
    if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0]->d263)
      return -1;

    switch (handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0]->d263->h263Profile)
    {
    case 0:
      *videotype = MP4_TYPE_H263_PROFILE_0;
      break;
    case 3:
      *videotype = MP4_TYPE_H263_PROFILE_3;
      break;
    default:
      break;
    }
  }
  else
   if (handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0])
   {
	   if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc)
		   return -1;

	   if (handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfigSize == 0)
		   return -1;

	   /* AVC profile is in the second byte of the avcconfigrecord */
	   switch((mp4_u8)handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfig[1]) 
	   {
	   case 66:
		   *videotype = MP4_TYPE_AVC_PROFILE_BASELINE;
		   break;
	   case 77:
		   *videotype = MP4_TYPE_AVC_PROFILE_MAIN;
		   break;
	   case 88:
		   *videotype = MP4_TYPE_AVC_PROFILE_EXTENDED;
		   break;
       case 100:
          *videotype = MP4_TYPE_AVC_PROFILE_HIGH;
           break;
	   default:
		   {
		   mp4_u8 constraintByte = (mp4_u8)handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfig[2];
		   if ( (constraintByte & 0x80) || (constraintByte & 0x10) )
			   {
			   *videotype = MP4_TYPE_AVC_PROFILE_BASELINE;	
			   }
		   else if (constraintByte & 0x40)
			   {
			   *videotype = MP4_TYPE_AVC_PROFILE_MAIN;	
			   }
		   else if (constraintByte & 0x20)
			   {
			   *videotype = MP4_TYPE_AVC_PROFILE_EXTENDED;
			   }
		   // NOTE: Cannot reliably determine higher profiles from
		   // the constraint flags.
		   break;	   	
		   }
	   }
	}
   else
       {}
   /* Note: Read the AVC level and recreate the actual AVC profile and level in the future! */
  if (*videotype == MP4_TYPE_NONE)
    return -2;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineVideoResolution(MP4HandleImp handle,
 *                                    mp4_u32 *videowidth,
 *                                    mp4_u32 *videoheight)
 *
 * Description:
 *
 *   This function finds out the video width and height from the atom
 *   structure.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   videowidth   Video width is returned here
 *   videoheight  Video height is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 determineVideoResolution(MP4HandleImp handle, mp4_u32 *videowidth, mp4_u32 *videoheight)
{
  mp4_u32 videotype = MP4_TYPE_NONE;


  if (determineVideoType(handle, &videotype) < 0)
    return -1;

  if (videotype == MP4_TYPE_NONE)
    return -1;

  if (!handle->moov)
    return -1;

  if (!handle->moov->trakVideo)
    return -1;

  if (!handle->moov->trakVideo->mdia)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf->stbl)
    return -1;

  if (!handle->moov->trakVideo->mdia->minf->stbl->stsd)
    return -1;

  if (handle->moov->trakVideo->mdia->minf->stbl->stsd->entryCount == 0)
    return -1;  
  
/* Assume that the video characteristics for all the video sample entries are the same. Just get them from the first one */

  if (videotype == MP4_TYPE_H263_PROFILE_0 ||
      videotype == MP4_TYPE_H263_PROFILE_3)
  {
    if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0])
      return -1;

    *videowidth  = handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0]->width;
    *videoheight = handle->moov->trakVideo->mdia->minf->stbl->stsd->s263[0]->height;
  }
  else if (videotype == MP4_TYPE_MPEG4_VIDEO)
  {
    if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0])
      return -1;

    *videowidth  = handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0]->width;
    *videoheight = handle->moov->trakVideo->mdia->minf->stbl->stsd->mp4v[0]->height;
  }
  else if ( isAvcVideo(videotype) )
  {
    if (!handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0])
      return -1;

    *videowidth  = handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->width;
    *videoheight = handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->height;
  }
  else
  {
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineVideoTimeScale(MP4HandleImp handle,
 *                                   mp4_u32 *timescale)
 *
 * Description:
 *
 *   This function determines the timescale of video track.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *   timescale  Timescale of video track is returned here
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 determineVideoTimeScale(MP4HandleImp handle, mp4_u32 *timescale)
{
  if (timescale == NULL)
    return 0;

  if (!handle)
    return -1;
  if (!handle->moov)
    return -1;
  if (!handle->moov->trakVideo)
    return -1;
  if (!handle->moov->trakVideo->mdia)
    return -1;
  if (!handle->moov->trakVideo->mdia->mdhd)
    return -1;
  if (handle->moov->trakVideo->mdia->mdhd->timeScale == 0)
    return -1;

  *timescale = handle->moov->trakVideo->mdia->mdhd->timeScale;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioLength(MP4HandleImp handle,
 *                                mp4_u32 *audiolength)
 *
 * Description:
 *
 *   This function determines the length of audio in milliseconds.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   audiolength  Audio length is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 determineAudioLength(MP4HandleImp handle, mp4_u32 *audiolength)
{
  if (!handle->moov)
    return -1;

  if (!handle->moov->mvhd)
    return -1;

  if (!handle->moov->trakAudio)
    return -1;

  if (!handle->moov->trakAudio->tkhd)
    return -1;

  /* Is timescale set? */
  if (handle->moov->mvhd->timeScale == 0)
    return -1;

  *audiolength = (mp4_u32)(((mp4_double)handle->moov->trakAudio->tkhd->duration / (mp4_double)handle->moov->mvhd->timeScale) * (mp4_double)1000);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioType(MP4HandleImp handle,
 *                              mp4_u32 *audiotype)
 *
 * Description:
 *
 *   This function determines the audio type of the MP4.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *   audiotype  Audio type is returned here
 *
 * Return value:
 *
 *   0          Success
 *   -1   		Error
 *   -2			Unknown audiotrack
 *
 */
mp4_i32 determineAudioType(MP4HandleImp handle, mp4_u32 *audiotype)
{
  *audiotype = MP4_TYPE_NONE;


  if (!handle->moov)
    return -1;

  if (!handle->moov->trakAudio)
    return -1;

  if (!handle->moov->trakAudio->mdia)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf->stbl)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf->stbl->stsd)
    return -1;
  
  if (handle->moov->trakAudio->mdia->minf->stbl->stsd->entryCount == 0)
    return -1;  

/* Assume that the audio type is the same for all sample entries. Just get the audio type from the first one */
  if (handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0])
  {
    if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0]->esd)
      return -1;

    if ((handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0]->esd->objectTypeIndication == 0x40) &&
        ((handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0]->esd->stream >> 2) == 0x05))
      *audiotype = MP4_TYPE_MPEG4_AUDIO;

   if ((handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0]->esd->objectTypeIndication == 0xE1) &&
     ((handle->moov->trakAudio->mdia->minf->stbl->stsd->mp4a[0]->esd->stream >> 2) == 0x05))  
   {
      *audiotype = MP4_TYPE_QCELP_13K;
      handle->qcelpStoredAsMPEGAudio = MP4TRUE;
   }
  }
  else if (handle->moov->trakAudio->mdia->minf->stbl->stsd->samr[0])
    *audiotype = MP4_TYPE_AMR_NB;
  else if (handle->moov->trakAudio->mdia->minf->stbl->stsd->sawb[0])
    *audiotype = MP4_TYPE_AMR_WB;
  else if (handle->moov->trakAudio->mdia->minf->stbl->stsd->sqcp[0])
  {
    *audiotype = MP4_TYPE_QCELP_13K;
    handle->qcelpStoredAsMPEGAudio = MP4FALSE;
  }
  else
  {
  }

  if (*audiotype == MP4_TYPE_NONE)
    return -2;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioFramesPerSample(MP4HandleImp handle,
 *                                         mp4_u8 *framespersample)
 *
 * Description:
 *
 *   This function determines the number of audio frames in each sample.
 *   The function works with AMR audio type only.
 *
 * Parameters:
 *
 *   handle           MP4 library handle
 *   framespersample  Number of frames in each sample
 *
 * Return value:
 *
 *   0                Success
 *   Negative         Error
 *
 */
mp4_i32 determineAudioFramesPerSample(MP4HandleImp handle, mp4_u8 *framespersample)
{
  mp4_i8 sampleentryindex;
  
  *framespersample = 0;


  if (!((handle->type & MP4_TYPE_AMR_NB) ||
        (handle->type & MP4_TYPE_AMR_WB) ||
        ((handle->type & MP4_TYPE_QCELP_13K) && (!handle->qcelpStoredAsMPEGAudio))))
  {
    *framespersample = 1;

    return 0;
  }


  if (!handle->moov)
    return -1;

  if (!handle->moov->trakAudio)
    return -1;

  if (!handle->moov->trakAudio->mdia)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf->stbl)
    return -1;

  if (!handle->moov->trakAudio->mdia->minf->stbl->stsd)
    return -1;

  if (handle->type & MP4_TYPE_AMR_NB)
  {
/* Now, framespersample returns the maximum frames_per_sample listed in the AMR sample entries*/
	for (sampleentryindex = 0; (mp4_u8) sampleentryindex < handle->moov->trakAudio->mdia->minf->stbl->stsd->entryCount; sampleentryindex++)
	{
		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->samr[sampleentryindex])
		  return -1;

		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->samr[sampleentryindex]->damr)
		  return -1;

		if (*framespersample < handle->moov->trakAudio->mdia->minf->stbl->stsd->samr[sampleentryindex]->damr->framesPerSample)
			*framespersample = handle->moov->trakAudio->mdia->minf->stbl->stsd->samr[sampleentryindex]->damr->framesPerSample;
	}
  }
  else if (handle->type & MP4_TYPE_AMR_WB)
  {
/* Now, framespersample returns the maximum frames_per_sample listed in the AMR sample entries*/
	for (sampleentryindex = 0; (mp4_u8)sampleentryindex < handle->moov->trakAudio->mdia->minf->stbl->stsd->entryCount; sampleentryindex++)
	{
		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->sawb[sampleentryindex])
		  return -1;

		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->sawb[sampleentryindex]->damr)
		  return -1;

		if (*framespersample < handle->moov->trakAudio->mdia->minf->stbl->stsd->sawb[sampleentryindex]->damr->framesPerSample)
 		  *framespersample = handle->moov->trakAudio->mdia->minf->stbl->stsd->sawb[sampleentryindex]->damr->framesPerSample;
	}
  }
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (!handle->qcelpStoredAsMPEGAudio))
  {
/* Now, framespersample returns the maximum frames_per_sample listed in the QCELP-13K sample entries*/
	for (sampleentryindex = 0; (mp4_u8)sampleentryindex < handle->moov->trakAudio->mdia->minf->stbl->stsd->entryCount; sampleentryindex++)
	{
		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->sqcp[sampleentryindex])
		  return -1;

		if (!handle->moov->trakAudio->mdia->minf->stbl->stsd->sqcp[sampleentryindex]->dqcp)
		  return -1;

		if (*framespersample < handle->moov->trakAudio->mdia->minf->stbl->stsd->sqcp[sampleentryindex]->dqcp->framesPerSample)
		  *framespersample = handle->moov->trakAudio->mdia->minf->stbl->stsd->sqcp[sampleentryindex]->dqcp->framesPerSample;
	}
  }
  else
  {
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioTimeScale(MP4HandleImp handle,
 *                                   mp4_u32 *timescale)
 *
 * Description:
 *
 *   This function determines the timescale of audio track.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *   timescale  Timescale of audio track is returned here
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 determineAudioTimeScale(MP4HandleImp handle, mp4_u32 *timescale)
{
  if (timescale == NULL)
    return 0;

  if (!handle)
    return -1;
  if (!handle->moov)
    return -1;
  if (!handle->moov->trakAudio)
    return -1;
  if (!handle->moov->trakAudio->mdia)
    return -1;
  if (!handle->moov->trakAudio->mdia->mdhd)
    return -1;


  if (handle->moov->trakAudio->mdia->mdhd->timeScale == 0)
    return -1;

  *timescale = handle->moov->trakAudio->mdia->mdhd->timeScale;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioAverageBitRate(MP4HandleImp handle,
 *                                        mp4_u32 *averagebitrate)
 *
 * Description:
 *
 *   This function determines the average bitrate of the audio in bits per
 *   second.
 *
 *   The average is calculated so that the audio data length is divided by
 *   the length of the  audio track.
 *
 * Parameters:
 *
 *   handle                MP4 library handle
 *   averagebitrate        Result is returned here
 *
 * Return value:
 *
 *   0           Success
 *   Negative    Error
 *
 */
mp4_i32 determineAudioAverageBitRate(MP4HandleImp handle, mp4_u32 *averagebitrate)
	{
	mp4_u32 audiosize = 0;

  	if (!handle->moov)
  		{
  		return -1;	
  		}
    	
  	if (handle->moov->trakAudio)
  		{
		if (!handle->moov->trakAudio->mdia)
			{
			return -1;		
			}
					
		if (!handle->moov->trakAudio->mdia->minf)
			{
			return -1;	
			}
		
		if (!handle->moov->trakAudio->mdia->minf->stbl)
			{
			return -1;				
			}

		if (!handle->moov->trakAudio->mdia->minf->stbl->stsz)
			{
			return -1;				
			}

		if (handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleSize != 0)
			{
			audiosize += (handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleCount *
	                   	 handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleSize);			
			}
	    else
	    	{
	      	mp4_u32 i;
	      	for (i = 0; i < handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleCount; i++)
	      		{
	        	audiosize += handle->moov->trakAudio->mdia->minf->stbl->stsz->entrySize[i];      			
	      		}
	    	}
	  	}
	else
		{
		return -1;  		
		}

  	if (!handle->moov->trakAudio->mdia->mdhd)
  		{
    	return -1;  			
  		}
  	
  	if (!handle->moov->trakAudio->mdia->mdhd->timeScale)
  		{
		return -1;  			
  		}

  	if (handle->moov->trakAudio->mdia->mdhd->duration == 0)
  		{
  		*averagebitrate = 0;
  		}
	else
		{
		*averagebitrate = (mp4_u32)((mp4_double)8 *
		                  (mp4_double)audiosize /
		                  ((mp4_double)handle->moov->trakAudio->mdia->mdhd->duration /
		                  (mp4_double)handle->moov->trakAudio->mdia->mdhd->timeScale));
		}  	

  	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 determineStreamSize(MP4HandleImp handle,
 *                              mp4_u32 *streamsize)
 *
 * Description:
 *
 *   This function determines the size of media data in MP4 file/stream.
 *
 * Parameters:
 *
 *   handle      MP4 library handle
 *   streamsize  Size of data
 *
 * Return value:
 *
 *   0           Success
 *   Negative    Error
 *
 */
mp4_i32 determineStreamSize(MP4HandleImp handle, mp4_u32 *streamsize)
{
  *streamsize = 0;

  if (!handle->moov)
    return -1;

  if ((!handle->moov->trakAudio) && (!handle->moov->trakVideo))
    return -1;

  if (handle->moov->trakAudio)
  {
    if (!handle->moov->trakAudio->mdia)
      return -1;

    if (!handle->moov->trakAudio->mdia->minf)
      return -1;

    if (!handle->moov->trakAudio->mdia->minf->stbl)
      return -1;

    if (!handle->moov->trakAudio->mdia->minf->stbl->stsz)
      return -1;

    if (handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleSize != 0)
      *streamsize += handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleCount *
                     handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleSize;
    else
    {
      mp4_u32 i;

      for (i = 0; i < handle->moov->trakAudio->mdia->minf->stbl->stsz->sampleCount; i++)
        *streamsize += handle->moov->trakAudio->mdia->minf->stbl->stsz->entrySize[i];
    }
  }

  if (handle->moov->trakVideo)
  {
    if (!handle->moov->trakVideo->mdia)
      return -1;

    if (!handle->moov->trakVideo->mdia->minf)
      return -1;

    if (!handle->moov->trakVideo->mdia->minf->stbl)
      return -1;

    if (!handle->moov->trakVideo->mdia->minf->stbl->stsz)
      return -1;

    if (handle->moov->trakVideo->mdia->minf->stbl->stsz->sampleSize != 0)
      *streamsize += handle->moov->trakVideo->mdia->minf->stbl->stsz->sampleCount *
                     handle->moov->trakVideo->mdia->minf->stbl->stsz->sampleSize;
    else
    {
      mp4_u32 i;

      for (i = 0; i < handle->moov->trakVideo->mdia->minf->stbl->stsz->sampleCount; i++)
        *streamsize += handle->moov->trakVideo->mdia->minf->stbl->stsz->entrySize[i];
    }
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineStreamAverageBitRate(MP4HandleImp handle,
 *                                         mp4_u32 *streamaveragebitrate,
 *                                         mp4_u32 streamsize)
 *
 * Description:
 *
 *   This function determines the average bitrate of the stream in bits per
 *   second.
 *
 *   The average is calculated so that the media data length is divided by
 *   the length of the presentation.
 *
 * Parameters:
 *
 *   handle                MP4 library handle
 *   streamaveragebitrate  Result is returned here
 *   streamsize            Size of media data in bytes
 *
 * Return value:
 *
 *   0           Success
 *   Negative    Error
 *
 */
mp4_i32 determineStreamAverageBitRate(MP4HandleImp handle, mp4_u32 *streamaveragebitrate, mp4_u32 streamsize)
{
  if (!handle->moov)
    return -1;

  if (!handle->moov->mvhd)
    return -1;

  if (!handle->moov->mvhd->timeScale)
    return -1;

  if (handle->moov->mvhd->atomhdr->version == 1) /* 64 bit */
      {
      if (!handle->moov->mvhd->duration64)
        return -1;
     
      *streamaveragebitrate = (mp4_u32)((mp4_double)8 * (mp4_double)streamsize /
                                        ((mp4_double)handle->moov->mvhd->duration64 / handle->moov->mvhd->timeScale));      
      }
  else /* 32 bit */
      {
      if (!handle->moov->mvhd->duration)
        return -1;

      *streamaveragebitrate = (mp4_u32)((mp4_double)8 * (mp4_double)streamsize /
                                        ((mp4_double)handle->moov->mvhd->duration / handle->moov->mvhd->timeScale));      
      }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 advanceVideoFrame(MP4HandleImp handle,
 *                             trackAtom *trak)
 *
 * Description:
 *
 *   This function Advances one video frame and finds the frame offset
 *   and size.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   trak      TRAK atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 advanceVideoFrame(MP4HandleImp handle, trackAtom *trak)
{
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;
  if (!trak->mdia->minf->stbl->stsz)
    return -1;

  /* Are there frames (samples) left? */

  if (trak->mdia->minf->stbl->stsz->sampleCount > handle->videoSampleNum)
    handle->videoSampleNum++;
  else
    return -2;

  /* Find the size of the frame (sample) */

  if (resolveVideoSampleSize(handle, trak->mdia->minf->stbl->stsz) < 0)
    return -1;

  /* Find the offset of the frame (sample) */

  if (resolveVideoSampleOffset(handle, trak->mdia->minf->stbl) < 0)
    return -1;


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 resolveVideoSampleOffset(MP4HandleImp handle,
 *                                    sampleTableAtom *stbl)
 *
 * Description:
 *
 *   This function finds the offset of the current video sample.
 *   The result is stored in handle->videoFrameOffset.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   stbl      STBL atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 resolveVideoSampleOffset(MP4HandleImp handle, sampleTableAtom *stbl)
	{
	mp4_u32  chunk;            /* Current chunk number */
	mp4_u32  sample;           /* Number of samples before this run of chunks */
	mp4_u32  entry;            /* Current entry in sample to chunk */
	mp4_u32  chunksInThisRun;  /* Number of chunks in this run */
	mp4_u32  sampleNrInChunk = 0;	/* Sample number in the chunk */

	if (!stbl->stsc || stbl->stsc->entryCount == 0)
		{
		return -1;
		}
	
	if (!stbl->stco || stbl->stco->entryCount == 0)
		{
		return -1;
		}

	chunk = 0;
	sample = 0;
	entry = 0;

  for (;;)
		{
		/* Find how many chunks there are in this run */
		
		if (stbl->stsc->entryCount > entry + 1) /* Not last chunk run */
			{
			chunksInThisRun = stbl->stsc->firstChunk[entry + 1] - stbl->stsc->firstChunk[entry];
			}
		else
			{
			chunksInThisRun = stbl->stco->entryCount - chunk + 1;
			}

		if (handle->videoSampleNum <= chunksInThisRun * stbl->stsc->samplesPerChunk[entry] + sample)
			{
			chunk += (handle->videoSampleNum - sample + stbl->stsc->samplesPerChunk[entry] - 1) / stbl->stsc->samplesPerChunk[entry];
			sampleNrInChunk = (handle->videoSampleNum - sample + stbl->stsc->samplesPerChunk[entry] - 1) % stbl->stsc->samplesPerChunk[entry];
	  
			/* The following functions are needed for multiple sample entry support */
			handle->videoSampleEntryIndex = stbl->stsc->sampleDescriptionIndex[entry];

			break; /* We know the chunk number and sample number in chunk AND the sample entry index of the current sample*/
			}
		else
			{
			chunk += chunksInThisRun;
			sample += chunksInThisRun * stbl->stsc->samplesPerChunk[entry];
			}

	    entry++;
		}

	if (chunk > stbl->stco->entryCount)
		{
		return -1;
		}

	handle->videoFrameOffset = getChunkOffset(stbl, chunk - 1);

	if (sampleNrInChunk)
		{
		if (stbl->stsz->sampleSize)
			{
			handle->videoFrameOffset += stbl->stsz->sampleSize * sampleNrInChunk;
			}
		else
			{
			if (stbl->stsz->sampleCount == 0)
				{
				return -1;
				}
			while (sampleNrInChunk)
				{
				handle->videoFrameOffset += stbl->stsz->entrySize[handle->videoSampleNum - sampleNrInChunk - 1];
				sampleNrInChunk--;
				}
			}
		}
		
		
	//PRINT((_L("videoFrameOffset %Lu"), handle->videoFrameOffset));
	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 resolveVideoSampleSize(MP4HandleImp handle,
 *                                  sampleSizeAtom *stsz)
 *
 * Description:
 *
 *   This function finds the size of the current video sample.
 *   The result is stored in handle->videoFrameSize.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   stsz      STSZ atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 resolveVideoSampleSize(MP4HandleImp handle, sampleSizeAtom *stsz)
	{
	if (stsz->sampleSize)
		{
		handle->videoFrameSize = stsz->sampleSize;
		return 0;
		}

	if (stsz->sampleCount == 0)
		{
		return -1;
		}
	handle->videoFrameSize = stsz->entrySize[handle->videoSampleNum - 1];

	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 fetchVideoFrame(MP4HandleImp handle,
 *                           trackAtom *trak,
 *                           mp4_u8 *buffer,
 *                           mp4_u32 buffersize,
 *                           mp4_u32 *framesize,
 *                           mp4_u32 *timestamp,
 *                           mp4_bool *keyframe,
 *                           mp4_u32 *timestamp2)
 *
 * Description:
 *
 *   This function fetches one video frame from a file.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   trak         TRAK atom pointer
 *   buffer       Video frame is retuned here
 *   buffersize   Size of buffer
 *   framesize    Size of returned frame in bytes
 *   timestamp    Frame time in milliseconds (from the beginning of the
 *                presentation)
 *   keyframe     True if intra frame, false otherwise
 *   timestamp2   Frame time in timescale (from the beginning of the
 *                presentation)
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 fetchVideoFrame(MP4HandleImp handle,
                        trackAtom *trak,
                        mp4_u8 *buffer,
                        mp4_u32 buffersize,
                        mp4_u32 *framesize,
                        mp4_u32 *timestamp,
                        mp4_bool *keyframe,
                        mp4_u32 *timestamp2)
{
  mp4_i32 bytesread;


  if (!trak->mdia)
    return -1;

  if (handle->file) /* Input is in a file */
  {
    if (seekFileAbs(handle, handle->videoFrameOffset) != 0)
      return -4;
  }
  else /* Input is a stream */
  {
      if (handle->videoFrameOffset + handle->videoFrameSize <= getCumulativeBufferedBytes(handle))
      {
          handle->absPosition = handle->videoFrameOffset;
      }
      else
        return -3;
  }

  if (handle->videoFrameSize > buffersize)
  {
    *framesize = handle->videoFrameSize;
    return -2;
  }

  bytesread = readData(handle, buffer, handle->videoFrameSize);
  switch (bytesread)
  {
    case -1:
      return -1;
    case -2:
      return -4;
    case -10:
      return -3;
    default:
      break;
  }

  if (handle->file)
    if (handle->videoFrameOffset + handle->videoFrameSize - 1 > handle->lastAccessedPosInFile)
      handle->lastAccessedPosInFile = handle->videoFrameOffset + handle->videoFrameSize - 1;

  *framesize = handle->videoFrameSize;

  if (convertVideoSampleToTime(handle, trak->mdia, timestamp, timestamp2) < 0)
    return -1;

  if (isVideoFrameKeyFrame(handle, trak, keyframe) < 0)
    return -1;

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 fetchVideoFrameAsync(MP4HandleImp handle,
 *                           	  trackAtom *trak,
 *                           	  mp4_u8 *buffer,
 *                           	  mp4_u32 buffersize,
 *
 * Description:
 *
 *   This function fetches one video frame from a file asyncronously.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   trak         TRAK atom pointer
 *   buffer       Video frame is retuned here
 *   buffersize   Size of buffer
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 fetchVideoFrameAsync(MP4HandleImp handle,
                        trackAtom *trak,
                        mp4_u8 *buffer,
                        mp4_u32 *buffersize)
{
  if (trak)
	{
	if (!trak->mdia)
		return -1;
	}
  else
    {
	return -1;
    }
  
  if (handle->videoFrameSize > *buffersize)
  {
    *buffersize = handle->videoFrameSize;
    return -2;
  }
  
  if ( handle->asyncReader == NULL )
  	{
	TRAPD(error, handle->asyncReader = CFileAsyncParser::NewL( handle, (RFile64&)handle->rfile ));
	if ( error != KErrNone )
		{
		if (error == KErrNoMemory )
			{
			return MP4_OUT_OF_MEMORY;    		
			}
		else
			{
			return -1;
			}
		}  	
  	}
  
  return handle->asyncReader->ReadVideoFrame( buffer, handle->videoFrameOffset, handle->videoFrameSize);
}

/*
 * Function:
 *
 *   mp4_i32 isVideoFrameKeyFrame(MP4HandleImp handle,
 *                                trackAtom *trak,
 *                                mp4_bool *keyframe)
 *
 * Description:
 *
 *   This function determines if the current frame is a keyframe (intra)
 *   or not.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   trak         TRAK atom pointer
 *   keyframe     Has a value of MP4TRUE if current frame is a keyframe
 *                (intra) or MP4FALSE otherwise
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 isVideoFrameKeyFrame(MP4HandleImp handle, trackAtom *trak, mp4_bool *keyframe)
{
  mp4_u32  i;


  *keyframe = MP4FALSE;

  if (!trak->mdia)
    return -1;

  if (!trak->mdia->minf)
    return -1;

  if (!trak->mdia->minf->stbl)
    return -1;

  if (!trak->mdia->minf->stbl->stss)
  {
    *keyframe = MP4TRUE;

    return 0;
  }

  for (i = 0; i < trak->mdia->minf->stbl->stss->entryCount; i++)
  {
    if (trak->mdia->minf->stbl->stss->sampleNumber[i] == handle->videoSampleNum)
    {
      *keyframe = MP4TRUE;
      break;
    }
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 convertVideoSampleToTime(MP4HandleImp handle,
 *                                    mediaAtom *mdia,
 *                                    mp4_u32 *timestamp,
 *                                    mp4_u32 *timestamp2)
 *
 * Description:
 *
 *   This function converts a video sample to corresponding time.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   mdia         MDIA atom pointer
 *   timestamp    Time in milliseconds is returned here
 *   timestamp2   Time in timescale is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 convertVideoSampleToTime(MP4HandleImp handle,
                                 mediaAtom *mdia,
                                 mp4_u32 *timestamp,
                                 mp4_u32 *timestamp2)
	{
	mp4_u32      tmptime;
	mp4_double   tmptime2;
	mp4_u32      sample;
	mp4_u32      entry;

	if (!mdia->mdhd)
		{
		return -1;
		}
	if (!mdia->minf)
		{
		return -1;
		}
	if (!mdia->minf->stbl)
		{
		return -1;
		}
	if (!mdia->minf->stbl->stts)
		{
		return -1;
		}
	if (mdia->minf->stbl->stts->entryCount == 0)
		{
		return -1;
		}

	tmptime = 0;
	sample = 0;
	entry = 0;

	for (;;)
		{
		if (sample + mdia->minf->stbl->stts->sampleCount[entry] < handle->videoSampleNum)
			{
			sample += mdia->minf->stbl->stts->sampleCount[entry];
			tmptime += (mdia->minf->stbl->stts->sampleCount[entry] * mdia->minf->stbl->stts->sampleDelta[entry]);
			entry++;
			if (entry == mdia->minf->stbl->stts->entryCount)
				{
				return -1;
				}
			}
		else
			{
			tmptime += ((handle->videoSampleNum - sample - 1) * mdia->minf->stbl->stts->sampleDelta[entry]);
			break;
			}
		}
	
  	if (mdia->mdhd->timeScale == 0)
  		{
  		return -1;
  		}

  	if (timestamp2)
  		{
  		*timestamp2 = tmptime;
  		}

  	tmptime2 = (mp4_double)tmptime * (mp4_double)1000 / (mp4_double)mdia->mdhd->timeScale + (mp4_double)0.5;

  	*timestamp = (mp4_u32)tmptime2;

  	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 advanceAudioSample(MP4HandleImp handle,
 *                              trackAtom *trak)
 *
 * Description:
 *
 *   This function advances one audio sample and finds the sample
 *   offset and sample size.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   trak      TRAK atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 advanceAudioSample(MP4HandleImp handle,
                           trackAtom *trak)
{
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;
  if (!trak->mdia->minf->stbl->stsz)
    return -1;


  /* Are there samples left? */

  if (trak->mdia->minf->stbl->stsz->sampleCount > handle->audioSampleNum)
    handle->audioSampleNum++;
  else
    return -2;

  /* Find the size of the sample */

  if (resolveAudioSampleSize(handle, trak->mdia->minf->stbl->stsz) < 0)
    return -1;

  /* Find the offset of the sample */

  if (resolveAudioSampleOffset(handle, trak->mdia->minf->stbl) < 0)
    return -1;


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 resolveAudioSampleOffset(MP4HandleImp handle,
 *                                    sampleTableAtom *stbl)
 *
 * Description:
 *
 *   This function finds the offset of the current audio sample.
 *   The result is stored in handle->audioSampleOffset.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   stbl      STBL atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 resolveAudioSampleOffset(MP4HandleImp handle, sampleTableAtom *stbl)
{
  mp4_u32  chunk;            /* Current chunk number */
  mp4_u32  sample;            /* Number of samples before this run of chunks */
  mp4_u32  entry;            /* Current entry in sample to chunk */
  mp4_u32 chunksInThisRun;  /* Number of chunks in this run */
  mp4_u32  sampleNrInChunk;  /* Sample number in the chunk */


  if (!stbl->stsc || stbl->stsc->entryCount == 0)
    return -1;
  if (!stbl->stco || stbl->stco->entryCount == 0)
    return -1;

  chunk = 0;
  sample = 0;
  entry = 0;

  for (;;)
  {
    /* Find how many chunks there are in this run */

    if (stbl->stsc->entryCount > entry + 1) /* Not last chunk run */
    {
      chunksInThisRun = stbl->stsc->firstChunk[entry + 1] -
                        stbl->stsc->firstChunk[entry];
    }
    else
      chunksInThisRun = stbl->stco->entryCount - chunk + 1;


    if (handle->audioSampleNum <= chunksInThisRun * stbl->stsc->samplesPerChunk[entry] + sample)
    {
      chunk += (handle->audioSampleNum - sample + stbl->stsc->samplesPerChunk[entry] - 1) / stbl->stsc->samplesPerChunk[entry];
      sampleNrInChunk = (handle->audioSampleNum - sample + stbl->stsc->samplesPerChunk[entry] - 1) % stbl->stsc->samplesPerChunk[entry];

	  /* The following functions are needed for multiple sample entry support */
	  handle->audioSampleEntryIndex = stbl->stsc->sampleDescriptionIndex[entry];

      break; /* We know the chunk number and sample number in chunk AND the sample entry index of the current sample*/
    }
    else
    {
      chunk += chunksInThisRun;
      sample += chunksInThisRun * stbl->stsc->samplesPerChunk[entry];
    }

    entry++;
  }

  if (chunk > stbl->stco->entryCount)
    return -1;

  handle->audioSampleOffset = getChunkOffset(stbl, chunk - 1);

  	if (sampleNrInChunk)
  		{
  		if (stbl->stsz->sampleSize)
  			{
  			handle->audioSampleOffset += stbl->stsz->sampleSize * sampleNrInChunk;
  			}
  		else
  			{
  			if (stbl->stsz->sampleCount == 0)
  				{
  				// ensure there are entries in the entrySize array
  				return -1;
  				}
    
  			while (sampleNrInChunk)
  				{
  				handle->audioSampleOffset += stbl->stsz->entrySize[handle->audioSampleNum - sampleNrInChunk - 1];
  				sampleNrInChunk--;
  				}
  			}
  		}
  		
  //PRINT((_L("audioSampleOffset %Lu"), handle->audioSampleOffset));
  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 resolveAudioSampleSize(MP4HandleImp handle,
 *                                  sampleSizeAtom *stsz)
 *
 * Description:
 *
 *   This function finds the size of the current audio sample.
 *   The result is stored in handle->audioSampleSize.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   stsz      STSZ atom pointer
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 resolveAudioSampleSize(MP4HandleImp handle, sampleSizeAtom *stsz)
	{
	if (stsz->sampleSize)
		{
		handle->audioSampleSize = stsz->sampleSize;
		return 0;
		}

	if (stsz->sampleCount == 0)
		{
		return -1;
		}
	handle->audioSampleSize = stsz->entrySize[handle->audioSampleNum - 1];

	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 fetchAudioSample(MP4HandleImp handle,
 *                            trackAtom *trak,
 *                            mp4_u8 *buffer,
 *                            mp4_u32 buffersize,
 *                            mp4_u32 *framesize,
 *                            mp4_u32 *timestamp,
 *                            mp4_u32 *returnedframes,
 *                            mp4_u32 *timestamp2)
 *
 * Description:
 *
 *   This function fetches one audio sample from a file.
 *
 *   Note: returnedframes may differ from the correct value when accessing
 *         the last audio sample.
 *
 * Parameters:
 *
 *   handle           MP4 library handle
 *   trak             TRAK atom pointer
 *   buffer           Audio frame is retuned here
 *   buffersize       Size of buffer
 *   framesize        Size of returned frame in bytes
 *   timestamp        Frame time in milliseconds (from the beginning of the
 *                    presentation)
 *   returnedframes   Number of frames returned, of 0 if not known
 *   timestamp2       Frame time in timescale (from the beginning of the
 *                    presentation)
 *
 * Return value:
 *
 *   0                Success
 *   Negative         Error
 *
 */
mp4_i32 fetchAudioSample(MP4HandleImp handle,
                         trackAtom *trak,
                         mp4_u8 *buffer,
                         mp4_u32 buffersize,
                         mp4_u32 *framesize,
                         mp4_u32 *timestamp,
                         mp4_u32 *returnedframes,
                         mp4_u32 *timestamp2)
{
  mp4_i32 bytesread;
  mp4_i32 frameLength;
  mp4_u32 numOfFrames;
  mp4_u8 *framepointer;
  mp4_u32 rawAmrFrameLength[16] = {13,14,16,18,20,21,27,32,6,0,0,0,0,0,0,1};

  if (!trak->mdia)
    return -1;

  if (handle->file) /* Input is in a file */
  {
    if (seekFileAbs(handle, handle->audioSampleOffset) != 0)
      return -4;
  }
  else /* Input is a stream */
  {
      if (handle->audioSampleOffset + handle->audioSampleSize <= getCumulativeBufferedBytes(handle))
      {
          handle->absPosition = handle->audioSampleOffset;
      }
      else
        return -3;
  }

  if (handle->audioSampleSize > buffersize)
  {
    *framesize = handle->audioSampleSize;
    return -2;
  }

  bytesread = readData(handle, buffer, handle->audioSampleSize);
  switch (bytesread)
  {
    case -1:
      return -1;
    case -2:
      return -4;
    case -10:
      return -3;
    default:
      break;
  }

  if (handle->file)
    if (handle->audioSampleOffset + handle->audioSampleSize - 1 > handle->lastAccessedPosInFile)
      handle->lastAccessedPosInFile = handle->audioSampleOffset + handle->audioSampleSize - 1;

  *framesize = handle->audioSampleSize;
  if (convertAudioSampleToTime(handle, trak->mdia, timestamp, timestamp2) < 0)
    return -1;

  *returnedframes = 0;

  /* AMR */
  if (trak->mdia->minf)
    if (trak->mdia->minf->stbl)
      if (trak->mdia->minf->stbl->stsd)
        if (handle->type & MP4_TYPE_AMR_NB)
        {
            framepointer = buffer;
            numOfFrames = 0;
            while ( bytesread > 0 )
            {
                frameLength = rawAmrFrameLength[(TInt)(((*framepointer) & 0x78) >> 3)];
                if ( frameLength == 0)
                {
                    return -4;
                }
                bytesread -= frameLength;
                framepointer += frameLength;
                numOfFrames++;
            }
            *returnedframes = numOfFrames;

		  /* Return the number of sample entries listed for this particular sample entry index 
          if (trak->mdia->minf->stbl->stsd->samr[handle->audioSampleEntryIndex - 1])
            if (trak->mdia->minf->stbl->stsd->samr[handle->audioSampleEntryIndex - 1]->damr)
              *returnedframes = trak->mdia->minf->stbl->stsd->samr[handle->audioSampleEntryIndex - 1]->damr->framesPerSample;*/
        }
        else if (handle->type & MP4_TYPE_AMR_WB)
        {
		  /* Return the number of sample entries listed for this particular sample entry index */
          if (trak->mdia->minf->stbl->stsd->sawb[handle->audioSampleEntryIndex - 1])
            if (trak->mdia->minf->stbl->stsd->sawb[handle->audioSampleEntryIndex - 1]->damr)
              *returnedframes = trak->mdia->minf->stbl->stsd->sawb[handle->audioSampleEntryIndex - 1]->damr->framesPerSample;
        }
        else
        {
        }

  /* MPEG-4 audio */
  if (trak->mdia->minf)
    if (trak->mdia->minf->stbl)
      if (trak->mdia->minf->stbl->stsd)
        if (trak->mdia->minf->stbl->stsd->mp4a[handle->audioSampleEntryIndex - 1])
          *returnedframes = 1;

  /* QCELP 13K as QCELPSampleEntry*/
  if (trak->mdia->minf)
    if (trak->mdia->minf->stbl)
      if (trak->mdia->minf->stbl->stsd)
        if ((handle->type & MP4_TYPE_QCELP_13K) && (!handle->qcelpStoredAsMPEGAudio))
        {
		  /* Return the number of sample entries listed for this particular sample entry index */
          if (trak->mdia->minf->stbl->stsd->sqcp[handle->audioSampleEntryIndex - 1])
            if (trak->mdia->minf->stbl->stsd->sqcp[handle->audioSampleEntryIndex - 1]->dqcp)
              *returnedframes = trak->mdia->minf->stbl->stsd->sqcp[handle->audioSampleEntryIndex - 1]->dqcp->framesPerSample;
        }

  /* QCELP 13K as MPEG-4 audio */
  if (trak->mdia->minf)
    if (trak->mdia->minf->stbl)
      if (trak->mdia->minf->stbl->stsd)
        if (trak->mdia->minf->stbl->stsd->mp4a[handle->audioSampleEntryIndex - 1])
          *returnedframes = 1;

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 fetchAudioSampleAsync(MP4HandleImp handle,
 *                            trackAtom *trak,
 *                            mp4_u8 *buffer,
 *                            mp4_u32 buffersize,
 *
 * Description:
 *
 *   This function fetches one audio sample from a file asyncronously.
 *
 * Parameters:
 *
 *   handle           MP4 library handle
 *   trak             TRAK atom pointer
 *   buffer           Audio frame is retuned here
 *   buffersize       Size of buffer
 *
 * Return value:
 *
 *   0                Success
 *   Negative         Error
 *
 */
mp4_i32 fetchAudioSampleAsync(MP4HandleImp handle,
                         	  trackAtom *trak,
                         	  mp4_u8 *buffer,
                         	  mp4_u32 *buffersize)
{
  if (trak)
	{
	if (!trak->mdia)
		return -1;
	}
  else
    {
	return -1;
    }
  
  if (handle->audioSampleSize > *buffersize)
		{
		*buffersize = handle->audioSampleSize;
		return -2;
		}
  
  if (!handle->file) // Other input than file is not supported
	  {
	  return -1;
	  }
  
  if ( handle->asyncReader == NULL )
  	{
	TRAPD(error, handle->asyncReader = CFileAsyncParser::NewL( handle, (RFile64&)handle->rfile ));
	if ( error != KErrNone )
		{
		if (error == KErrNoMemory )
			{
			return MP4_OUT_OF_MEMORY;    		
			}
		else
			{
			return -1;
			}
		}  	
  	}
    
  return handle->asyncReader->ReadAudioFrames( buffer, handle->audioSampleOffset, handle->audioSampleSize);
}

/*
 * Function:
 *
 *   mp4_i32 convertAudioSampleToTime(MP4HandleImp handle,
 *                                    mediaAtom *mdia,
 *                                    mp4_u32 *timestamp,
 *                                    mp4_u32 *timestamp2)
 *
 * Description:
 *
 *   This function converts an audio sample to corresponding time.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   mdia         MDIA atom pointer
 *   timestamp    Time in milliseconds is returned here
 *   timestamp2   Time in timescale is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 convertAudioSampleToTime(MP4HandleImp handle,
                                 mediaAtom *mdia,
                                 mp4_u32 *timestamp,
                                 mp4_u32 *timestamp2)
	{
	mp4_u32      tmptime;
	mp4_double  tmptime2;
	mp4_u32      sample;
	mp4_u32      entry;

  	if (!mdia->mdhd)
  		{
  		return -1;
  		}
  	if (!mdia->minf)
  		{
  		return -1;
  		}
  	if (!mdia->minf->stbl)
  		{
  		return -1;
  		}
  	if (!mdia->minf->stbl->stts)
  		{
  		return -1;
  		}
  	if (mdia->minf->stbl->stts->entryCount == 0)
  		{
  		return -1;
  		}

  	tmptime = 0;
  	sample = 0;
  	entry = 0;

  	for (;;)
  		{
  		if (sample + mdia->minf->stbl->stts->sampleCount[entry] < handle->audioSampleNum)
  			{
  			sample += mdia->minf->stbl->stts->sampleCount[entry];
  			tmptime += mdia->minf->stbl->stts->sampleCount[entry] *
                 	mdia->minf->stbl->stts->sampleDelta[entry];
  			entry++;
  			
  			if (entry == mdia->minf->stbl->stts->entryCount)
  				return -1;
  			}
  		else
  			{
  			tmptime += (handle->audioSampleNum - sample - 1) * mdia->minf->stbl->stts->sampleDelta[entry];
  			break;
  			}
  		}

  	if (mdia->mdhd->timeScale == 0)
  		{
  		return -1;
  		}

  	if (timestamp2)
  		{
  		*timestamp2 = tmptime;
  		}

  	tmptime2 = (mp4_double)tmptime * (mp4_double)1000 / (mp4_double)mdia->mdhd->timeScale + (mp4_double)0.5;

  	*timestamp = (mp4_u32)tmptime2;

  	return 0;
	}


/*
 * Function:
 *
 *   mp4_i32 convertTimeToSample(MP4HandleImp handle,
 *                               trackAtom *trak,
 *                               mp4_u32 position,
 *                               mp4_u32 *sample)
 *
 * Description:
 *
 *   This function converts time to corresponding sample number.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   trak         trackAtom pointer
 *   position     Time in milliseconds
 *   sample       Sample number is returned here
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 convertTimeToSample(MP4HandleImp handle,
                            trackAtom *trak,
                            mp4_u32 position,
                            mp4_u32 *sample)
{
  mp4_u32 pos;      /* Target position in media timescale */
  mp4_u32 tmppos;   /* Temporary position counter */
  mp4_u32 i;


  if (!handle)
    return -1;
  if (!trak)
    return -1;
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->mdhd)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;
  if (!trak->mdia->minf->stbl->stts)
    return -1;
  if (trak->mdia->minf->stbl->stts->entryCount == 0)
    return -1;

  *sample = 1;
  tmppos = 0;

  pos = (mp4_u32)((mp4_double)position / (mp4_double)1000 * (mp4_double)trak->mdia->mdhd->timeScale + (mp4_double)0.5);

  for (i = 0; i < trak->mdia->minf->stbl->stts->entryCount; i++)
  {
    if (pos >= (tmppos + trak->mdia->minf->stbl->stts->sampleCount[i] *
                         trak->mdia->minf->stbl->stts->sampleDelta[i]))
    {
      *sample += trak->mdia->minf->stbl->stts->sampleCount[i];
      tmppos += (trak->mdia->minf->stbl->stts->sampleCount[i] *
                 trak->mdia->minf->stbl->stts->sampleDelta[i]);

      if (i == trak->mdia->minf->stbl->stts->entryCount - 1) /* Last entry */
        *sample = *sample - 1;
    }
    else
    {
      if (trak->mdia->minf->stbl->stts->sampleDelta[i] == 0)
        return -1;

      *sample += ((pos - tmppos) / trak->mdia->minf->stbl->stts->sampleDelta[i]);

      break;
    }
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 goToVideoSample(MP4HandleImp handle,
 *                           trackAtom *trak,
 *                           mp4_u32 sample)
 *
 * Description:
 *
 *   This function moves to video sample indicated by sample and finds the
 *   sample offset and sample size.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   trak      TRAK atom pointer
 *   sample    Sample to go to
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 goToVideoSample(MP4HandleImp handle,
                        trackAtom *trak,
                        mp4_u32 sample)
{
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;
  if (!trak->mdia->minf->stbl->stsz)
    return -1;


  /* Is the sample valid? */

  if (sample <= trak->mdia->minf->stbl->stsz->sampleCount)
    handle->videoSampleNum = sample;
  else
    return -1;

  /* Find the size of the sample */

  if (resolveVideoSampleSize(handle, trak->mdia->minf->stbl->stsz) < 0)
    return -1;

  /* Find the offset of the sample */

  if (resolveVideoSampleOffset(handle, trak->mdia->minf->stbl) < 0)
    return -1;


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 goToAudioSample(MP4HandleImp handle,
 *                           trackAtom *trak,
 *                           mp4_u32 sample)
 *
 * Description:
 *
 *   This function moves to audio sample indicated by sample and finds the
 *   sample offset and sample size.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   trak      TRAK atom pointer
 *   sample    Sample to go to
 *
 * Return value:
 *
 *   0         Success
 *   Negative  Error
 *
 */
mp4_i32 goToAudioSample(MP4HandleImp handle,
                        trackAtom *trak,
                        mp4_u32 sample)
{
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;
  if (!trak->mdia->minf->stbl->stsz)
    return -1;


  /* Is the sample valid? */

  if (sample <= trak->mdia->minf->stbl->stsz->sampleCount)
    handle->audioSampleNum = sample;
  else
    return -1;

  /* Find the size of the sample */

  if (resolveAudioSampleSize(handle, trak->mdia->minf->stbl->stsz) < 0)
    return -1;

  /* Find the offset of the sample */

  if (resolveAudioSampleOffset(handle, trak->mdia->minf->stbl) < 0)
    return -1;


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 findVideoKeyFrame(MP4HandleImp handle,
 *                             trackAtom *trak,
 *                             mp4_u32 sample,
 *                             mp4_u32 *newsample)
 *
 * Description:
 *
 *   This function finds the video keyframe that is identical to or precedes
 *   the sample indicated by sample.
 *
 * Parameters:
 *
 *   handle       MP4 library handle
 *   trak         TRAK atom pointer
 *   sample       Sample number
 *   newsample    Sample number of found keyframe
 *
 * Return value:
 *
 *   0            Success
 *   Negative     Error
 *
 */
mp4_i32 findVideoKeyFrame(MP4HandleImp handle,
                          trackAtom *trak,
                          mp4_u32 sample,
                          mp4_u32 *newsample)
{
  mp4_i32  i;


  if (!handle)
    return -1;
  if (!trak->mdia)
    return -1;
  if (!trak->mdia->minf)
    return -1;
  if (!trak->mdia->minf->stbl)
    return -1;

  if (!trak->mdia->minf->stbl->stss) /* No sync sample atom => all samples are
                                        random access points */
  {
    *newsample = sample;

    return 0;
  }
  *newsample = 0;

  for (i = trak->mdia->minf->stbl->stss->entryCount - 1; i >= 0; i--)
  {
    if (sample >= trak->mdia->minf->stbl->stss->sampleNumber[i])
    {
      *newsample = trak->mdia->minf->stbl->stss->sampleNumber[i];
      break;
    }
  }

  if (*newsample == 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 readAVCC(MP4HandleImp handle,
 *                    avcConfigurationAtom *avcc)
 *
 * Description:
 *
 *   This function parses one avcc atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   avcc               avcC pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readAVCC(MP4HandleImp handle, avcConfigurationAtom *avcc)
{
	mp4_i32 bytesread;
	mp4_i32 totalbytesread = 0;

	avcc->avcConfigSize = 0;

	if ((avcc->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
		return -100;

	bytesread = readAtomHeader(handle, avcc->atomhdr);
	if (bytesread < 0)
		return -1;
	totalbytesread +=bytesread;

	if (avcc->atomhdr->type != ATOMTYPE_AVCC)
		return -1;

    /* read the avcDecoderConfigurationRecord */
    if  (avcc->atomhdr->size != 1)
    	avcc->avcConfigSize = avcc->atomhdr->size - 8;
    else
    	avcc->avcConfigSize = (mp4_u32)(I64INT(avcc->atomhdr->largeSize) - 16);

   	avcc->avcConfig = (mp4_u8 *)mp4malloc(avcc->avcConfigSize);
	if (avcc->avcConfig == 0)
		return -100;
	
	bytesread = readData(handle, avcc->avcConfig, avcc->avcConfigSize );
    
	if (bytesread < 0)
		return -1;
	totalbytesread +=bytesread;
	return totalbytesread;
}


/*
 * Function:
 *
 *   mp4_i32 readBTRT(MP4HandleImp handle,
 *                    mpeg4BitrateAtom *btrt)
 *
 * Description:
 *
 *   This function parses one btrt atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   btrt					btrt pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readBTRT(MP4HandleImp handle, mpeg4BitrateAtom *btrt)
{
	mp4_i32 bytesread;
	mp4_i32 totalbytesread = 0;


	if ((btrt->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
		return -100;

	bytesread = readAtomHeader(handle, btrt->atomhdr);
	if (bytesread < 0)
		return -1;
	totalbytesread +=bytesread;

	if (btrt->atomhdr->type != ATOMTYPE_BTRT)
		return -1;

    /* read the mpeg4BitrateAtom */
   bytesread = readData(handle, handle->buf, 4);
   if (bytesread < 0)
	   return -1;
   btrt->bufferSizeDB = u32endian(*((mp4_u32 *)handle->buf));
   totalbytesread +=bytesread;

   bytesread = readData(handle, handle->buf, 4);
   if (bytesread < 0)
	   return -1;
   btrt->maxBitRate = u32endian(*((mp4_u32 *)handle->buf));
   totalbytesread +=bytesread;

   bytesread = readData(handle, handle->buf, 4);
   if (bytesread < 0)
	   return -1;
   btrt->avgBitrate = u32endian(*((mp4_u32 *)handle->buf));
   totalbytesread +=bytesread;

	return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 readM4DS(MP4HandleImp handle,
 *                    mpeg4ExtensionDescriptorsAtom *m4ds)
 *
 * Description:
 *
 *   This function parses one m4ds atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   m4ds               m4ds pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readM4DS(MP4HandleImp handle, mpeg4ExtensionDescriptorsAtom *m4ds)
{
	mp4_i32 bytesread;
	mp4_i32 totalbytesread = 0;
	mp4_u32 i;

	m4ds->descrSize = 0;

	if ((m4ds->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
		return -100;

	bytesread = readAtomHeader(handle, m4ds->atomhdr);
	if (bytesread < 0)
		return -1;
	totalbytesread +=bytesread;

	if (m4ds->atomhdr->type != ATOMTYPE_M4DS)
		return -1;

    /* read the avcDecoderConfigurationRecord */
	if  (m4ds->atomhdr->size != 1)
		bytesread = readData(handle, handle->buf, m4ds->atomhdr->size - 8);
	else
        bytesread = readData(handle, handle->buf, (mp4_u32)(I64INT(m4ds->atomhdr->largeSize) - 16) );

	if (bytesread < 0)
		return -1;
	m4ds->descrSize = bytesread;
	m4ds->descr = (mp4_u8 *)mp4malloc(m4ds->descrSize * sizeof(mp4_u8));
	
	/* copy the mpeg4ExtensionDescriptors from the temp. buffer */
	for(i = 0; i <  m4ds->descrSize; i++)
		m4ds->descr[i] = handle->buf[i];
	
	totalbytesread +=bytesread;

	return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 readAVC1(MP4HandleImp handle,
 *                    avcSampleEntry *avc1)
 *
 * Description:
 *
 *   This function parses one avc1 atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   avc1               avc1 pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readAVC1(MP4HandleImp handle, avcSampleEntry *avc1)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;

  if ((avc1->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, avc1->atomhdr);

  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (avc1->atomhdr->type != ATOMTYPE_AVC1)
    return -1;

  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  avc1->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  avc1->width = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  avc1->height = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 50);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  /* Check for the present atoms */
  while ((mp4_u32)totalbytesread < avc1->atomhdr->size)
  {
    mp4_u32 type;

    if (peekData(handle, handle->buf, 8) < 0)
      return -1;

    type = u32endian(*((mp4_u32 *)(handle->buf+4)));

    switch (type)
    {
    case ATOMTYPE_AVCC:

		if (avc1->avcc) /* avcC has already been read, more than one is not allowed. */
			return -1;

		if ((avc1->avcc = (avcConfigurationAtom *)mp4malloc(sizeof(avcConfigurationAtom))) == NULL)
			return -100;

		bytesread = readAVCC(handle, avc1->avcc);  
		if(bytesread < 0)
			return -1;
		totalbytesread +=bytesread;
  	    break;

    case ATOMTYPE_BTRT:

		if (avc1->btrt) /* btrt has already been read, more than one is not allowed. */
			return -1;

		if ((avc1->btrt = (mpeg4BitrateAtom *)mp4malloc(sizeof(mpeg4BitrateAtom))) == NULL)
			return -100;

		bytesread = readBTRT(handle, avc1->btrt);  
		if(bytesread < 0)
			return -1;
		totalbytesread +=bytesread;
	    break;

    case ATOMTYPE_M4DS:

		if (avc1->m4ds) /* m4ds has already been read, more than one is not allowed. */
			return -1;

		if ((avc1->m4ds = (mpeg4ExtensionDescriptorsAtom *)mp4malloc(sizeof(mpeg4ExtensionDescriptorsAtom))) == NULL)
			return -100;

		bytesread = readM4DS(handle, avc1->m4ds);  
		if(bytesread < 0)
			return -1;
		totalbytesread +=bytesread;
	    break;
	 
	 default:

        bytesread = readUnknown(handle);
        if (bytesread < 0)
            return -1;
        totalbytesread += bytesread;

        break;

	}
  }

  return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 freeAVCC(avcConfigurationAtom *avcc)
 *
 * Description:
 *
 *   This function frees memory for avcc atom.
 *
 * Parameters:
 *
 *   avcc       avcc atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeAVCC(avcConfigurationAtom *avcc)
{
  if (avcc)
  {
    if (freeAtomHeader(avcc->atomhdr) < 0)
      return -1;
	if(avcc->avcConfig)
		mp4free(avcc->avcConfig);

    mp4free(avcc);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 freeBTRT(mpeg4BitrateAtom *btrt)
 *
 * Description:
 *
 *   This function frees memory for btrt atom.
 *
 * Parameters:
 *
 *   btrt       btrt atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeBTRT(mpeg4BitrateAtom *btrt)
{
  if (btrt)
  {
    if (freeAtomHeader(btrt->atomhdr) < 0)
      return -1;

    mp4free(btrt);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 freeM4DS(mpeg4ExtensionDescriptorsAtom *m4ds)
 *
 * Description:
 *
 *   This function frees memory for m4ds atom.
 *
 * Parameters:
 *
 *   m4ds       m4ds atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeM4DS(mpeg4ExtensionDescriptorsAtom *m4ds)
{
  if (m4ds)
  {
    if (freeAtomHeader(m4ds->atomhdr) < 0)
      return -1;
	if(m4ds->descr)
		mp4free(m4ds->descr);

    mp4free(m4ds);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 freeAVC1(avcSampleEntry *avc1)
 *
 * Description:
 *
 *   This function frees memory for avc1 atom.
 *
 * Parameters:
 *
 *   avc1       avc1 atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeAVC1(avcSampleEntry *avc1)
{
  if (avc1)
  {
    if (freeAtomHeader(avc1->atomhdr) < 0)
      return -1;
    if (freeAVCC(avc1->avcc) < 0)
      return -1;
	if (freeBTRT(avc1->btrt) < 0)
		return -1;
	if (freeM4DS(avc1->m4ds) < 0)
		return -1;

    mp4free(avc1);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 readSQCP(MP4HandleImp handle,
 *                    qcelpSampleEntry *sqcp)
 *
 * Description:
 *
 *   This function parses one SQCP atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   sqcp               SQCP pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readSQCP(MP4HandleImp handle, qcelpSampleEntry *sqcp)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((sqcp->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, sqcp->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (sqcp->atomhdr->type != ATOMTYPE_SQCP)
    return -1;


  bytesread = discardData(handle, 6);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  sqcp->dataReferenceIndex = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 16);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 2);
  if (bytesread < 0)
    return -1;
  sqcp->timeScale = u16endian(*((mp4_u16 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = discardData(handle, 2);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if ((sqcp->dqcp = (qcelpDecSpecStruc *)mp4malloc(sizeof(qcelpDecSpecStruc))) == NULL)
    return -100;

  bytesread = readDQCP(handle, sqcp->dqcp);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;
  
  if ( totalbytesread < sqcp->atomhdr->size )
  	{
    bytesread = discardData(handle, sqcp->atomhdr->size - totalbytesread );
  	if (bytesread < 0)
  		{
    	return -1;	  		
  		}
  	totalbytesread += bytesread;
  	}  

  return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 freeSQCP(qcelpSampleEntry *sqcp)
 *
 * Description:
 *
 *   This function frees memory for SQCP atom.
 *
 * Parameters:
 *
 *   sqcp       SQCP atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeSQCP(qcelpSampleEntry *sqcp)
{
  if (sqcp)
  {
    if (freeAtomHeader(sqcp->atomhdr) < 0)
      return -1;
    if (freeDQCP(sqcp->dqcp) < 0)
      return -1;

    mp4free(sqcp);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 readDQCP(MP4HandleImp handle,
 *                    qcelpDecSpecStruc *dqcp)
 *
 * Description:
 *
 *   This function parses one DQCP atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   dqcp               DQCP pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */
mp4_i32 readDQCP(MP4HandleImp handle, qcelpDecSpecStruc *dqcp)
{
  mp4_i32 bytesread;
  mp4_i32 totalbytesread = 0;


  if ((dqcp->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, dqcp->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (dqcp->atomhdr->type != ATOMTYPE_DQCP)
    return -1;


  bytesread = readData(handle, handle->buf, 4);
  if (bytesread < 0)
    return -1;
  dqcp->vendor = u32endian(*((mp4_u32 *)handle->buf));
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  dqcp->decoderVersion = handle->buf[0];
  totalbytesread += bytesread;

  bytesread = readData(handle, handle->buf, 1);
  if (bytesread < 0)
    return -1;
  dqcp->framesPerSample = handle->buf[0];
  totalbytesread += bytesread;

  return totalbytesread;
}

/*
 * Function:
 *
 *   mp4_i32 freeDQCP(qcelpDecSpecStruc *dqcp)
 *
 * Description:
 *
 *   This function frees memory for DQCP atom.
 *
 * Parameters:
 *
 *   damr       DQCP atom pointer
 *
 * Return value:
 *
 *   0          Success
 *   Negative   Error
 *
 */
mp4_i32 freeDQCP(qcelpDecSpecStruc *dqcp)
{
  if (dqcp)
  {
    if (freeAtomHeader(dqcp->atomhdr) < 0)
      return -1;

    mp4free(dqcp);
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 readSDTP(MP4HandleImp handle, sampleDependencyAtom *sdtp, 
 * 				      mp4_i32 sample_count)
 *
 * Description:
 *
 *   This function parses one SDTP atom.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   sdtp               SDTP atom pointer
 *
 * Return value:
 *
 *   Negative integer   Error
 *   >= 0               Success. Value tells how many bytes were read.
 *
 */

mp4_i32 readSDTP(MP4HandleImp handle, 
				 sampleDependencyAtom *sdtp, 
				 mp4_i32 sample_count)
{
  mp4_i32 bytesread = 0;
  mp4_i32 totalbytesread = 0;
  mp4_u32 i = 0;

  if ((sdtp->atomhdr = (atomHeader *)mp4malloc(sizeof(atomHeader))) == NULL)
    return -100;

  bytesread = readAtomHeader(handle, sdtp->atomhdr);
  if (bytesread < 0)
    return -1;
  totalbytesread += bytesread;

  if (sdtp->atomhdr->type != ATOMTYPE_SDTP)
    return -1;
  
  bytesread = readData(handle, handle->buf, 1); //Version must be 0
  if (bytesread < 0 || handle->buf[0] != 0)
    return -1;
  totalbytesread += bytesread;
  bytesread = readData(handle, handle->buf, 3); // Flags must be 0
  if (bytesread < 0 || handle->buf[0] != 0 || handle->buf[1] != 0 || handle->buf[2] != 0)
    return -1;
  totalbytesread += bytesread;

  // sample_count == (size_of_atom - 12) ???  12 = size + 'sdtp' + ver + flags = 4 + 4 + 1 + 3 ???
  //
  // sample_count is taken from the sample_count in the Sample Size Box ('stsz') or
  // Compact Sample Size Box (‘stz2’).
  if (sample_count != sdtp->atomhdr->size-12 || sample_count < 0)
	  {
	  // as each byte consititue one entry in the table, the number of remaining bytes in this
	  // atom should match the sample count.  If not, the file is corrupted.
	  return -1;
	  }
  
  if ((sdtp->dep = (sampleDependency *)mp4malloc(sample_count * sizeof(sampleDependency))) == NULL)
    return -100;
  
  for(i=0;i<sample_count;i++)
  {
    bytesread = readData(handle, handle->buf, 1);
    if (bytesread < 0)
      return -1;
    
    sdtp->dep[i].sDependsOn = (handle->buf[0] >> 4) & 0x03; // value from 0 to 3
    sdtp->dep[i].sIsDependentOn = (handle->buf[0] >> 2) & 0x03; // value from 0 to 3
    sdtp->dep[i].sHasRedundancy = handle->buf[0] & 0x03; // value from 0 to 3
    totalbytesread += bytesread;
  }

  sdtp->sampleCount = sample_count;
  return totalbytesread;	
}

/*
 * Function:
 *
 *   mp4_i32 readBoxHeader(MP4HandleImp handle, mp4_i64* size, mp4_u32* type)
 * 				      
 *
 * Description:
 *
 *   This function parses the size and type information for a box. 
 *   Taking into account largesize if needed.
 *
 * Parameters:
 *
 *   handle             MP4 library handle
 *   size               Size of box is returned here
 *   type               Type of the box is returned here
 * Return value:
 *
 *   Negative integer   Error
 *   0               Success.
 *
 */
mp4_i32 readBoxHeader(MP4HandleImp handle, mp4_i64* size, mp4_u32* type)
{
  if (peekData(handle, handle->buf, 8) < 0)
    return -1;

  *size = u32endian(*((mp4_u32 *)handle->buf));
  *type = u32endian(*((mp4_u32 *)(handle->buf+4)));

  if (*size == 1)
  {
    if (peekData(handle, handle->buf, 16) < 0)
      return -1;
    
    *size = u64endian(*((mp4_u64 *)(handle->buf+8)));
  }

  return 0;
}

/*
 */

mp4_i64 getChunkOffset(sampleTableAtom *stbl, mp4_u32 index)
{
  if (stbl->is32BitOffsets)
  	return (mp4_i64)stbl->stco->chunkOffset[index];
  else
    return stbl->stco64->chunkOffset[index];
}

// End of File