mmplugins/lib3gp/impl/src/compose.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 17 Sep 2010 08:37:57 +0300
changeset 54 b68f3e90dca1
parent 0 40261b775718
permissions -rw-r--r--
Revision: 201037 Kit: 201037

// 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 "mp4file.h"
#include "mp4endian.h"
#include "mp4compose.h"
#include "mp4currenttime.h"
#include "mp4utils.h"

#define MP4_INT_MAX		KMaxTInt32	
#define MDAT_HEADER_SIZE 16

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


inline void updateChunkOffset(sampleTable *st, mp4_i32 index, mp4_i64 value) 
{
  if (value > MP4_INT_MAX) 
    st->stcoNeed64Bits = ETrue; 
  
  st->stcoChunkOffset[index] = value;
}

/* must be called after determineAudioTrakMetaDataSize and determineVideoTrakMetaDataSize */
size_t mvhdAtomSize(MP4HandleImp handle)
{
  if (handle->videoDuration > MP4_INT_MAX || handle->audioDuration > MP4_INT_MAX)
  {
    return 120;
  }
  else
  {
    return 108;
  }
}



/* helper functions */
mp4_i32 formatMdatHeader(mp4_u8 *buffer, mp4_i64 size);

/*
 * Function:
 *
 *   mp4_i32 updateVideoMetaData(MP4HandleImp handle,
 *                               mp4_u32 size,
 *                               mp4_u32 duration)
 *
 * Description:
 *
 *   This function updates sample table atom data.
 *
 *   One call of this function will generate one chunk in the MP4 file.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   size      Size of video frame to insert
 *   duration  Duration of the video frame (in media timescale)
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateVideoMetaData(MP4HandleImp handle, mp4_u32 size, mp4_u32 duration, mp4_bool keyframe)
{
  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (handle->metaDataBlocks == BLOCK_LIMIT)
    {
      /* Write metadata to temporary files */

      if (writeMetaDataTmp(handle) < 0)
        return -1;
    }

    handle->metaDataBlocks++;
  }

  handle->videoSampleTable->currentChunk++;

  if (updateDecodingTimeToSample(handle, handle->videoSampleTable, duration) < 0)
    return -1;

  if (updateSampleSize(handle, handle->videoSampleTable, size) < 0)
    return -1;

  if (updateSampleToChunk(handle->videoSampleTable) < 0)
    return -1;

  if (updateChunkOffset(handle, handle->videoSampleTable) < 0)
    return -1;

  if (keyframe)
    if (updateSyncSample(handle, handle->videoSampleTable) < 0)
      return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateAudioMetaData(MP4HandleImp handle,
 *                               mp4_u32 size,
 *                               mp4_u32 numberofframes)
 *
 * Description:
 *
 *   This function updates sample table atom data.
 *
 *   One call of this function will generate one chunk in the MP4 file.
 *
 * Parameters:
 *
 *   handle          MP4 library handle
 *   size            Size of video frame to insert
 *   duration        Duration of audio frames (in timescale,
 *                   see MP4ComposeAddAudioDescription)
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateAudioMetaData(MP4HandleImp handle, mp4_u32 size, mp4_u32 duration)
{
  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (handle->metaDataBlocks == BLOCK_LIMIT)
    {
      /* Write metadata to temporary files */

      if (writeMetaDataTmp(handle) < 0)
        return -1;
    }

    handle->metaDataBlocks++;
  }

  handle->audioSampleTable->currentChunk++;

  if (updateDecodingTimeToSample(handle, handle->audioSampleTable, duration) < 0)
    return -1;

  if (updateSampleSize(handle, handle->audioSampleTable, size) < 0)
    return -1;

  if (updateSampleToChunk(handle->audioSampleTable) < 0)
    return -1;

  if (updateChunkOffset(handle, handle->audioSampleTable) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeFTYPAndMDATToFile(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function writes FTYP box to a file. In addition, it writes MDAT box
 *   size and type to a file. The function is used when meta data is put to
 *   the end of file.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *
 * Return value:
 *
 *   0          Success
 *   -1         Error
 *
 */
mp4_i32 writeFTYPAndMDATToFile(MP4HandleImp handle)
{
  mp4_u8  buf[32];
  mp4_u32 i = 0;


  if (writeFTYP(handle) < 0)
    return -1;

  handle->ftypWritten = MP4TRUE;


  i = formatMdatHeader(buf, (mp4_u32)0);
  if (writeFile(handle, buf, i) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeDataToFile(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function writes meta and media data to a file.
 *
 * Parameters:
 *
 *   handle     MP4 library handle
 *
 * Return value:
 *
 *   0          Success
 *   -1         Error
 *
 */
mp4_i32 writeDataToFile(MP4HandleImp handle)
{
  PRINT((_L("e_writedatatofile 1")));
  mp4_u32   metaDataSize = 0;
  trakSize  *audioTrackSize;
  trakSize  *videoTrackSize;
  mp4_bool  haveAudio = MP4FALSE;
  mp4_bool  haveVideo = MP4FALSE;
  mp4_u8 ftypdelta = 0;


  if ((handle->type & MP4_TYPE_AMR_NB) ||
      (handle->type & MP4_TYPE_AMR_WB) ||
       (handle->type & MP4_TYPE_QCELP_13K) ||     
      (handle->type & MP4_TYPE_MPEG4_AUDIO))
    haveAudio = MP4TRUE;

  if ((handle->type & MP4_TYPE_H263_PROFILE_0) ||
      (handle->type & MP4_TYPE_H263_PROFILE_3) ||
      (handle->type & MP4_TYPE_MPEG4_VIDEO) ||
	  containsAvcVideo( handle->type ) )
    haveVideo = MP4TRUE;

  if ((handle->generate3G2 && !(handle->type &  MP4_TYPE_QCELP_13K)) ||
       (!handle->generate3G2 && !(handle->type &  MP4_TYPE_AMR_WB)))
     ftypdelta = 4; /* one more additional compatible brand */
  else
     ftypdelta = 0;
  
  if( containsAvcVideo( handle->type ) )
  {
    ftypdelta += 4;
  }  

  PRINT((_L("e_writedatatofile_alloc_audiotrk 1")));
  audioTrackSize = (trakSize *)mp4malloc(sizeof(trakSize));
  if (audioTrackSize == NULL)
    return -1;
  PRINT((_L("e_writedatatofile_alloc_audiotrk 0")));    
	
  PRINT((_L("e_writedatatofile_alloc_videotrk 1")));  
  videoTrackSize = (trakSize *)mp4malloc(sizeof(trakSize));
  if (videoTrackSize == NULL)
  {
    mp4free(audioTrackSize);

    return -1;
  }
  PRINT((_L("e_writedatatofile_alloc_videotrk 0")));  

  if (haveAudio)
  {
	PRINT((_L("e_writedatatofile_deter_audiotrk_metadatasize 1")));  
    if (determineAudioTrakMetaDataSize(handle, handle->audioSampleTable, audioTrackSize) < 0)
        {
        mp4free(audioTrackSize);
        mp4free(videoTrackSize);
        return -1;
        }
	PRINT((_L("e_writedatatofile_deter_audiotrk_metadatasize 0")));          
  }

  if (haveVideo)
  {
	PRINT((_L("e_writedatatofile_deter_videotrk_metadatasize 1")));
    if (determineVideoTrakMetaDataSize(handle, handle->videoSampleTable, videoTrackSize) < 0)
        {
        mp4free(audioTrackSize);
        mp4free(videoTrackSize);
        return -1;
        }
PRINT((_L("e_writedatatofile_deter_videotrk_metadatasize 0")));        
  }

  if (handle->flags & MP4_FLAG_METADATALAST)
  {
    metaDataSize += (FTYP_SIZE + ftypdelta);             /* ftyp */
    handle->metaDataSize = metaDataSize;
  }
  else
  {
    metaDataSize += (FTYP_SIZE + ftypdelta);             /* ftyp */
    metaDataSize += 8;                     /* moov atomheader */
    metaDataSize += mvhdAtomSize(handle);                   /* mvhd */
    if (handle->moovUDTA)
        {
        metaDataSize += 8 + (mp4_u32)handle->moovUDTA->atomcontentsize;
        }
    metaDataSize += audioTrackSize->trak;  /* Audio trak */
    metaDataSize += videoTrackSize->trak;  /* Video trak */

    handle->metaDataSize = metaDataSize;
  }


  if (!(handle->flags & MP4_FLAG_LONGCLIP))
  {
    /* Update metadata pointers only if metadata is in memory */

    if (haveAudio)
    {
	PRINT((_L("e_writedatatofile_reupdata_audiometadata 1")));     
      if (reUpdateAudioMetaData(handle->audioSampleTable, metaDataSize) < 0)
      {
        mp4free(audioTrackSize);
        mp4free(videoTrackSize);

        return -1;
      }
	PRINT((_L("e_writedatatofile_reupdata_audiometadata 0")));           
    }

    if (haveVideo)
    {
	PRINT((_L("e_writedatatofile_reupdata_videometadata 1")));               
      if (reUpdateVideoMetaData(handle->videoSampleTable, metaDataSize) < 0)
      {
        mp4free(audioTrackSize);
        mp4free(videoTrackSize);

        return -1;
      }
	PRINT((_L("e_writedatatofile_reupdata_videometadata 0")));      
    }
  }
  else
  {
    /* Write the rest of metadata to temporary files */
	PRINT((_L("e_writedatatofile_write_metadatablocks 1")));
    if (handle->metaDataBlocks)
      if (writeMetaDataTmp(handle) < 0)
          {
          mp4free(audioTrackSize);
          mp4free(videoTrackSize);
          return -1;
          }
	PRINT((_L("e_writedatatofile_write_metadatablocks 0")));          
  }


  if (handle->flags & MP4_FLAG_METADATALAST)
  {
    mp4_u8  buf[16];
    mp4_u32 moovSize = 0;

    moovSize += 8;                     /* moov atomheader */
    moovSize += mvhdAtomSize(handle);                   /* mvhd */
    moovSize += audioTrackSize->trak;  /* Audio trak */
    moovSize += videoTrackSize->trak;  /* Video trak */
    if (handle->moovUDTA)
        {
        moovSize += 8 + handle->moovUDTA->atomcontentsize;
        }    
        
	PRINT((_L("e_writedatatofile_write_moov 1")));
    if (writeMOOV(handle, moovSize, haveAudio, haveVideo, audioTrackSize, videoTrackSize) < 0)
    {
      mp4free(audioTrackSize);
      mp4free(videoTrackSize);

      return -1;
    }
    PRINT((_L("e_writedatatofile_write_moov 0")));

    /* Overwrite media data size */
	PRINT((_L("e_writedatatofile_update_moov_media_size 1")));
	if(!handle->bufferWrite)
	{
		if (seekFileAbsWrite(handle, (FTYP_SIZE + ftypdelta)) != 0)
			{
			mp4free(audioTrackSize);
			mp4free(videoTrackSize);
			return -1;
			}
	}
	
	//make sure the buf is large enough to hold the mdat header
	TInt i;
	i = formatMdatHeader(buf, handle->mediaDataBytes);
    if (writeFileUnbuffered(handle, buf, i) < 0)
        {
        mp4free(audioTrackSize);
        mp4free(videoTrackSize);
        return -1;
        }
	PRINT((_L("e_writedatatofile_update_moov_media_size 0")));        
  }
  else
  {
	PRINT((_L("e_writedatatofile_write_ftyp 1")));  
    if (writeFTYP(handle) < 0)
    {
      mp4free(audioTrackSize);
      mp4free(videoTrackSize);

      return -1;
    }
    PRINT((_L("e_writedatatofile_write_ftyp 0")));  

	PRINT((_L("e_writedatatofile_write_new_moov 1")));  
    if (writeMOOV(handle, metaDataSize - (FTYP_SIZE + ftypdelta), haveAudio, haveVideo, audioTrackSize, videoTrackSize) < 0)
    {
      mp4free(audioTrackSize);
      mp4free(videoTrackSize);

      return -1;
    }
    PRINT((_L("e_writedatatofile_write_new_moov 0")));

	PRINT((_L("e_writedatatofile_write_new_mdia 1")));
    if (writeMediaData(handle) < 0)
    {
      mp4free(audioTrackSize);
      mp4free(videoTrackSize);

      return -1;
    }
    PRINT((_L("e_writedatatofile_write_new_mdia 0")));
  }

PRINT((_L("e_writedatatofile_free_audioandvideotrks 1")));
  mp4free(audioTrackSize);
  mp4free(videoTrackSize);
PRINT((_L("e_writedatatofile_free_audioandvideotrks 0")));
PRINT((_L("e_writedatatofile 0")));
  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateDecodingTimeToSample(MP4HandleImp handle,
 *                                      sampleTable *st,
 *                                      mp4_u32 duration)
 *
 * Description:
 *
 *   This function updates stts atom data.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   st        sampleTable
 *   duration  Duration of sample to insert (in media timescale)
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateDecodingTimeToSample(MP4HandleImp handle, sampleTable *st, mp4_u32 duration)
{
  if (!handle)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (st->sttsCurrentEntryCount == 0)
    {
      st->sttsSampleCount[st->sttsCurrentEntryCount] = 1;
      st->sttsSampleDelta[st->sttsCurrentEntryCount] = duration;

      st->sttsCurrentEntryCount++;
      st->sttsEntryCount++;

      return 0;
    }

    if (st->sttsCurrentEntryCount == st->sttsMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->sttsSampleCount,
                     2 * sizeof(mp4_u32) * st->sttsMaxEntryCount,
                     sizeof(mp4_u32) * st->sttsMaxEntryCount);
      if (p == NULL)
        return -1;

      st->sttsSampleCount = (mp4_u32 *)p;

      p = mp4realloc(st->sttsSampleDelta,
                     2 * sizeof(mp4_u32) * st->sttsMaxEntryCount,
                     sizeof(mp4_u32) * st->sttsMaxEntryCount);
      if (p == NULL)
        return -1;

      st->sttsSampleDelta = (mp4_u32 *)p;

      st->sttsMaxEntryCount *= 2;
    }

    if (st->sttsSampleDelta[st->sttsCurrentEntryCount - 1] == duration)
    {
      st->sttsSampleCount[st->sttsCurrentEntryCount - 1]++;
    }
    else
    {
      st->sttsSampleCount[st->sttsCurrentEntryCount] = 1;
      st->sttsSampleDelta[st->sttsCurrentEntryCount] = duration;

      st->sttsCurrentEntryCount++;
      st->sttsEntryCount++;
    }
  }
  else
  {
    if (st->sttsEntryCount == 0)
    {
      st->sttsSampleCount[st->sttsEntryCount]++;
      st->sttsSampleDelta[st->sttsEntryCount] = duration;

      st->sttsEntryCount++;

      return 0;
    }

    if (st->sttsEntryCount == st->sttsMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->sttsSampleCount,
                     2 * sizeof(mp4_u32) * st->sttsMaxEntryCount,
                     sizeof(mp4_u32) * st->sttsMaxEntryCount);
      if (p == NULL)
        return -1;

      st->sttsSampleCount = (mp4_u32 *)p;

      p = mp4realloc(st->sttsSampleDelta,
                     2 * sizeof(mp4_u32) * st->sttsMaxEntryCount,
                     sizeof(mp4_u32) * st->sttsMaxEntryCount);
      if (p == NULL)
        return -1;

      st->sttsSampleDelta = (mp4_u32 *)p;

      st->sttsMaxEntryCount *= 2;
    }

    if (st->sttsSampleDelta[st->sttsEntryCount - 1] == duration)
    {
      st->sttsSampleCount[st->sttsEntryCount - 1]++;
    }
    else
    {
      st->sttsSampleCount[st->sttsEntryCount] = 1;
      st->sttsSampleDelta[st->sttsEntryCount] = duration;

      st->sttsEntryCount++;
    }
  }


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateSampleSize(MP4HandleImp handle,
 *                            sampleTable *st,
 *                            mp4_u32 size)
 *
 * Description:
 *
 *   This function updates stsz atom data.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   st        sampleTable
 *   size      Size of sample in bytes
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateSampleSize(MP4HandleImp handle, sampleTable *st, mp4_u32 size)
{
  if (!handle)
    return -1;

  if (size == 0)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (st->stszCurrentSampleCount == st->stszMaxSampleCount)
    {
      void *p;

      p = mp4realloc(st->stszEntrySize,
                     2 * sizeof(mp4_u32) * st->stszMaxSampleCount,
                     sizeof(mp4_u32) * st->stszMaxSampleCount);
      if (p == NULL)
        return -1;

      st->stszEntrySize = (mp4_u32 *)p;

      st->stszMaxSampleCount *= 2;
    }

    st->stszEntrySize[st->stszCurrentSampleCount] = size;

    st->stszCurrentSampleCount++;
    st->stszSampleCount++;
  }
  else
  {
    if (st->stszSampleCount == st->stszMaxSampleCount)
    {
      void *p;

      p = mp4realloc(st->stszEntrySize,
                     2 * sizeof(mp4_u32) * st->stszMaxSampleCount,
                     sizeof(mp4_u32) * st->stszMaxSampleCount);
      if (p == NULL)
        return -1;

      st->stszEntrySize = (mp4_u32 *)p;

      st->stszMaxSampleCount *= 2;
    }

    st->stszEntrySize[st->stszSampleCount] = size;

    st->stszSampleCount++;
  }


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateSampleToChunk(sampleTable *st)
 *
 * Description:
 *
 *   This function updates stsc atom data.
 *
 * Parameters:
 *
 *   st        sampleTable
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateSampleToChunk(sampleTable *st)
{
  if (st->stscEntryCount != 0)
    return 0;


  st->stscFirstChunk[st->stscEntryCount] = st->currentChunk;
  st->stscSamplesPerChunk[st->stscEntryCount] = 1;
  st->stscSampleDescriptionIndex[st->stscEntryCount] = 1;  /* Note: Need to update here for multiple sample entry support */
  st->stscEntryCount++;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateChunkOffset(MP4HandleImp handle,
 *                             sampleTable *st)
 *
 * Description:
 *
 *   This function updates stco atom data.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   st        sampleTable
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateChunkOffset(MP4HandleImp handle, sampleTable *st)
{
  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (st->stcoCurrentEntryCount == st->stcoMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->stcoChunkOffset,
                     2 * sizeof(mp4_u64) * st->stcoMaxEntryCount,
                     sizeof(mp4_u64) * st->stcoMaxEntryCount);
      if (p == NULL)
        return -1;

      st->stcoChunkOffset = (mp4_u64*)p;

      st->stcoMaxEntryCount *= 2;
    }
    
    if (handle->flags & MP4_FLAG_METADATALAST)
      updateChunkOffset(st, st->stcoCurrentEntryCount, handle->mediaDataBytes);
    else
      updateChunkOffset(st, st->stcoCurrentEntryCount, handle->bytesInTmpFile);

    st->stcoCurrentEntryCount++;
    st->stcoEntryCount++;
  }
  else
  {
    if (st->stcoEntryCount == st->stcoMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->stcoChunkOffset,
                     2 * sizeof(mp4_u64) * st->stcoMaxEntryCount,
                     sizeof(mp4_u64) * st->stcoMaxEntryCount);
      if (p == NULL)
        return -1;

      st->stcoChunkOffset = (mp4_u64 *)p;

      st->stcoMaxEntryCount *= 2;
    }

    if (handle->flags & MP4_FLAG_METADATALAST)
      updateChunkOffset(st, st->stcoEntryCount, handle->mediaDataBytes);
    else
      updateChunkOffset(st, st->stcoEntryCount, handle->bytesInTmpFile);

    st->stcoEntryCount++;
  }


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 updateSyncSample(MP4HandleImp handle,
 *                            sampleTable *st)
 *
 * Description:
 *
 *   This function updates stss atom data.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *   st        sampleTable
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 updateSyncSample(MP4HandleImp handle, sampleTable *st)
{
  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (st->stssCurrentEntryCount == st->stssMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->stssSampleNumber,
                     2 * sizeof(mp4_u32) * st->stssMaxEntryCount,
                     sizeof(mp4_u32) * st->stssMaxEntryCount);
      if (p == NULL)
        return -1;

      st->stssSampleNumber = (mp4_u32 *)p;

      st->stssMaxEntryCount *= 2;
    }

    st->stssSampleNumber[st->stssCurrentEntryCount] = handle->videoSampleNum;
    st->stssCurrentEntryCount++;
    st->stssEntryCount++;
  }
  else
  {
    if (st->stssEntryCount == st->stssMaxEntryCount)
    {
      void *p;

      p = mp4realloc(st->stssSampleNumber,
                     2 * sizeof(mp4_u32) * st->stssMaxEntryCount,
                     sizeof(mp4_u32) * st->stssMaxEntryCount);
      if (p == NULL)
        return -1;

      st->stssSampleNumber = (mp4_u32 *)p;

      st->stssMaxEntryCount *= 2;
    }

    st->stssSampleNumber[st->stssEntryCount] = handle->videoSampleNum;
    st->stssEntryCount++;
  }


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineAudioTrakMetaDataSize(MP4HandleImp handle,
 *                                          sampleTable *st,
 *                                          trakSize *ts)
 *
 * Description:
 *
 *   This function calculates the audio track meta data size.
 *
 * Parameters:
 *
 *   handle      MP4 library handle
 *   st          Sample table data
 *   ts          Atom sizes are returned here
 *
 * Return value:
 *
 *   0           Success
 *
 */
mp4_i32 determineAudioTrakMetaDataSize(MP4HandleImp handle, sampleTable *st, trakSize *ts)
{
  if (handle->type & MP4_TYPE_AMR_NB) /* AMR-NB */
  {
    ts->damr = 17;
    ts->samr = 36 + ts->damr;
    ts->stsd = 16 + ts->samr;
  }
  else if (handle->type & MP4_TYPE_AMR_WB) /* AMR-WB */
  {
    ts->damr = 17;
    ts->sawb = 36 + ts->damr;
    ts->stsd = 16 + ts->sawb;
  }
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (!handle->qcelpStoredAsMPEGAudio)) /* QCELP 13K stored in QCELPSampleEntry */
  {
    ts->dqcp = 14;
    ts->sqcp = 36 + ts->dqcp;
    ts->stsd = 16 + ts->sqcp;
  } 
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (handle->qcelpStoredAsMPEGAudio)) /* QCELP 13K stored in MP4AudioDescription */
  {
    calculateES_DescriptorSize(handle, MP4_TYPE_QCELP_13K);
    ts->esds = 12 + handle->ES_DescriptorSize; /*37 + handle->audioDecSpecificInfoSize;*/
    ts->mp4a = 36 + ts->esds;
    ts->stsd = 16 + ts->mp4a;
  }
  else /* MPEG audio */
  {
    calculateES_DescriptorSize(handle, MP4_TYPE_MPEG4_AUDIO);
    ts->esds = 12 + handle->ES_DescriptorSize; /*37 + handle->audioDecSpecificInfoSize;*/
    ts->mp4a = 36 + ts->esds;
    ts->stsd = 16 + ts->mp4a;
  }
  ts->stts = 16 + st->sttsEntryCount * 8;
  ts->stsc = 16 + st->stscEntryCount * 12;
  if (st->stszSampleSize != 0)
    ts->stsz = 20;
  else
    ts->stsz = 20 + st->stszSampleCount * 4;
  ts->stco = 16 + st->stcoEntryCount * (st->stcoNeed64Bits ? 8 : 4);
  ts->stbl = 8 + ts->stsd + ts->stts + ts->stsc + ts->stsz + ts->stco;
  ts->dref = 28;
  ts->dinf = 8 + ts->dref;
  ts->smhd = 16;
  ts->minf = 8 + ts->smhd + ts->dinf + ts->stbl;
  ts->hdlr = 33;

  if (handle->audioDuration > MP4_INT_MAX)
  {
    ts->mdhd = 44;
    ts->tkhd = 104;
  }
  else
  {
    ts->mdhd = 32;
    ts->tkhd = 92;
  }

  ts->mdia = 8 + ts->mdhd + ts->hdlr + ts->minf;
  if ( handle->audioUDTA )
      {
      ts->udta = 8 + handle->audioUDTA->atomcontentsize;
      }
  ts->trak = 8 + ts->tkhd + ts->mdia + ts->udta;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 determineVideoTrakMetaDataSize(MP4HandleImp handle,
 *                                          sampleTable *st,
 *                                          trakSize *ts)
 *
 * Description:
 *
 *   This function calculates the video track meta data size.
 *
 * Parameters:
 *
 *   handle      MP4 library handle
 *   st          Sample table data
 *   ts          Atom sizes are returned here
 *
 * Return value:
 *
 *   0           Success
 *
 */
mp4_i32 determineVideoTrakMetaDataSize(MP4HandleImp handle, sampleTable *st, trakSize *ts)
{
  /* Note: This functions assumes single sample entry per media track. 
	   If necessary, modify to support multiple sample entries in the future. */
  if ((handle->type & MP4_TYPE_H263_PROFILE_0) || (handle->type & MP4_TYPE_H263_PROFILE_3))
  {
    ts->d263 = 15;
    ts->s263 = 86 + ts->d263;
    ts->stsd = 16 + ts->s263;
  }
  else /* MPEG-4 */
   if ((handle->type & MP4_TYPE_MPEG4_VIDEO))		
  {
    ts->esds = 37 + handle->videoDecSpecificInfoSize;
    ts->mp4v = 86 + ts->esds;
    ts->stsd = 16 + ts->mp4v;
  }
  else  /* AVC */
  if ( containsAvcVideo( handle->type ) )
  {
    /* Note: If necessary, add btrt and m4ds boxes here in the future. */
   ts->avcc = 8 + handle->videoDecSpecificInfoSize; /*handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfigSize;*/
   ts->avc1 = 86 + ts->avcc;
   ts->stsd = 16 + ts->avc1;
  }
  else
  {
  }

  ts->stts = 16 + st->sttsEntryCount * 8;
  ts->stsc = 16 + st->stscEntryCount * 12;
  if (st->stszSampleSize != 0)
    ts->stsz = 20;
  else
    ts->stsz = 20 + st->stszSampleCount * 4;
  ts->stco = 16 + st->stcoEntryCount * (st->stcoNeed64Bits ? 8 : 4);
  ts->stss = 16 + st->stssEntryCount * 4;
  if( handle->videoSampleTable->sdtpEntryCount )
  	ts->sdtp = 4 + 4 + 1 + 3 + handle->videoSampleTable->sdtpEntryCount; //size + 'SDTP' + ver + flags + dependencies
  else
    ts->sdtp = 0;

  ts->stbl = 8 + ts->stsd + ts->stts + ts->stsc + ts->stsz + ts->stco + ts->stss + ts->sdtp;
  ts->dref = 28;
  ts->dinf = 8 + ts->dref;
  ts->vmhd = 20;
  ts->minf = 8 + ts->vmhd + ts->dinf + ts->stbl;
  ts->hdlr = 33;
  
  if (handle->videoDuration > MP4_INT_MAX)
  {
    ts->mdhd = 44;
    ts->tkhd = 104;
  }
  else
  {
    ts->mdhd = 32;
    ts->tkhd = 92;
  }

  ts->mdia = 8 + ts->mdhd + ts->hdlr + ts->minf;
  if ( handle->videoUDTA )
    {
    ts->udta = 8 + handle->videoUDTA->atomcontentsize;  
    }
  ts->trak = 8 + ts->tkhd + ts->mdia + ts->udta;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 reUpdateAudioMetaData(sampleTable *st,
 *                                 mp4_u32 metaDataSize)
 *
 * Description:
 *
 *   This function updates the chunk offsets in the meta data to point to
 *   correct places.
 *
 * Parameters:
 *
 *   st            Sample table data
 *   metaDataSize  Meta data size
 *
 * Return value:
 *
 *   0             Success
 *
 */
mp4_i32 reUpdateAudioMetaData(sampleTable *st, mp4_u32 metaDataSize)
{
  mp4_u32 i;


  for (i = 0; i < st->stcoEntryCount; i++)
    updateChunkOffset(st, i, st->stcoChunkOffset[i] + metaDataSize + MDAT_HEADER_SIZE);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 reUpdateVideoMetaData(sampleTable *st,
 *                                 mp4_u32 metaDataSize)
 *
 * Description:
 *
 *   This function updates the chunk offsets in the meta data to point to
 *   correct places.
 *
 * Parameters:
 *
 *   st            Sample table data
 *   metaDataSize  Meta data size
 *
 * Return value:
 *
 *   0             Success
 *
 */
mp4_i32 reUpdateVideoMetaData(sampleTable *st, mp4_u32 metaDataSize)
{
  mp4_u32 i;


  for (i = 0; i < st->stcoEntryCount; i++)
    updateChunkOffset(st, i, st->stcoChunkOffset[i] + metaDataSize + MDAT_HEADER_SIZE);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeFTYP(MP4HandleImp handle)
 *
 * Description:
 *
 *   Write FTYP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeFTYP(MP4HandleImp handle)
{
  mp4_u8  *buf;
  mp4_u32 i = 0;
  mp4_u8 ftypdelta = 0;

   if ((handle->generate3G2 && !(handle->type &  MP4_TYPE_QCELP_13K)) ||
       (!handle->generate3G2 && !(handle->type &  MP4_TYPE_AMR_WB)))
     ftypdelta = 4; /* one more additional compatible brand */
   else
     ftypdelta = 0;

   if( containsAvcVideo( handle->type ) )
   {
     ftypdelta += 4;
   }
   if(handle->bufferWrite)
		handle->ftypdelta=ftypdelta;

  buf = (mp4_u8 *)mp4malloc(FTYP_SIZE + ftypdelta);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)(FTYP_SIZE + ftypdelta));
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_FTYP);
  i += 4;

  if ( containsAvcVideo( handle->type ) )
  {
      if(handle->generateMP4)
      {
	      /* MPEG-4 Major brand */
	      buf[i++] = 'm';
	      buf[i++] = 'p';
	      buf[i++] = '4';
	      buf[i++] = '2';
      }

	  else
	  {
	    /* AVC is included for 3GPP Release 6 and beyond */
	    /* Major brand */
	    buf[i++] = '3';
	    buf[i++] = 'g';
	    buf[i++] = 'p';
	    buf[i++] = '6';
	  }
  }
  else
  {
    if(handle->generateMP4)
    {
	    /* MPEG-4 Major brand */
	    buf[i++] = 'm';
	    buf[i++] = 'p';
	    buf[i++] = '4';
	    buf[i++] = '2';
    }
    else if(handle->generate3G2)
    {
	    /* 3GPP2 Major brand */
	    buf[i++] = '3';
	    buf[i++] = 'g';
	    buf[i++] = '2';
	    buf[i++] = 'a';
    }
    else
    {
	    /* 3GPP Major brand */
	    buf[i++] = '3';
	    buf[i++] = 'g';
	    buf[i++] = 'p';
	    buf[i++] = '4';
    }
  }

  /* Minor version */
  if(handle->generateMP4)
  {  /* MPEG-4 Minor Version */
      insertu32(buf+i, (mp4_u32)(0)); /* 0 */
      i += 4;
  }
  else if(handle->generate3G2)
  {  /* 3GPP2 Minor Version */
      if( containsAvcVideo( handle->type ) )
      {
      	insertu32(buf+i, (mp4_u32)(2*256*256)); /* VB.0.0 */
      	i += 4;
      }
      else
      {
      	insertu32(buf+i, (mp4_u32)(1*256*256)); /* VA.0.0 */
      	i += 4;
      }
  }
  else  
  { /* 3GPP Minor Version */
      if( containsAvcVideo( handle->type ) )
      {
      	  insertu32(buf+i, (mp4_u32)2*256); /* V6.3.0 */
      	  i += 4;
      }
      else
      {
      	  insertu32(buf+i, (mp4_u32)4*256); /* V4.4.0 */
          i += 4;
      }
  }

  /* Compatible brands */
  if(handle->generateMP4)
  {/* MPEG-4 Compatible Brands */
	  buf[i++] = 'm';
	  buf[i++] = 'p';
	  buf[i++] = '4';
	  buf[i++] = '2';

      buf[i++] = '3';
      buf[i++] = 'g';
      buf[i++] = 'p';
      buf[i++] = '4';

      buf[i++] = 'i';
      buf[i++] = 's';
      buf[i++] = 'o';
      buf[i++] = 'm';
	  if ( containsAvcVideo( handle->type ) )
	  {
		  /* AVC is included for 3GPP Release 6 and beyond */
		  buf[i++] = 'a';
		  buf[i++] = 'v';
		  buf[i++] = 'c';
		  buf[i++] = '1';
	  }
  }
  else if(handle->generate3G2)
  {/* 3GPP2 Compatible Brands */
      if( containsAvcVideo( handle->type ) )
      {
      	  buf[i++] = '3';
	      buf[i++] = 'g';
	      buf[i++] = '2';
	      buf[i++] = 'b';
	      if (!(handle->type &  MP4_TYPE_QCELP_13K))
          { // If 3GPP codecs are used, then put 3GP6 in compatible brands 
              buf[i++] = '3';
              buf[i++] = 'g';
              buf[i++] = 'p';
              buf[i++] = '6';
          }
      }
      else
      {
      	  buf[i++] = '3';
      	  buf[i++] = 'g';
      	  buf[i++] = '2';
      	  buf[i++] = 'a';
          if (!(handle->type &  MP4_TYPE_QCELP_13K))
          { // If 3GPP codecs are used, then put 3GP4 in compatible brands 
              buf[i++] = '3';
              buf[i++] = 'g';
              buf[i++] = 'p';
              buf[i++] = '4';
          }
      }
	  if ( containsAvcVideo( handle->type ) )
	  {
		  /* AVC is included for 3GPP Release 6 and beyond */
		  buf[i++] = 'a';
		  buf[i++] = 'v';
		  buf[i++] = 'c';
		  buf[i++] = '1';
	  }

      buf[i++] = 'i';
      buf[i++] = 's';
      buf[i++] = 'o';
      buf[i++] = 'm';
  }
  else
  {/* 3GPP Compatible Brands */
      if ( containsAvcVideo( handle->type ) )
	  {
	      buf[i++] = '3';
	      buf[i++] = 'g';
	      buf[i++] = 'p';
	      buf[i++] = '6';
	  }
	  else
	  {
	      buf[i++] = '3';
	      buf[i++] = 'g';
	      buf[i++] = 'p';
	      buf[i++] = '4';
	  }
	  
      if (!(handle->type &  MP4_TYPE_AMR_WB))
      { // If 3GPP2 codecs are used, then put 3G2A in compatible brands 
          buf[i++] = '3';
          buf[i++] = 'g';
          buf[i++] = '2';
          buf[i++] = 'a';
      }

      buf[i++] = 'i';
      buf[i++] = 's';
      buf[i++] = 'o';
      buf[i++] = 'm';

	  if ( containsAvcVideo( handle->type ) )
	  {
		  /* AVC is included for 3GPP Release 6 and beyond */
		  buf[i++] = 'a';
		  buf[i++] = 'v';
		  buf[i++] = 'c';
		  buf[i++] = '1';
	  }
      
  }
  if (writeFile(handle, buf, (FTYP_SIZE + ftypdelta)) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMOOV(MP4HandleImp handle,
 *                     mp4_u32 moovSize,
 *                     mp4_bool haveAudio,
 *                     mp4_bool haveVideo,
 *                     trakSize *audioTrackSize,
 *                     trakSize *videoTrackSize)
 *
 * Description:
 *
 *   Write MOOV atom.
 *
 * Parameters:
 *
 *   handle          MP4 library handle
 *   moovSize        Size of MOOV atom in bytes
 *   haveAudio       Flag to indicate whether audio exists or not
 *   haveVideo       Flag to indicate whether video exists or not
 *   audioTrackSize  Size of audio track in bytes
 *   videoTrackSize  Size of video track in bytes
 *
 * Return value:
 *
 *   0               Success
 *   -1              Error
 *
 */
mp4_i32 writeMOOV(MP4HandleImp handle, mp4_u32 moovSize, mp4_bool haveAudio, mp4_bool haveVideo, trakSize *audioTrackSize, trakSize *videoTrackSize)
{
  PRINT((_L("e_writemoov 1")));
  mp4_u8  buf[8];
  mp4_u32 i = 0;


  /* Size */
  insertu32(buf+i, moovSize);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MOOV);
  i += 4;

  PRINT((_L("e_writemoov_header 1")));
  if (writeFile(handle, buf, 8) < 0)
    return -1;
  PRINT((_L("e_writemoov_header 0")));  

  PRINT((_L("e_writemoov_mvhd 1")));	
  if (writeMVHD(handle) < 0)
    return -1;
  PRINT((_L("e_writemoov_mvhd 0")));

  PRINT((_L("e_writemoov_video 1")));
  if (haveVideo)
    if (writeVideoTrak(handle, videoTrackSize) < 0)
      return -1;
  PRINT((_L("e_writemoov_video 0")));    

  PRINT((_L("e_writemoov_audio 1")));
  if (haveAudio)
    if (writeAudioTrak(handle, audioTrackSize) < 0)
      return -1;
  PRINT((_L("e_writemoov_audio 0")));    
    
  PRINT((_L("e_writemoov_udta 1")));    
  if (handle->moovUDTA)
    {
    if (writeUDTA(handle, handle->moovUDTA) < 0)
        {
        return -1;
        }
    }
  PRINT((_L("e_writemoov_udta 0")));
  PRINT((_L("e_writemoov 0")));
  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMVHD(MP4HandleImp handle)
 *
 * Description:
 *
 *   Write MVHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeMVHD(MP4HandleImp handle)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  u32;

  size_t mvhdSize = mvhdAtomSize(handle);
      
  buf = (mp4_u8 *)mp4malloc(mvhdSize);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)mvhdSize);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MVHD);
  i += 4;


  if (handle->videoDuration > MP4_INT_MAX || handle->audioDuration > MP4_INT_MAX)
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0x01000000);  //its going to be a version 1 atom
      i += 4;
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)MVHD_TIMESCALE);
      i += 4;
    
      /* Duration */
      {
        mp4_u64  u64;
        mp4_u64  videoDuration = 0;
        mp4_u64  audioDuration = 0;
    
    
        if (handle->videoTimeScale)
          videoDuration = (mp4_u64)((mp4_double)MVHD_TIMESCALE *
                                    (mp4_double)handle->videoDuration /
                                    (mp4_double)handle->videoTimeScale +
                                    (mp4_double)0.5);
        if (handle->audioTimeScale)
          audioDuration = (mp4_u64)((mp4_double)MVHD_TIMESCALE *
                                    (mp4_double)handle->audioDuration /
                                    (mp4_double)handle->audioTimeScale +
                                    (mp4_double)0.5);
    
        if (audioDuration > videoDuration)
          u64 = audioDuration;
        else
          u64 = videoDuration;
    
        insertu64(buf+i, u64);
        i += 8;
      }
 
  }
  else
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;

      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)MVHD_TIMESCALE);
      i += 4;
    
      /* Duration */
      {
        mp4_u32  videoDuration = 0;
        mp4_u32  audioDuration = 0;
    
    
        if (handle->videoTimeScale)
          videoDuration = (mp4_u32)((mp4_double)MVHD_TIMESCALE *
                                    (mp4_double)handle->videoDuration /
                                    (mp4_double)handle->videoTimeScale +
                                    (mp4_double)0.5);
        if (handle->audioTimeScale)
          audioDuration = (mp4_u32)((mp4_double)MVHD_TIMESCALE *
                                    (mp4_double)handle->audioDuration /
                                    (mp4_double)handle->audioTimeScale +
                                    (mp4_double)0.5);
    
        if (audioDuration > videoDuration)
          u32 = audioDuration;
        else
          u32 = videoDuration;
    
        insertu32(buf+i, u32);
        i += 4;
      }
  }
  /* Reserved */
  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu16(buf+i, (mp4_u16)0x0100);
  i += 2;

  insertu16(buf+i, (mp4_u16)0x0000);
  i += 2;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x40000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  if (writeFile(handle, buf, mvhdSize) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoTrak(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video track atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoTrak(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32 i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->trak);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_TRAK);
  i += 4;

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

  if (writeVideoTKHD(handle, ts) < 0)
    return -1;

  if (writeVideoMDIA(handle, ts) < 0)
    return -1;
  
  if (handle->videoUDTA)
    {
    if (writeUDTA(handle, handle->videoUDTA) < 0)
        return -1;
    }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoTKHD(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write TKHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoTKHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8      *buf;
  mp4_u32     i = 0;
  mp4_u32     u32;
  mp4_double  ud;


  buf = (mp4_u8 *)mp4malloc(ts->tkhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->tkhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_TKHD);
  i += 4;

  
  if (handle->videoDuration > MP4_INT_MAX)
  {
      mp4_u64     u64;
      /* Version and flags */
      buf[i++] = 1;     //make this a version 1 atom
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 7;  /* Track enabled, used in movie and preview */
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Track ID */
      insertu32(buf+i, (mp4_u32)1);
      i += 4;
    
      /* Reserved */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Duration */
      if ( (handle->videoDuration == 0) || (handle->videoTimeScale == 0) )
          {
          ud = 0;
          }
      else
          {
          ud = (mp4_double)MVHD_TIMESCALE * (mp4_double)handle->videoDuration / (mp4_double)handle->videoTimeScale + (mp4_double)0.5;
          }
    
      u64 = (mp4_u64)ud;
      insertu64(buf+i, u64);
      i += 8;
  }
  else
  {
      /* Version and flags */
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 7;  /* Track enabled, used in movie and preview */
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Track ID */
      insertu32(buf+i, (mp4_u32)1);
      i += 4;
    
      /* Reserved */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Duration */
      if ( (handle->videoDuration == 0) || (handle->videoTimeScale == 0) )
          {
          ud = 0;
          }
      else
          {
          ud = (mp4_double)MVHD_TIMESCALE * (mp4_double)handle->videoDuration / (mp4_double)handle->videoTimeScale + (mp4_double)0.5;
          }
    
      u32 = (mp4_u32)ud;
      insertu32(buf+i, u32);
      i += 4;
  }
  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)0);  /* Visual track */
  i += 2;

  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x40000000);
  i += 4;

  insertu16(buf+i, (mp4_u16)handle->videoWidth);  /* Width */
  i += 2;

  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  insertu16(buf+i, (mp4_u16)handle->videoHeight);  /* Height */
  i += 2;

  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  if (writeFile(handle, buf, ts->tkhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoMDIA(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video MDIA atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoMDIA(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mdia);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MDIA);
  i += 4;

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

  if (writeVideoMDHD(handle, ts) < 0)
    return -1;

  if (writeVideoHDLR(handle, ts) < 0)
    return -1;

  if (writeVideoMINF(handle, ts) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoMDHD(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video MDHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoMDHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  u32;


  buf = (mp4_u8 *)mp4malloc(ts->mdhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mdhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MDHD);
  i += 4;

  if (handle->videoDuration > MP4_INT_MAX)
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0x01000000); //version 1 atom
      i += 4;
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)handle->videoTimeScale);
      i += 4;
    
      /* Duration */
      insertu64(buf+i, handle->videoDuration);
      i += 8;
  }
  else
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)handle->videoTimeScale);
      i += 4;
    
      /* Duration */
      insertu32(buf+i, (mp4_u32)handle->videoDuration);
      i += 4;
  }
  
  /* Language */
  insertu16(buf+i, (mp4_u16)0x55c4); /* 'und' */
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0x0000);
  i += 2;

  if (writeFile(handle, buf, ts->mdhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoHDLR(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video HDLR atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoHDLR(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->hdlr);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->hdlr);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_HDLR);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Handler type */
  buf[i++] = 'v';
  buf[i++] = 'i';
  buf[i++] = 'd';
  buf[i++] = 'e';

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Empty string */
  buf[i++] = 0;

  if (writeFile(handle, buf, ts->hdlr) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoMINF(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video MINF atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoMINF(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->minf);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MINF);
  i += 4;

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

  if (writeVMHD(handle, ts) < 0)
    return -1;

  if (writeDINF(handle, ts) < 0)
    return -1;

  if (writeVideoSTBL(handle, ts) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32  writeVMHD(MP4HandleImp handle,
 *                      trakSize *ts)
 *
 * Description:
 *
 *   Write VMHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeVMHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->vmhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->vmhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_VMHD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0x00000001);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  if (writeFile(handle, buf, ts->vmhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeDINF(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write DINF atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeDINF(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->dinf);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_DINF);
  i += 4;

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

  if (writeDREF(handle, ts) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32  writeDREF(MP4HandleImp handle,
 *                      trakSize *ts)
 *
 * Description:
 *
 *   Write DREF atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeDREF(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->dref);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->dref);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_DREF);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)1);
  i += 4;

  /* URL atom */

  /* Size */
  insertu32(buf+i, (mp4_u32)12);
  i += 4;

  /* Type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_URL);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0x00000001);
  i += 4;

  if (writeFile(handle, buf, ts->dref) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTBL(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STBL atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTBL(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stbl);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STBL);
  i += 4;

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

  if (writeVideoSTSD(handle, ts) < 0)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (writeVideoSTTSLongClip(handle, ts) < 0)
      return -1;
  }
  else
  {
    if (writeVideoSTTS(handle, ts) < 0)
      return -1;
  }

  if (writeVideoSTSC(handle, ts) < 0)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (writeVideoSTSZLongClip(handle, ts) < 0)
      return -1;
	
	if (handle->videoSampleTable->stcoNeed64Bits)
	{
      if (writeVideoCO64LongClip(handle, ts) < 0)
        return -1;
	}
	else
	{
      if (writeVideoSTCOLongClip(handle, ts) < 0)
        return -1;
	}

    if (writeVideoSTSSLongClip(handle, ts) < 0)
      return -1;
    
    if(ts->sdtp && writeVideoSDTPLongClip(handle, ts) < 0)
      return -1;
  }
  else
  {
    if (writeVideoSTSZ(handle, ts) < 0)
      return -1;

	if (handle->videoSampleTable->stcoNeed64Bits)
	{
      if (writeVideoCO64(handle, ts) < 0)
        return -1;
	}
	else
	{
      if (writeVideoSTCO(handle, ts) < 0)
        return -1;
	}

    if (writeVideoSTSS(handle, ts) < 0)
      return -1;
    
    if(ts->sdtp && writeVideoSDTP(handle, ts) < 0)
      return -1;
  }
  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSD(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STSD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[16];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)1);
  i += 4;

  if (writeFile(handle, buf, 16) < 0)
    return -1;

  if (handle->type & MP4_TYPE_MPEG4_VIDEO)
  {
    if (writeMP4V(handle, ts) < 0)
      return -1;
  }
  else if ((handle->type & MP4_TYPE_H263_PROFILE_0) ||
           (handle->type & MP4_TYPE_H263_PROFILE_3))
  {
    if (writeS263(handle, ts) < 0)
      return -1;
  }
  else if ( containsAvcVideo( handle->type ) )
  {
	  if (writeAVC1(handle, ts) < 0)
		  return -1;
  }
  else
  {
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMP4V(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write MP4V atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeMP4V(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(86);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mp4v);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MP4V);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Width */
  insertu16(buf+i, (mp4_u16)handle->videoWidth);
  i += 2;

  /* Height */
  insertu16(buf+i, (mp4_u16)handle->videoHeight);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)24);
  i += 2;

  insertu16(buf+i, (mp4_u16)0xffff);
  i += 2;

  if (writeFile(handle, buf, 86) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeVideoESD(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoESD(MP4HandleImp handle,
 *                         trakSize *ts)
 *
 * Description:
 *
 *   Write video ESD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoESD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->esds);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->esds);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_ESD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* ES_DescrTag */
  buf[i++] = 0x03;

  /* Size */
  buf[i++] = (mp4_u8)(23 + handle->videoDecSpecificInfoSize);

  /* ES_ID */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  /* Flags */
  buf[i++] = 0;

  /* DecoderConfigDescrTag */
  buf[i++] = 0x04;

  /* Size */
  buf[i++] = (mp4_u8)(15 + handle->videoDecSpecificInfoSize);

  /* ObjectTypeIndication */
  buf[i++] = 0x20;

  /* Flags */
  buf[i++] = 0x11;

  /* BufferSizeDB */
  buf[i++] = 0x00;
  buf[i++] = 0x50;
  buf[i++] = 0x00;

  /* MaxBitrate */
  insertu32(buf+i, (mp4_u32)handle->videoMaxBitrate);
  i += 4;

  /* AvgBitrate */
  insertu32(buf+i, (mp4_u32)handle->videoAvgBitrate);
  i += 4;

  /* DecSpecificInfoTag */
  buf[i++] = 0x05;

  /* Size */
  buf[i++] = (mp4_u8)handle->videoDecSpecificInfoSize;

  /* DecoderSpecificInfo */
  mp4memcpy(buf+i, handle->videoDecSpecificInfo, handle->videoDecSpecificInfoSize);
  i += handle->videoDecSpecificInfoSize;

  /* SLConfigDescrTag */
  buf[i++] = 0x06;

  /* Size */
  buf[i++] = 1;

  /* */
  buf[i++] = 2;

  if (writeFile(handle, buf, ts->esds) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeS263(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write S263 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeS263(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(86);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->s263);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_S263);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Width */
  insertu16(buf+i, (mp4_u16)handle->videoWidth);
  i += 2;

  /* Height */
  insertu16(buf+i, (mp4_u16)handle->videoHeight);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)24);
  i += 2;

  insertu16(buf+i, (mp4_u16)0xffff);
  i += 2;

  if (writeFile(handle, buf, 86) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeD263(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeD263(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write D263 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeD263(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[15];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->d263);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_D263);
  i += 4;

  /* Vendor */
  buf[i++] = 'S';
  buf[i++] = '6';
  buf[i++] = '0';
  buf[i++] = ' ';

  /* Decoder version */
  buf[i++] = 0;

  /* H263_Level */
  buf[i++] = handle->videoLevel;

  /* H263_Profile */
  if (handle->type & MP4_TYPE_H263_PROFILE_0)
    buf[i++] = 0;
  else if (handle->type & MP4_TYPE_H263_PROFILE_3)
    buf[i++] = 3;
  else
    return -1;

  if (writeFile(handle, buf, 15) < 0)
    return -1;

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAVC1(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write AVC1 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAVC1(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(86);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->avc1);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_AVC1);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Width */
  insertu16(buf+i, (mp4_u16)handle->videoWidth);
  i += 2;

  /* Height */
  insertu16(buf+i, (mp4_u16)handle->videoHeight);
  i += 2;

  /* H-res (default is 72dpi = 0x00480000) */
  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  /* V-res (default is 72dpi = 0x00480000) */
  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;
  
  /* Frame count (default is 1) */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Compressor name (32 byte string) */
  // The spec *recommends* inserting "\012AVC Coding" here
  // but we just have a string of nulls.
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Depth (default is 0x0018 which indicates colour images with no alpha) */
  insertu16(buf+i, (mp4_u16)24);
  i += 2;

  /* Pre-defined (-1) */
  insertu16(buf+i, (mp4_u16)0xffff);
  i += 2;

  if (writeFile(handle, buf, 86) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeAVCC(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  /* Note: If necessary, include writing of btrt and m4ds atoms. */

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAVCC(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write AVCC atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAVCC(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->avcc);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->avcc);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_AVCC);
  i += 4;

  /*mp4memcpy(buf+i, handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfig, 
									 handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfigSize);*/
  
  mp4memcpy(buf+i, handle->videoDecSpecificInfo, handle->videoDecSpecificInfoSize);

  /*i += handle->moov->trakVideo->mdia->minf->stbl->stsd->avc1[0]->avcc->avcConfigSize;*/

  i += handle->videoDecSpecificInfoSize;

  if (writeFile(handle, buf, ts->avcc) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTTS(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STTS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTTS(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stts);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stts);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STTS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->sttsEntryCount);
  i += 4;

  /* Sample count and sample delta */
  for (j = 0; j < handle->videoSampleTable->sttsEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->sttsSampleCount[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->sttsSampleDelta[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTTSLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video STTS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTTSLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE / 2);
  if (buf2 == NULL)
    {
    mp4free(buf);
    return -1;
    }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stts);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STTS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->sttsEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Sample count and delta */

  /* Seek to the beginning of temporary files */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STTS_SAMPLE_COUNT) < 0)
      {
      mp4free(buf);
      mp4free(buf2);
      return -1;
      }
  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STTS_SAMPLE_DELTA) < 0)
      {
      mp4free(buf);
      mp4free(buf2);
      return -1;
      }

  left = handle->videoSampleTable->sttsEntryCount * 4; /* Bytes left in each file */

  while (left)
  {
    if (left > METADATA_COPY_BUFFERSIZE / 2)
      bytestoread = METADATA_COPY_BUFFERSIZE / 2;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STTS_SAMPLE_COUNT) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 4)
    {
      insertu32(buf + 2*j, ((mp4_u32 *)buf2)[j / 4]);
    }

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STTS_SAMPLE_DELTA) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 4)
    {
      insertu32(buf + 2*j + 4, ((mp4_u32 *)buf2)[j / 4]);
    }

    if (writeFile(handle, buf, 2 * bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSC(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STSC atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSC(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stsc);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsc);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSC);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stscEntryCount);
  i += 4;

  /* First chunk, samples per chunk and sample description index */
  for (j = 0; j < handle->videoSampleTable->stscEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stscFirstChunk[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stscSamplesPerChunk[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stscSampleDescriptionIndex[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stsc) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSZ(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STSZ atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSZ(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stsz);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsz);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSZ);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Sample size */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stszSampleSize);
  i += 4;

  /* Sample count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stszSampleCount);
  i += 4;

  /* Entry sizes */
  if (handle->videoSampleTable->stszSampleSize == 0)
  {
    for (j = 0; j < handle->videoSampleTable->stszSampleCount; j++)
    {
      insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stszEntrySize[j]);
      i += 4;
    }
  }

  if (writeFile(handle, buf, ts->stsz) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSZLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video STSZ atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSZLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u8  *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf2 == NULL)
    {
    mp4free(buf);
    return -1;
    }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsz);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSZ);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Sample size */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stszSampleSize);
  i += 4;

  /* Sample count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stszSampleCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Entry sizes */

  if (handle->videoSampleTable->stszSampleSize == 0)
  {
    /* Seek to the beginning of temporary file */

    if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STSZ_ENTRY_SIZE) < 0)
        {
        mp4free(buf);
        mp4free(buf2);
        return -1;
        }

    left = handle->videoSampleTable->stszSampleCount * 4; /* Bytes left in the file */

    while (left)
    {
      if (left > METADATA_COPY_BUFFERSIZE)
        bytestoread = METADATA_COPY_BUFFERSIZE;
      else
        bytestoread = left;

      if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STSZ_ENTRY_SIZE) < 0)
      {
        mp4free(buf);
        mp4free(buf2);

        return -1;
      }

      for (j = 0; j < bytestoread; j += 4)
      {
        insertu32(buf + j, ((mp4_u32 *)buf2)[j / 4]);
      }

      if (writeFile(handle, buf, bytestoread) < 0)
      {
        mp4free(buf);
        mp4free(buf2);

        return -1;
      }

      left -= bytestoread;
    }
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTCO(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STCO atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTCO(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stco);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STCO);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stcoEntryCount);
  i += 4;

  /* Chunk offsets */
  for (j = 0; j < handle->videoSampleTable->stcoEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stcoChunkOffset[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stco) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoCO64(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video CO64 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoCO64(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stco);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_CO64);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stcoEntryCount);
  i += 4;

  /* Chunk offsets */
  for (j = 0; j < handle->videoSampleTable->stcoEntryCount; j++)
  {
    insertu64(buf+i, (mp4_u64)handle->videoSampleTable->stcoChunkOffset[j]);
    i += 8;
  }

  if (writeFile(handle, buf, ts->stco) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoSTCOLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video STCO atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTCOLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;
  mp4_u32  bufferSize = (METADATA_COPY_BUFFERSIZE/sizeof(mp4_u64)) * sizeof(mp4_u64); //must be a multiple of u64
  																					  //METADATA_COPY_BUFFERSIZE is only guaranteed to be a multiple of 4
	
  buf = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf2 == NULL)
    {
    mp4free(buf);
    return -1;
    }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STCO);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stcoEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Chunk offsets */

  /* Seek to the beginning of temporary file */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STCO_CHUNK_OFFSET) < 0)
    {
    mp4free(buf);
    mp4free(buf2);
    return -1;
    }

  left = handle->videoSampleTable->stcoEntryCount * 8; /* Bytes left in the file */

  while (left)
  {
    if (left > bufferSize)
      bytestoread = bufferSize;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STCO_CHUNK_OFFSET) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 8)
    {
      ((mp4_u64 *)buf2)[j / 8] += (handle->metaDataSize + MDAT_HEADER_SIZE); /* Update chunk offsets */
      insertu32(buf + j/2, ((mp4_u64 *)buf2)[j / 8]);
    }

    if (writeFile(handle, buf, bytestoread/2) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoCO64LongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video CO64 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoCO64LongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;
  mp4_u32  bufferSize = (METADATA_COPY_BUFFERSIZE/sizeof(mp4_u64)) * sizeof(mp4_u64); //must be a multiple of u64
  																					  //METADATA_COPY_BUFFERSIZE is only guaranteed to be a multiple of 4
	
  buf = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf2 == NULL)
    {
    mp4free(buf);
    return -1;
    }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_CO64);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stcoEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Chunk offsets */

  /* Seek to the beginning of temporary file */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STCO_CHUNK_OFFSET) < 0)
    {
    mp4free(buf);
    mp4free(buf2);
    return -1;
    }

  left = handle->videoSampleTable->stcoEntryCount * 8; /* Bytes left in the file */

  while (left)
  {
    if (left > bufferSize)
      bytestoread = bufferSize;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STCO_CHUNK_OFFSET) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 8)
    {
      ((mp4_u64 *)buf2)[j / 8] += (handle->metaDataSize + MDAT_HEADER_SIZE); /* Update chunk offsets */
      insertu64(buf + j, ((mp4_u64 *)buf2)[j / 8]);
    }

    if (writeFile(handle, buf, bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSS(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video STSS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSS(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stss);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stss);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stssEntryCount);
  i += 4;

  /* Sample numbers */
  for (j = 0; j < handle->videoSampleTable->stssEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stssSampleNumber[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stss) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeVideoSTSSLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video STSS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSTSSLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf2 == NULL)
      {
      mp4free(buf);
      return -1;
      }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stss);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->videoSampleTable->stssEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Sample numbers */

  /* Seek to the beginning of temporary file */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_STSS_SAMPLE_NUMBER) < 0)
    {
    mp4free(buf);
    mp4free(buf2);
    return -1;
    }

  left = handle->videoSampleTable->stssEntryCount * 4; /* Bytes left in the file */

  while (left)
  {
    if (left > METADATA_COPY_BUFFERSIZE)
      bytestoread = METADATA_COPY_BUFFERSIZE;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_STSS_SAMPLE_NUMBER) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 4)
    {
      insertu32(buf + j, ((mp4_u32 *)buf2)[j / 4]);
    }

    if (writeFile(handle, buf, bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioTrak(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio track atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioTrak(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->trak);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_TRAK);
  i += 4;

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

  if (writeAudioTKHD(handle, ts) < 0)
    return -1;

  if (writeAudioMDIA(handle, ts) < 0)
    return -1;
  
  if (handle->audioUDTA)
    {
    if (writeUDTA(handle, handle->audioUDTA) < 0)
        return -1;
    }  

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioTKHD(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio TKHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioTKHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8      *buf;
  mp4_u32     i = 0;
  mp4_u32     u32;
  mp4_double  ud;


  buf = (mp4_u8 *)mp4malloc(ts->tkhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->tkhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_TKHD);
  i += 4;

  
  if (handle->audioDuration > MP4_INT_MAX)
  {
      mp4_u64     u64;
      /* Version and flags */
      buf[i++] = 1;
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 7;  /* Track enabled, used in movie and preview */
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Track ID */
      insertu32(buf+i, (mp4_u32)2);
      i += 4;
    
      /* Reserved */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Duration */
      if ( ( handle->audioTimeScale == 0 ) || ( handle->audioDuration == 0 ) )
          {
          ud = 0;
          }
      else
          {
          ud = (mp4_double)MVHD_TIMESCALE * (mp4_double)handle->audioDuration / (mp4_double)handle->audioTimeScale + (mp4_double)0.5;
          }
      u64 = (mp4_u64)ud;
      insertu64(buf+i, u64);
      i += 8;

  }
  else
  {
      /* Version and flags */
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 0;
      buf[i++] = 7;  /* Track enabled, used in movie and preview */
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Track ID */
      insertu32(buf+i, (mp4_u32)2);
      i += 4;
    
      /* Reserved */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Duration */
      if ( ( handle->audioTimeScale == 0 ) || ( handle->audioDuration == 0 ) )
          {
          ud = 0;
          }
      else
          {
          ud = (mp4_double)MVHD_TIMESCALE * (mp4_double)handle->audioDuration / (mp4_double)handle->audioTimeScale + (mp4_double)0.5;
          }
      u32 = (mp4_u32)ud;
      insertu32(buf+i, u32);
      i += 4;
  }
  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)0x0100);  /* Audio track */
  i += 2;

  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00010000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x40000000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);  /* Audio track */
  i += 4;

  insertu32(buf+i, (mp4_u32)0);  /* Audio track */
  i += 4;

  if (writeFile(handle, buf, ts->tkhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioMDIA(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio MDIA atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioMDIA(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mdia);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MDIA);
  i += 4;

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

  if (writeAudioMDHD(handle, ts) < 0)
    return -1;

  if (writeAudioHDLR(handle, ts) < 0)
    return -1;

  if (writeAudioMINF(handle, ts) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioMDHD(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio MDHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioMDHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  u32;


  buf = (mp4_u8 *)mp4malloc(ts->mdhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mdhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MDHD);
  i += 4;

  if (handle->audioDuration > MP4_INT_MAX)
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0x1000000);     //version 1 atom
      i += 4;
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu64(buf+i, (mp4_u64)u32);
      i += 8;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)handle->audioTimeScale);
      i += 4;
    
      /* Duration */
      insertu64(buf+i, handle->audioDuration);
      i += 8;
      
  }
  else
  {
      /* Version and flags */
      insertu32(buf+i, (mp4_u32)0);
      i += 4;
    
      /* Creation time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Modification time */
      if (getCurrentTime(&u32) < 0)
        u32 = 0;
      insertu32(buf+i, (mp4_u32)u32);
      i += 4;
    
      /* Timescale */
      insertu32(buf+i, (mp4_u32)handle->audioTimeScale);
      i += 4;
    
      /* Duration */
      insertu32(buf+i, (mp4_u32)handle->audioDuration);
      i += 4;
  }

  /* Language */
  insertu16(buf+i, (mp4_u16)0x55c4); /* 'und' */
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0x0000);
  i += 2;

  if (writeFile(handle, buf, ts->mdhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioHDLR(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio HDLR atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioHDLR(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->hdlr);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->hdlr);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_HDLR);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Handler type */
  buf[i++] = 's';
  buf[i++] = 'o';
  buf[i++] = 'u';
  buf[i++] = 'n';

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Empty string */
  buf[i++] = 0;

  if (writeFile(handle, buf, ts->hdlr) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioMINF(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio MINF atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioMINF(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->minf);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MINF);
  i += 4;

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

  if (writeSMHD(handle, ts) < 0)
    return -1;

  if (writeDINF(handle, ts) < 0)
    return -1;

  if (writeAudioSTBL(handle, ts) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32  writeSMHD(MP4HandleImp handle,
 *                      trakSize *ts)
 *
 * Description:
 *
 *   Write SMHD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeSMHD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(ts->smhd);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->smhd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SMHD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  if (writeFile(handle, buf, ts->smhd) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTBL(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio STBL atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTBL(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[8];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stbl);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STBL);
  i += 4;

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

  if (writeAudioSTSD(handle, ts) < 0)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (writeAudioSTTSLongClip(handle, ts) < 0)
      return -1;
  }
  else
  {
    if (writeAudioSTTS(handle, ts) < 0)
      return -1;
  }

  if (writeAudioSTSC(handle, ts) < 0)
    return -1;

  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (writeAudioSTSZLongClip(handle, ts) < 0)
      return -1;
    
	if (handle->audioSampleTable->stcoNeed64Bits)
	{
	  if (writeAudioCO64LongClip(handle, ts) < 0)
      	return -1;
	}
	else
	{
	  if (writeAudioSTCOLongClip(handle, ts) < 0)
        return -1;
	}
  }
  else
  {
    if (writeAudioSTSZ(handle, ts) < 0)
      return -1;

	if (handle->audioSampleTable->stcoNeed64Bits)
	{
      if (writeAudioCO64(handle, ts) < 0)
        return -1;
	}
	else
	{
      if (writeAudioSTCO(handle, ts) < 0)
        return -1;
	}
  }


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32  writeAudioSTSD(MP4HandleImp handle,
 *                           trakSize *ts)
 *
 * Description:
 *
 *   Write audio STSD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTSD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[16];
  mp4_u32 i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsd);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)1);
  i += 4;

  if (writeFile(handle, buf, 16) < 0)
    return -1;

  if (handle->type & MP4_TYPE_MPEG4_AUDIO)
  {
    if (writeMP4A(handle, ts) < 0)
      return -1;
  }
  else if (handle->type & MP4_TYPE_AMR_NB)
  {
    if (writeSAMR(handle, ts) < 0)
      return -1;
  }
  else if (handle->type & MP4_TYPE_AMR_WB)
  {
    if (writeSAWB(handle, ts) < 0)
      return -1;
  }
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (!handle->qcelpStoredAsMPEGAudio))
  {
    if (writeSQCP(handle, ts) < 0)
      return -1;
  }
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (handle->qcelpStoredAsMPEGAudio))
  {
    if (writeMP4A(handle, ts) < 0)
      return -1;
  }
  else
  {
  }

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMP4A(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write MP4A atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeMP4A(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(36);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->mp4a);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_MP4A);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)2);
  i += 2;

  insertu16(buf+i, (mp4_u16)16);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Timescale */
  insertu16(buf+i, (mp4_u16)handle->audioTimeScale);
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  if (writeFile(handle, buf, 36) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeAudioESD(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioESD(MP4HandleImp handle,
 *                         trakSize *ts)
 *
 * Description:
 *
 *   Write audio ESD atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioESD(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  bitrate = 0;
  mp4_u32 num_of_bytes = 0;
  mp4_u32 size = 0;
  mp4_u32 index = 0;
  mp4_u32 tempnum = 0;

  mp4_u32 size1, size2;
  mp4_u32 numofsizebytes1, numofsizebytes2;

  buf = (mp4_u8 *)mp4malloc(ts->esds);
  if (buf == NULL)
    return -1;

  /* Calculate the necessary size information */
  size1 = handle->audioDecSpecificInfoSize;
  if (size1 < 128)
    numofsizebytes1 = 1;
  else
  {
      numofsizebytes1 = 1;
      while ( size1 >= 128 ) 
      {
        size1 = size1 >> 7;
        numofsizebytes1++;
      }  
  }

  size2 = 14 + numofsizebytes1 + handle->audioDecSpecificInfoSize;
   if (size2 < 128)
      numofsizebytes2 = 1;
    else
    {
        numofsizebytes2 = 1;
        while ( size2 >= 128 ) 
        {
          size2 = size2 >> 7;
          numofsizebytes2++;
        }  
    }
    /* End of size calculations */

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->esds);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_ESD);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* ES_DescrTag */
  buf[i++] = 0x03;

  /* Size */
  size = 21 + numofsizebytes1 + numofsizebytes2 + handle->audioDecSpecificInfoSize; 
  if (size < 128)
    buf[i++] = (mp4_u8)(size);
  else
  {
      num_of_bytes = 0;
      while ( size >= 128 ) 
      {
        size = size >> 7;
        num_of_bytes++;
      }  
      size = 21 + numofsizebytes1 + numofsizebytes2 + handle->audioDecSpecificInfoSize; 
      for(index = num_of_bytes; index > 0; index--)  
      {
        tempnum = size >> (7 * index); 
        buf[i++] = (mp4_u8)(0x80 | (mp4_u8) tempnum);
        size -= (tempnum << (7 * index));
      }
      buf[i++] = (mp4_u8)size;
  }

  /* ES_ID */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  /* Flags */
  buf[i++] = 0;

  /* DecoderConfigDescrTag */
  buf[i++] = 0x04;

  /* Size */
  size =  14 + numofsizebytes1 + handle->audioDecSpecificInfoSize; 
  if (size < 128)
    buf[i++] = (mp4_u8)(size);
  else
  {
      num_of_bytes = 0;
      while ( size >= 128 ) 
      {
        size = size >> 7;
        num_of_bytes++;
      }  
      size =  14 + numofsizebytes1 + handle->audioDecSpecificInfoSize; 
      for(index = num_of_bytes; index > 0; index--)  
      {
        tempnum = size >> (7 * index); 
        buf[i++] = (mp4_u8)(0x80 | (mp4_u8) tempnum);
        size -= (tempnum << (7 * index));
      }
      buf[i++] = (mp4_u8)size;
  }

  /* ObjectTypeIndication */
  if (handle->type & MP4_TYPE_MPEG4_AUDIO)
    buf[i++] = 0x40;
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (handle->qcelpStoredAsMPEGAudio))
    buf[i++] = 0xE1;
  else
  {
  }

  /* Flags */
  buf[i++] = 0x15;

  /* BufferSizeDB */
  if (handle->type & MP4_TYPE_MPEG4_AUDIO) 
  {
      buf[i++] = 0x00;
      buf[i++] = 0x00;
      buf[i++] = 0x00;
  }
  else if ((handle->type & MP4_TYPE_QCELP_13K) && (handle->qcelpStoredAsMPEGAudio))
  { /* 4096 for QCELP 13K */
      buf[i++] = 0x00;
      buf[i++] = 0x10;
      buf[i++] = 0x00;
  }
  else
  {
  }


  if ((handle->audioDuration != 0) && (handle->audioTimeScale != 0))
    bitrate = (mp4_u32)((mp4_double)8 *
                              (mp4_double)handle->audioMediaDataSize /
                              ((mp4_double)handle->audioDuration /
                               (mp4_double)handle->audioTimeScale));
  else
    bitrate = 0;

  /* MaxBitrate */
  insertu32(buf+i, (mp4_u32)bitrate); /*0x00010000*/
  i += 4;

  /* AvgBitrate */
  insertu32(buf+i, (mp4_u32)bitrate);/*0x00008000*/
  i += 4;

  /* DecSpecificInfoTag */
  buf[i++] = 0x05;

  /* Size */
  size = handle->audioDecSpecificInfoSize;
  if (size < 128)
    buf[i++] = (mp4_u8)(size);
  else
  {
      num_of_bytes = 0;
      while ( size >= 128 ) 
      {
        size = size >> 7;
        num_of_bytes++;
      }  
      size = handle->audioDecSpecificInfoSize;
      for(index = num_of_bytes; index > 0; index--)  
      {
        tempnum = size >> (7 * index); 
        buf[i++] = (mp4_u8)(0x80 | (mp4_u8) tempnum);
        size -= (tempnum << (7 * index));
      }
      buf[i++] = (mp4_u8)size;
  }

  /* DecoderSpecificInfo */
  mp4memcpy(buf+i, handle->audioDecSpecificInfo, handle->audioDecSpecificInfoSize);
  i += handle->audioDecSpecificInfoSize;

  /* SLConfigDescrTag */
  buf[i++] = 0x06;

  /* Size */
  buf[i++] = 1;

  /* */
  buf[i++] = 2;

  if (writeFile(handle, buf, ts->esds) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeSAMR(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write SAMR atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeSAMR(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(36);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->samr);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SAMR);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)2);
  i += 2;

  insertu16(buf+i, (mp4_u16)16);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Timescale */
  insertu16(buf+i, (mp4_u16)handle->audioTimeScale);
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  if (writeFile(handle, buf, 36) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeDAMR(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeSAWB(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write SAWB atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeSAWB(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32 i = 0;


  buf = (mp4_u8 *)mp4malloc(36);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->sawb);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SAWB);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)2);
  i += 2;

  insertu16(buf+i, (mp4_u16)16);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Timescale */
  insertu16(buf+i, (mp4_u16)handle->audioTimeScale);
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  if (writeFile(handle, buf, 36) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeDAMR(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeDAMR(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write DAMR atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeDAMR(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[17];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->damr);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_DAMR);
  i += 4;

  /* Vendor */
  buf[i++] = 'S';
  buf[i++] = '6';
  buf[i++] = '0';
  buf[i++] = ' ';

  /* Decoder version */
  buf[i++] = 0;

  /* Mode set */
  insertu16(buf+i, (mp4_u16)handle->audioModeSet);
  i += 2;

  /* Mode change period */
  buf[i++] = 0;

  /* Frames per sample */
  buf[i++] = handle->audioFramesPerSample;

  if (writeFile(handle, buf, 17) < 0)
    return -1;

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTTS(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio STTS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTTS(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stts);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stts);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STTS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->sttsEntryCount);
  i += 4;

  /* Sample count and sample delta */
  for (j = 0; j < handle->audioSampleTable->sttsEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->sttsSampleCount[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->sttsSampleDelta[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTTSLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write audio STTS atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTTSLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE / 2);
  if (buf2 == NULL)
      {
      mp4free(buf);
      return -1;
      }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stts);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STTS);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->sttsEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Sample count and delta */

  /* Seek to the beginning of temporary files */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_AUDIO_STTS_SAMPLE_COUNT) < 0)
      {
      mp4free(buf);
      mp4free(buf2);
      return -1;
      }
  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_AUDIO_STTS_SAMPLE_DELTA) < 0)
      {
      mp4free(buf);
      mp4free(buf2);
      return -1;
      }

  left = handle->audioSampleTable->sttsEntryCount * 4; /* Bytes left in each file */

  while (left)
  {
    if (left > METADATA_COPY_BUFFERSIZE / 2)
      bytestoread = METADATA_COPY_BUFFERSIZE / 2;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_AUDIO_STTS_SAMPLE_COUNT) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 4)
    {
      insertu32(buf + 2*j, ((mp4_u32 *)buf2)[j / 4]);
    }

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_AUDIO_STTS_SAMPLE_DELTA) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 4)
    {
      insertu32(buf + 2*j + 4, ((mp4_u32 *)buf2)[j / 4]);
    }

    if (writeFile(handle, buf, 2 * bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTSC(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio STSC atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTSC(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stsc);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsc);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSC);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stscEntryCount);
  i += 4;

  /* First chunk, samples per chunk and sample description index */
  for (j = 0; j < handle->audioSampleTable->stscEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stscFirstChunk[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stscSamplesPerChunk[j]);
    i += 4;
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stscSampleDescriptionIndex[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stsc) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTSZ(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio STSZ atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTSZ(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stsz);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsz);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSZ);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Sample size */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stszSampleSize);
  i += 4;

  /* Sample count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stszSampleCount);
  i += 4;

  /* Entry sizes */
  if (handle->audioSampleTable->stszSampleSize == 0)
  {
    for (j = 0; j < handle->audioSampleTable->stszSampleCount; j++)
    {
      insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stszEntrySize[j]);
      i += 4;
    }
  }

  if (writeFile(handle, buf, ts->stsz) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTSZLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write audio STSZ atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTSZLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u8  *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf2 == NULL)
      {
      mp4free(buf);
      return -1;
      }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stsz);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STSZ);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Sample size */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stszSampleSize);
  i += 4;

  /* Sample count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stszSampleCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Entry sizes */

  if (handle->audioSampleTable->stszSampleSize == 0)
  {
    /* Seek to the beginning of temporary file */

    if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_AUDIO_STSZ_ENTRY_SIZE) < 0)
        {
        mp4free(buf);
        mp4free(buf2);
        return -1;
        }

    left = handle->audioSampleTable->stszSampleCount * 4; /* Bytes left in the file */

    while (left)
    {
      if (left > METADATA_COPY_BUFFERSIZE)
        bytestoread = METADATA_COPY_BUFFERSIZE;
      else
        bytestoread = left;

      if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_AUDIO_STSZ_ENTRY_SIZE) < 0)
      {
        mp4free(buf);
        mp4free(buf2);

        return -1;
      }

      for (j = 0; j < bytestoread; j += 4)
      {
        insertu32(buf + j, ((mp4_u32 *)buf2)[j / 4]);
      }

      if (writeFile(handle, buf, bytestoread) < 0)
      {
        mp4free(buf);
        mp4free(buf2);

        return -1;
      }

      left -= bytestoread;
    }
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeAudioSTCO(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio STCO atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTCO(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stco);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STCO);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stcoEntryCount);
  i += 4;

  /* Chunk offsets */
  for (j = 0; j < handle->audioSampleTable->stcoEntryCount; j++)
  {
    insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stcoChunkOffset[j]);
    i += 4;
  }

  if (writeFile(handle, buf, ts->stco) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAudioCO64(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write audio CO64 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioCO64(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;


  buf = (mp4_u8 *)mp4malloc(ts->stco);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_CO64);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stcoEntryCount);
  i += 4;

  /* Chunk offsets */
  for (j = 0; j < handle->audioSampleTable->stcoEntryCount; j++)
  {
    insertu64(buf+j, (mp4_u64)handle->audioSampleTable->stcoChunkOffset[j]);
  }

  if (writeFile(handle, buf, ts->stco) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAudioSTCOLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write audio STCO atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioSTCOLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;
  mp4_u32  bufferSize = (METADATA_COPY_BUFFERSIZE/sizeof(mp4_u64)) * sizeof(mp4_u64); //must be a multiple of u64
  																					  //METADATA_COPY_BUFFERSIZE is only guaranteed to be a multiple of 4


  buf = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf2 == NULL)
      {
      mp4free(buf);
      return -1;
      }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_STCO);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stcoEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Chunk offsets */

  /* Seek to the beginning of temporary file */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_AUDIO_STCO_CHUNK_OFFSET) < 0)
  {
    mp4free(buf);
    mp4free(buf2);
    return -1;
  }

  left = handle->audioSampleTable->stcoEntryCount * 8; /* Bytes left in the file */

  while (left)
  {
    if (left > bufferSize)
      bytestoread = bufferSize;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_AUDIO_STCO_CHUNK_OFFSET) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 8)
    {
      ((mp4_u64 *)buf2)[j / 8] += (handle->metaDataSize + MDAT_HEADER_SIZE); /* Update chunk offsets */
      insertu32(buf + j/2, ((mp4_u64 *)buf2)[j / 8]);
    }

    if (writeFile(handle, buf, bytestoread/2) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAudioCO64LongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write audio CO64 atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAudioCO64LongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  j;
  mp4_u32  left;
  mp4_u32  bytestoread;
  mp4_u32  bufferSize = (METADATA_COPY_BUFFERSIZE/sizeof(mp4_u64)) * sizeof(mp4_u64); //must be a multiple of u64
  																					  //METADATA_COPY_BUFFERSIZE is only guaranteed to be a multiple of 4


  buf = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(bufferSize);
  if (buf2 == NULL)
      {
      mp4free(buf);
      return -1;
      }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->stco);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_CO64);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Entry count */
  insertu32(buf+i, (mp4_u32)handle->audioSampleTable->stcoEntryCount);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Chunk offsets */

  /* Seek to the beginning of temporary file */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_AUDIO_STCO_CHUNK_OFFSET) < 0)
  {
    mp4free(buf);
    mp4free(buf2);
    return -1;
  }

  left = handle->audioSampleTable->stcoEntryCount * 8; /* Bytes left in the file */

  while (left)
  {
    if (left > bufferSize)
      bytestoread = bufferSize;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_AUDIO_STCO_CHUNK_OFFSET) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    for (j = 0; j < bytestoread; j += 8)
    {
      ((mp4_u64 *)buf2)[j / 8] += (handle->metaDataSize + MDAT_HEADER_SIZE); /* Update chunk offsets */
      insertu64(buf + j, ((mp4_u64 *)buf2)[j / 8]);
    }

    if (writeFile(handle, buf, bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMediaData(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function writes media data to the output file.
 *
 *   Before writing media data to the output file, meta data has
 *   been written to the file. Media data that is in tmpfile is
 *   read from there and written to the end of the output file.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 writeMediaData(MP4HandleImp handle)
{
  mp4_u8  *buf;
  mp4_u32 i = 0;
  mp4_u64 left;
  mp4_u32 bytestoread;


  buf = (mp4_u8 *)mp4malloc(1024);
  if (buf == NULL)
    return -1;


  i = formatMdatHeader(buf, handle->bytesInTmpFile);
  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);

    return -1;
  }

  /* Seek to the beginning of tmpfile */
  if (seekTmpFileAbs(handle, 0) < 0)
      {
      mp4free(buf);
      return -1;
      }

  left = handle->bytesInTmpFile;

  while (left)
  {
    if (left > 1024)
      bytestoread = 1024;
    else
      bytestoread = left;

    if (readTmpFile(handle, buf, bytestoread) < 0)
    {
      mp4free(buf);

      return -1;
    }

    if (writeFile(handle, buf, bytestoread) < 0)
    {
      mp4free(buf);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 insertu64(mp4_u8 *buf,
 *                     mp4_u64 value)
 *
 * Description:
 *
 *   This function writes value into buf taking into account the endianism
 *   of the current computer.
 *
 *   It is assumed that the caller of the function has allocated enough
 *   space for buf.
 *
 * Parameters:
 *
 *   buf      Buffer to write to
 *   value    Value to write to buf
 *
 * Return value:
 *
 *   0        Success
 *
 */
mp4_i32 insertu64(mp4_u8 *buf, mp4_u64 value)
{
  mp4_u64 u64;


  u64 = u64endian(value);
  mp4memcpy(buf, &u64, sizeof(mp4_u64));

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 insertu32(mp4_u8 *buf,
 *                     mp4_u32 value)
 *
 * Description:
 *
 *   This function writes value into buf taking into account the endianism
 *   of the current computer.
 *
 *   It is assumed that the caller of the function has allocated enough
 *   space for buf.
 *
 * Parameters:
 *
 *   buf      Buffer to write to
 *   value    Value to write to buf
 *
 * Return value:
 *
 *   0        Success
 *
 */
mp4_i32 insertu32(mp4_u8 *buf, mp4_u32 value)
{
  mp4_u32 u32;


  u32 = u32endian(value);
  mp4memcpy(buf, &u32, 4);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 insertu16(mp4_u8 *buf,
 *                     mp4_u16 value)
 *
 * Description:
 *
 *   This function writes value into buf taking into account the endianism
 *   of the current computer.
 *
 *   It is assumed that the caller of the function has allocated enough
 *   space for buf.
 *
 * Parameters:
 *
 *   buf      Buffer to write to
 *   value    Value to write to buf
 *
 * Return value:
 *
 *   0        Success
 *
 */
mp4_i32 insertu16(mp4_u8 *buf, mp4_u16 value)
{
  mp4_u16 u16;


  u16 = u16endian(value);
  mp4memcpy(buf, &u16, 2);

  return 0;
}


/*
 * Function:
 *
 *   mp4_i32 writeMetaDataTmp(MP4HandleImp handle)
 *
 * Description:
 *
 *   This function writes metadata accumulated in memory to the disk.
 *
 * Parameters:
 *
 *   handle    MP4 library handle
 *
 * Return value:
 *
 *   0         Success
 *   -1        Error
 *
 */
mp4_i32 writeMetaDataTmp(MP4HandleImp handle)
{
  PRINT((_L("e_writemetadatatmp 1")));
  if (handle->videoSampleTable)
  {
  	PRINT((_L("e_writemetadatatmp_writemetadata_video 1")));
    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->sttsSampleCount,
                             handle->videoSampleTable->sttsCurrentEntryCount * sizeof(mp4_u32),
                             METADATAFILE_VIDEO_STTS_SAMPLE_COUNT) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->sttsSampleDelta,
                             handle->videoSampleTable->sttsCurrentEntryCount * sizeof(mp4_u32),
                             METADATAFILE_VIDEO_STTS_SAMPLE_DELTA) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->stszEntrySize,
                             handle->videoSampleTable->stszCurrentSampleCount * sizeof(mp4_u32),
                             METADATAFILE_VIDEO_STSZ_ENTRY_SIZE) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->stcoChunkOffset,
                             handle->videoSampleTable->stcoCurrentEntryCount * sizeof(mp4_u64),
                             METADATAFILE_VIDEO_STCO_CHUNK_OFFSET) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->stssSampleNumber,
                             handle->videoSampleTable->stssCurrentEntryCount * sizeof(mp4_u32),
                             METADATAFILE_VIDEO_STSS_SAMPLE_NUMBER) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->videoSampleTable->sdtpSampleDependency,
                             handle->videoSampleTable->sdtpCurrentEntryCount * sizeof(mp4_u8),
                             METADATAFILE_VIDEO_SDTP_SAMPLE_DEPENDENCY) < 0)
      return -1;
  	PRINT((_L("e_writemetadatatmp_writemetadata_video 0")));
  }

  if (handle->audioSampleTable)
  {
  	PRINT((_L("e_writemetadatatmp_writemetadata_audio 1")));
    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->audioSampleTable->sttsSampleCount,
                             handle->audioSampleTable->sttsCurrentEntryCount * sizeof(mp4_u32),
                             METADATAFILE_AUDIO_STTS_SAMPLE_COUNT) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->audioSampleTable->sttsSampleDelta,
                             handle->audioSampleTable->sttsCurrentEntryCount * sizeof(mp4_u32),
                             METADATAFILE_AUDIO_STTS_SAMPLE_DELTA) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->audioSampleTable->stszEntrySize,
                             handle->audioSampleTable->stszCurrentSampleCount * sizeof(mp4_u32),
                             METADATAFILE_AUDIO_STSZ_ENTRY_SIZE) < 0)
      return -1;

    if (writeMetaDataFileNum(handle,
                             (mp4_u8 *)handle->audioSampleTable->stcoChunkOffset,
                             handle->audioSampleTable->stcoCurrentEntryCount * sizeof(mp4_u64),
                             METADATAFILE_AUDIO_STCO_CHUNK_OFFSET) < 0)
      return -1;
  	PRINT((_L("e_writemetadatatmp_writemetadata_audio 0")));      
  }

	if (handle->videoSampleTable)
		{
  		handle->videoSampleTable->sttsCurrentEntryCount  = 0;
  		handle->videoSampleTable->stszCurrentSampleCount = 0;
  		handle->videoSampleTable->stcoCurrentEntryCount  = 0;
  		handle->videoSampleTable->stssCurrentEntryCount  = 0;
  		handle->videoSampleTable->sdtpCurrentEntryCount  = 0;  
		}

	if (handle->audioSampleTable)
		{		
  		handle->audioSampleTable->sttsCurrentEntryCount  = 0;
  		handle->audioSampleTable->stszCurrentSampleCount = 0;
  		handle->audioSampleTable->stcoCurrentEntryCount  = 0;		
		}

  handle->metaDataBlocks = 0;
  handle->metaDataOnDisk = MP4TRUE;

  PRINT((_L("e_writemetadatatmp 0")));
  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeSQCP(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write SQCP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeSQCP(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(36);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->sqcp);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SQCP);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)2);
  i += 2;

  insertu16(buf+i, (mp4_u16)16);
  i += 2;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Timescale */
  insertu16(buf+i, (mp4_u16)handle->audioTimeScale);
  i += 2;

  /* Reserved */
  insertu16(buf+i, (mp4_u16)0);
  i += 2;

  if (writeFile(handle, buf, 36) < 0)
  {
    mp4free(buf);

    return -1;
  }

  if (writeDQCP(handle, ts) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeDQCP(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write DQCP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeDQCP(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  buf[14];
  mp4_u32  i = 0;


  /* Size */
  insertu32(buf+i, (mp4_u32)ts->dqcp);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_DQCP);
  i += 4;

  /* Vendor */
  buf[i++] = 'n';
  buf[i++] = 'o';
  buf[i++] = 'k';
  buf[i++] = 'i';

  /* Decoder version */
  buf[i++] = 0;

  /* Mode set */
//  insertu16(buf+i, (mp4_u16)handle->audioModeSet);
//  i += 2;

  /* Mode change period */
//  buf[i++] = 0;

  /* Frames per sample */
  buf[i++] = handle->audioFramesPerSample;

  if (writeFile(handle, buf, 14) < 0)
    return -1;

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 calculateES_DescriptorSize(mp4_u32 type)
 *
 * Description:
 *
 *   Calculated the ES_Descriptor size inside the ESDS box.
 *    Updates handle->ES_DescriptorSize;
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *  type        the media type:
 *                  MP4_TYPE_QCELP_13K
 *                  MP4_TYPE_MPEG_AUDIO
 *                  MP4_TYPE_MPEG4_VIDEO 
 *
 * Return value:
 *
 *   0        Success
 *
 */
mp4_i32 calculateES_DescriptorSize(MP4HandleImp handle, mp4_u32 type)
{
  mp4_u32 size1, size2, size3;
  mp4_u32 numofsizebytes1, numofsizebytes2, numofsizebytes3;
  mp4_u32 decspecinfosize = 0;

  if (((type & MP4_TYPE_QCELP_13K) && (handle->qcelpStoredAsMPEGAudio)) ||
        (type & MP4_TYPE_MPEG4_AUDIO))
    decspecinfosize = handle->audioDecSpecificInfoSize;
  else /* MPEG video case */
    decspecinfosize = handle->videoDecSpecificInfoSize;

  size1 = decspecinfosize;
  if (size1 < 128)
    numofsizebytes1 = 1;
  else
  {
      numofsizebytes1 = 1;
      while ( size1 >= 128 ) 
      {
        size1 = size1 >> 7;
        numofsizebytes1++;
      }  
  }

  size2 = 14 + numofsizebytes1 + decspecinfosize;
   if (size2 < 128)
      numofsizebytes2 = 1;
    else
    {
        numofsizebytes2 = 1;
        while ( size2 >= 128 ) 
        {
          size2 = size2 >> 7;
          numofsizebytes2++;
        }  
    }

  size3 = 21 + numofsizebytes1 + numofsizebytes2 + decspecinfosize;
   if (size3 < 128)
      numofsizebytes3 = 1;
    else
    {
        numofsizebytes3 = 1;
        while ( size3 >= 128 ) 
        {
          size3 = size3 >> 7;
          numofsizebytes3++;
        }  
    }

    /* ES_DescriptorSize contains the size of the ES_Descriptor Field, including the 0x03 (ES_ID Tag) */
    handle->ES_DescriptorSize =  21 + numofsizebytes1 + numofsizebytes2 + decspecinfosize + numofsizebytes3 + 1;
    return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeUDTA(MP4HandleImp handle,
 *                     userDataAtom *udta)
 *
 * Description:
 *
 *   Write UDTA atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32  writeUDTA(MP4HandleImp handle, userDataAtom *udta)
{
  mp4_u8  buf[8];
  mp4_u32 i = 0;


  /* Size */
  insertu32(buf+i, 8 + (mp4_u32)udta->atomcontentsize);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_UDTA);
  i += 4;

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

  if (writeFile(handle, udta->contentdata, udta->atomcontentsize) < 0)
    return -1;

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 updateVideoDependencyMetaData(MP4HandleImp handle,
 *                                         mp4_u8 aDependsOn,
 *                                         mp4_u8 aIsDependentOn,
 *                                         mp4_u8 aHasRedundancy)
 *
 * Description:
 *
 *   Updates SDTP video dependency data
 *
 * Parameters:
 *
 *   handle          MP4 library handle
 *   aDependsOn      This frame's dependency on other frames
 *   aIsDependentOn  Other frames's dependency on this frame
 *   aHasRedundancy  Whether there is redundant coding in this frame
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 updateVideoDependencyMetaData(MP4HandleImp handle, mp4_u8 aDependsOn, mp4_u8 aIsDependentOn, mp4_u8 aHasRedundancy)
{
  if (handle->flags & MP4_FLAG_LONGCLIP)
  {
    if (handle->videoSampleTable->sdtpCurrentEntryCount == handle->videoSampleTable->sdtpMaxEntryCount)
    {
      void *p;

      p = mp4realloc(handle->videoSampleTable->sdtpSampleDependency,
                     2 * sizeof(mp4_u8) * handle->videoSampleTable->sdtpMaxEntryCount,
                     sizeof(mp4_u8) * handle->videoSampleTable->stssMaxEntryCount);
      if (p == NULL)
        return -1;

      handle->videoSampleTable->sdtpSampleDependency = (mp4_u8 *)p;

      handle->videoSampleTable->sdtpMaxEntryCount *= 2;
    }

    handle->videoSampleTable->sdtpSampleDependency[handle->videoSampleTable->sdtpCurrentEntryCount] = (aDependsOn << 4) | (aIsDependentOn << 2) | (aHasRedundancy);
    handle->videoSampleTable->sdtpCurrentEntryCount++;
    handle->videoSampleTable->sdtpEntryCount++;
  }
  else
  {
    if (handle->videoSampleTable->sdtpEntryCount == handle->videoSampleTable->sdtpMaxEntryCount)
    {
      void *p;

      p = mp4realloc(handle->videoSampleTable->sdtpSampleDependency,
                     2 * sizeof(mp4_u8) * handle->videoSampleTable->sdtpMaxEntryCount,
                     sizeof(mp4_u8) * handle->videoSampleTable->sdtpMaxEntryCount);
      if (p == NULL)
        return -1;

      handle->videoSampleTable->sdtpSampleDependency = (mp4_u8 *)p;

      handle->videoSampleTable->sdtpMaxEntryCount *= 2;
    }

    handle->videoSampleTable->sdtpSampleDependency[handle->videoSampleTable->sdtpEntryCount] = (aDependsOn << 4) | (aIsDependentOn << 2) | (aHasRedundancy);
    handle->videoSampleTable->sdtpEntryCount++;
  }

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoSDTP(MP4HandleImp handle,
 *                          trakSize *ts)
 *
 * Description:
 *
 *   Write video SDTP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSDTP(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;
  mp4_u32  j;

  buf = (mp4_u8 *)mp4malloc(ts->sdtp);
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->sdtp);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SDTP);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Sample dependencies */
  for (j = 0; j < handle->videoSampleTable->sdtpEntryCount; j++)
  {
    buf[i++] = (mp4_u8)handle->videoSampleTable->sdtpSampleDependency[j];
  }

  if (writeFile(handle, buf, ts->sdtp) < 0)
  {
    mp4free(buf);

    return -1;
  }

  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeVideoSDTPLongClip(MP4HandleImp handle,
 *                                  trakSize *ts)
 *
 * Description:
 *
 *   Write video SDTP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeVideoSDTPLongClip(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8   *buf;
  mp4_u8   *buf2;
  mp4_u32  i = 0;
  mp4_u32  left;
  mp4_u32  bytestoread;


  buf = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE);
  if (buf == NULL)
    return -1;

  buf2 = (mp4_u8 *)mp4malloc(METADATA_COPY_BUFFERSIZE / 2);
  if (buf2 == NULL)
    {
    mp4free(buf);
    return -1;
    }

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->sdtp);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_SDTP);
  i += 4;

  /* Version and flags */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  if (writeFile(handle, buf, i) < 0)
  {
    mp4free(buf);
    mp4free(buf2);

    return -1;
  }

  /* Sample count and delta */

  /* Seek to the beginning of temporary files */

  if (seekMetaDataFileNumAbs(handle, 0, METADATAFILE_VIDEO_SDTP_SAMPLE_DEPENDENCY) < 0)
      {
      mp4free(buf);
      mp4free(buf2);
      return -1;
      }

  left = handle->videoSampleTable->sdtpEntryCount; /* Bytes left in each file */

  while (left)
  {
    if (left > METADATA_COPY_BUFFERSIZE / 2)
      bytestoread = METADATA_COPY_BUFFERSIZE / 2;
    else
      bytestoread = left;

    if (readMetaDataFileNum(handle, buf2, bytestoread, METADATAFILE_VIDEO_SDTP_SAMPLE_DEPENDENCY) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    if (writeFile(handle, buf2, bytestoread) < 0)
    {
      mp4free(buf);
      mp4free(buf2);

      return -1;
    }

    left -= bytestoread;
  }

  mp4free(buf);
  mp4free(buf2);


  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 writeAVCP(MP4HandleImp handle,
 *                     trakSize *ts)
 *
 * Description:
 *
 *   Write AVCP atom.
 *
 * Parameters:
 *
 *   handle   MP4 library handle
 *   ts       Atom sizes
 *
 * Return value:
 *
 *   0        Success
 *   -1       Error
 *
 */
mp4_i32 writeAVCP(MP4HandleImp handle, trakSize *ts)
{
  mp4_u8  *buf;
  mp4_u32  i = 0;


  buf = (mp4_u8 *)mp4malloc(86+7); // 86 = size of VisualSampleEntry, 7 = size of AVCDecoderConfigurationRecord
  								 // with PPS and SPS sizes set to 0
  if (buf == NULL)
    return -1;

  /* Size */
  insertu32(buf+i, (mp4_u32)ts->avcp);
  i += 4;

  /* Atom type */
  insertu32(buf+i, (mp4_u32)ATOMTYPE_AVCP);
  i += 4;

  /* Reserved */
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;
  buf[i++] = 0;

  /* Data reference index */
  insertu16(buf+i, (mp4_u16)1);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  /* Width */
  insertu16(buf+i, (mp4_u16)handle->videoWidth);
  i += 2;

  /* Height */
  insertu16(buf+i, (mp4_u16)handle->videoHeight);
  i += 2;

  /* Reserved */
  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0x00480000);
  i += 4;

  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)1);
  i += 2;

// Compressorname
  buf[i++] = 14;
  insertu32(buf+i, (mp4_u32)0x41564320); // "AVC "
  i += 4;
  insertu32(buf+i, (mp4_u32)0x70617261); // "para"
  i += 4;
  insertu32(buf+i, (mp4_u32)0x6d657465); // "mete"
  i += 4;
  insertu16(buf+i, (mp4_u16)0x7273); // "rs"
  i += 2;
  buf[i++] = 0;
  insertu32(buf+i, (mp4_u32)0);
  i += 4;
  insertu32(buf+i, (mp4_u32)0);
  i += 4;
  insertu32(buf+i, (mp4_u32)0);
  i += 4;
  insertu32(buf+i, (mp4_u32)0);
  i += 4;

  insertu16(buf+i, (mp4_u16)24);
  i += 2;

  insertu16(buf+i, (mp4_u16)0xffff);
  i += 2;

  // AVCC without picparams & seqparams
  mp4memcpy(buf+i, handle->videoDecSpecificInfo, 5); // 5 = configurationVersion + AVCProfileIndication +
													 //     profile_compatibility + AVCLevelIndication +
													 //     '111111' + lengthSizeMinusOne (2 bits)
  i += 5;
  buf[i++] = 0xE0 | 0; // '111' + numOfSequenceParameterSets (5 bits)
  buf[i++] = 0;        // numOfPictureParameterSets
  
  if (writeFile(handle, buf, 86+7) < 0)
  {
    mp4free(buf);

    return -1;
  }
  
  mp4free(buf);

  return 0;
}

/*
 * Function:
 *
 *   mp4_i32 formatMdatHeader(mp4_u8 *buffer, 
 *                            mp4_u64 size)
 *
 * Description:
 *
 *   Formats the MDAT header into buffer
 *
 * Parameters:
 *
 *   buffer   buffer to write the MDAT header into
 *   size     Size of the media data (excluding the MDAT header)
 *
 * Return value:
 *
 *   size of header
 *
 */
mp4_i32 formatMdatHeader(mp4_u8 *buf, mp4_u64 size)
{
  TInt i = 0;
  if (size < MP4_INT_MAX)
  {
  	//insert free box
    insertu32(buf, (mp4_u32)8);
    i += sizeof(mp4_u32);
    insertu32(buf + i, (mp4_u32)ATOMTYPE_FREE);
    i += sizeof(mp4_u32);
    
    //insert mdat box
    insertu32(buf + i, (mp4_u32)size + 8);
    i += sizeof(mp4_u32);
    insertu32(buf + i, (mp4_u32)ATOMTYPE_MDAT);
    i += sizeof(mp4_u32);
  }
  else
  {
    //insert mdat box
    insertu32(buf + i, (mp4_u32)1);
    i += sizeof(mp4_u32);
    insertu32(buf + i, (mp4_u32)ATOMTYPE_MDAT);
    i += sizeof(mp4_u32);
	insertu64(buf + i, (mp4_u64)size + 16);        
	i += sizeof(mp4_u64);
  }

  ASSERT(MDAT_HEADER_SIZE == i);  
  return i;
}
	
// End of File