system: Linux mars.sprixweb.com 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
cmd: 

Direktori : /usr/local/src/mod_h264_streaming-2.2.7/src/
Upload File :
Current File : //usr/local/src/mod_h264_streaming-2.2.7/src/mp4_writer.c

/*******************************************************************************
 mp4_writer.c - A library for writing MPEG4.

 Copyright (C) 2007-2009 CodeShop B.V.
 http://www.code-shop.com

 For licensing see the LICENSE file
******************************************************************************/ 

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef __cplusplus
#define __STDC_FORMAT_MACROS // C++ should define this for PRIu64
#define __STDC_LIMIT_MACROS  // C++ should define this for UINT64_MAX
#endif

#include "mp4_writer.h"
#include "mp4_io.h"
#include <stdlib.h>
#include <string.h>

static unsigned char* atom_writer_unknown(unknown_atom_t* atoms,
                                          unsigned char* buffer)
{
  while(atoms)
  {
    size_t size = read_32((const unsigned char*)atoms->atom_);
    memcpy(buffer, atoms->atom_, size);
    buffer += size;
    atoms = atoms->next_;
  }

  return buffer;
}

extern unsigned char* atom_writer(struct unknown_atom_t* unknown_atoms,
                                  atom_write_list_t* atom_write_list,
                                  unsigned int atom_write_list_size,
                                  unsigned char* buffer)
{
  unsigned i;
  const int write_box64 = 0;

  for(i = 0; i != atom_write_list_size; ++i)
  {
    if(atom_write_list[i].source_ != 0)
    {
      unsigned char* atom_start = buffer;
      // atom size
      if(write_box64)
      {
        write_32(buffer, 1); // box64
      }
      buffer += 4;

      // atom type
      buffer = write_32(buffer, atom_write_list[i].type_);
      if(write_box64)
      {
        buffer += 8; // box64
      }

      // atom payload
      buffer = atom_write_list[i].writer_(atom_write_list[i].source_, buffer);

      if(write_box64)
        write_64(atom_start + 8, buffer - atom_start);
      else
        write_32(atom_start, (uint32_t)(buffer - atom_start));
    }
  }

  if(unknown_atoms)
  {
    buffer = atom_writer_unknown(unknown_atoms, buffer);
  }

  return buffer;
}

static unsigned char* tkhd_write(void const* atom, unsigned char* buffer)
{
  tkhd_t const* tkhd = (tkhd_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, tkhd->version_);
  buffer = write_24(buffer, tkhd->flags_);

  if(tkhd->version_ == 0)
  {
    buffer = write_32(buffer, (uint32_t)tkhd->creation_time_);
    buffer = write_32(buffer, (uint32_t)tkhd->modification_time_);
    buffer = write_32(buffer, tkhd->track_id_);
    buffer = write_32(buffer, tkhd->reserved_);
    buffer = write_32(buffer, (uint32_t)tkhd->duration_);
  }
  else
  {
    buffer = write_64(buffer, tkhd->creation_time_);
    buffer = write_64(buffer, tkhd->modification_time_);
    buffer = write_32(buffer, tkhd->track_id_);
    buffer = write_32(buffer, tkhd->reserved_);
    buffer = write_64(buffer, tkhd->duration_);
  }

  buffer = write_32(buffer, tkhd->reserved2_[0]);
  buffer = write_32(buffer, tkhd->reserved2_[1]);
  buffer = write_16(buffer, tkhd->layer_);
  buffer = write_16(buffer, tkhd->predefined_);
  buffer = write_16(buffer, tkhd->volume_);
  buffer = write_16(buffer, tkhd->reserved3_);

  for(i = 0; i != 9; ++i)
  {
    buffer = write_32(buffer, tkhd->matrix_[i]);
  }

  buffer = write_32(buffer, tkhd->width_);
  buffer = write_32(buffer, tkhd->height_);

  return buffer;
}

static unsigned char* mdhd_write(void const* atom, unsigned char* buffer)
{
  mdhd_t const* mdhd = (mdhd_t const*)atom;

  buffer = write_8(buffer, mdhd->version_);
  buffer = write_24(buffer, mdhd->flags_);

  if(mdhd->version_ == 0)
  {
    buffer = write_32(buffer, (uint32_t)mdhd->creation_time_);
    buffer = write_32(buffer, (uint32_t)mdhd->modification_time_);
    buffer = write_32(buffer, mdhd->timescale_);
    buffer = write_32(buffer, (uint32_t)mdhd->duration_);
  }
  else
  {
    buffer = write_64(buffer, mdhd->creation_time_);
    buffer = write_64(buffer, mdhd->modification_time_);
    buffer = write_32(buffer, mdhd->timescale_);
    buffer = write_64(buffer, mdhd->duration_);
  }

  buffer = write_16(buffer,
                    ((mdhd->language_[0] - 0x60) << 10) +
                    ((mdhd->language_[1] - 0x60) << 5) +
                    ((mdhd->language_[2] - 0x60) << 0));

  buffer = write_16(buffer, mdhd->predefined_);

  return buffer;
}

static unsigned char* vmhd_write(void const* atom, unsigned char* buffer)
{
  vmhd_t const* vmhd = (vmhd_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, vmhd->version_);
  buffer = write_24(buffer, vmhd->flags_);
  buffer = write_16(buffer, vmhd->graphics_mode_);
  for(i = 0; i != 3; ++i)
  {
    buffer = write_16(buffer, vmhd->opcolor_[i]);
  }

  return buffer;
}

static unsigned char* smhd_write(void const* atom, unsigned char* buffer)
{
  smhd_t const* smhd = (smhd_t const*)atom;

  buffer = write_8(buffer, smhd->version_);
  buffer = write_24(buffer, smhd->flags_);

  buffer = write_16(buffer, smhd->balance_);
  buffer = write_16(buffer, smhd->reserved_);

  return buffer;
}

static unsigned char* dref_write(void const* atom, unsigned char* buffer)
{
  unsigned int i;
  dref_t const* dref = (dref_t const*)atom;

  buffer = write_8(buffer, dref->version_);
  buffer = write_24(buffer, dref->flags_);
  buffer = write_32(buffer, dref->entry_count_);

  for(i = 0; i != dref->entry_count_; ++i)
  {
    dref_table_t* entry = &dref->table_[i];
    if(entry->flags_ == 0x000001)
    {
      write_32(buffer + 0, 12);
      write_32(buffer + 4, FOURCC('u', 'r', 'l', ' '));
      write_32(buffer + 8, entry->flags_);
      buffer += 12;
    }
    else
    {
    // TODO: implement urn and url
    }
  }

  return buffer;
}

static unsigned char* dinf_write(void const* atom, unsigned char* buffer)
{
  dinf_t const* dinf = (dinf_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('d', 'r', 'e', 'f'), dinf->dref_, &dref_write },
  };

  buffer = atom_writer(NULL,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* hdlr_write(void const* atom, unsigned char* buffer)
{
  hdlr_t const* hdlr = (hdlr_t const*)atom;
  buffer = write_8(buffer, hdlr->version_);
  buffer = write_24(buffer, hdlr->flags_);

  buffer = write_32(buffer, hdlr->predefined_);
  buffer = write_32(buffer, hdlr->handler_type_);
  buffer = write_32(buffer, hdlr->reserved1_);
  buffer = write_32(buffer, hdlr->reserved2_);
  buffer = write_32(buffer, hdlr->reserved3_);
  if(hdlr->name_)
  {
    char const* p;
    if(hdlr->predefined_ == FOURCC('m', 'h', 'l', 'r'))
    {
      buffer = write_8(buffer, (unsigned int)(strlen(hdlr->name_)));
    }

    for(p = hdlr->name_; *p; ++p)
    {
      buffer = write_8(buffer, *p);
    }
  }

  return buffer;
}

static unsigned char*
video_sample_entry_write(video_sample_entry_t const* sample_entry,
                         unsigned char* buffer)
{
  buffer = write_16(buffer, sample_entry->version_);
  buffer = write_16(buffer, sample_entry->revision_level_);
  buffer = write_32(buffer, sample_entry->vendor_);
  buffer = write_32(buffer, sample_entry->temporal_quality_);
  buffer = write_32(buffer, sample_entry->spatial_quality_);
  buffer = write_16(buffer, sample_entry->width_);
  buffer = write_16(buffer, sample_entry->height_);
  buffer = write_32(buffer, sample_entry->horiz_resolution_);
  buffer = write_32(buffer, sample_entry->vert_resolution_);
  buffer = write_32(buffer, sample_entry->data_size_);
  buffer = write_16(buffer, sample_entry->frame_count_);
  memcpy(buffer, sample_entry->compressor_name_, 32);
  buffer += 32;
  buffer = write_16(buffer, sample_entry->depth_);
  buffer = write_16(buffer, sample_entry->color_table_id_);

  return buffer;
}

static unsigned char*
audio_sample_entry_write(audio_sample_entry_t const* sample_entry,
                         unsigned char* buffer)
{
  buffer = write_16(buffer, sample_entry->version_);
  buffer = write_16(buffer, sample_entry->revision_);
  buffer = write_32(buffer, sample_entry->vendor_);
  buffer = write_16(buffer, sample_entry->channel_count_);
  buffer = write_16(buffer, sample_entry->sample_size_);
  buffer = write_16(buffer, sample_entry->compression_id_);
  buffer = write_16(buffer, sample_entry->packet_size_);
  buffer = write_32(buffer, sample_entry->samplerate_);

  return buffer;
}

static unsigned char* avcc_write(void const* atom, unsigned char* buffer)
{
  sample_entry_t const* sample_entry = (sample_entry_t const*)atom;

  memcpy(buffer, sample_entry->codec_private_data_,
         sample_entry->codec_private_data_length_);
  buffer += sample_entry->codec_private_data_length_;

  return buffer;
}

// returns the size of the descriptor including the tag and length
static unsigned int mp4_desc_len(uint32_t v)
{
  unsigned int bytes = 0;

  if(v >= 0x00200000)
    ++bytes;
  if(v >= 0x00004000)
    ++bytes;
  if(v >= 0x00000080)
    ++bytes;
  ++bytes;

  return 1 + bytes + v;
}

static unsigned char* mp4_write_desc_len(unsigned char* buffer, uint32_t v)
{
  if(v >= 0x00200000)
    buffer = write_8(buffer, (v >> 21) | 0x80);
  if(v >= 0x00004000)
    buffer = write_8(buffer, (v >> 14) | 0x80);
  if(v >= 0x00000080)
    buffer = write_8(buffer, (v >>  7) | 0x80);

  buffer = write_8(buffer, v & 0x7f);

  return buffer;
}

// http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
static unsigned char* esds_write(void const* atom, unsigned char* buffer)
{
  sample_entry_t const* sample_entry = (sample_entry_t const*)atom;

  uint32_t decoder_specific_descriptor_length =
    sample_entry->codec_private_data_length_ ?
      mp4_desc_len(sample_entry->codec_private_data_length_) : 0;
  uint32_t decoder_config_descriptor_length =
    13 + decoder_specific_descriptor_length;
  uint32_t elementary_stream_descriptor_length =
    3 + mp4_desc_len(decoder_config_descriptor_length);

  buffer = write_8(buffer, 0);              // version
  buffer = write_24(buffer, 0);             // flags

  buffer = write_8(buffer, MP4_ELEMENTARY_STREAM_DESCRIPTOR_TAG);
  buffer = mp4_write_desc_len(buffer, elementary_stream_descriptor_length);
  buffer = write_16(buffer, 1);             // track_id
  buffer = write_8(buffer, 0);              // flags

  buffer = write_8(buffer, MP4_DECODER_CONFIG_DESCRIPTOR_TAG);
  buffer = mp4_write_desc_len(buffer, decoder_config_descriptor_length);

  buffer = write_8(buffer, MP4_MPEG4Audio); // object_type_id
  buffer = write_8(buffer, 0x15);           // stream_type (0x11=vid, 0x15=aud)
  buffer = write_24(buffer, 0);             // buffer_size_db
  buffer = write_32(buffer, 0);             // max_bitrate
  buffer = write_32(buffer, 0);             // avg_bitrate

  if(sample_entry->codec_private_data_length_)
  {
    buffer = write_8(buffer, MP4_DECODER_SPECIFIC_DESCRIPTOR_TAG);
    buffer = mp4_write_desc_len(buffer,
      sample_entry->codec_private_data_length_);
    memcpy(buffer, sample_entry->codec_private_data_, 
      sample_entry->codec_private_data_length_);
    buffer += sample_entry->codec_private_data_length_;
  }

  buffer = write_8(buffer, 6);              // SL
  buffer = mp4_write_desc_len(buffer, 1);
  buffer = write_8(buffer, 0x02);

  return buffer;
}

static unsigned char* stsd_write(void const* atom, unsigned char* buffer)
{
  stsd_t const* stsd = (stsd_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, stsd->version_);
  buffer = write_24(buffer, stsd->flags_);
  buffer = write_32(buffer, stsd->entries_);
  for(i = 0; i != stsd->entries_; ++i)
  {
    sample_entry_t const* sample_entry = &stsd->sample_entries_[i];
    unsigned int j = 0;
    if(sample_entry->buf_ != NULL)
    {
      // just copy the sample_entry as we read it
      buffer = write_32(buffer, sample_entry->len_ + 8);
      buffer = write_32(buffer, sample_entry->fourcc_);
      for(j = 0; j != sample_entry->len_; ++j)
      {
        buffer = write_8(buffer, sample_entry->buf_[j]);
      }
    }
    else
    {
      unsigned char* sample_entry_buffer = buffer;
      buffer = write_32(buffer, 0);
      buffer = write_32(buffer, sample_entry->fourcc_);

      buffer = write_32(buffer, 0); // 6 bytes reserved
      buffer = write_16(buffer, 0);
      buffer = write_16(buffer, 1); // data reference index

      if(sample_entry->video_)
      {
        atom_write_list_t atom_write_list[] = {
          { FOURCC('a', 'v', 'c', 'C'), sample_entry, &avcc_write },
        };

        buffer = video_sample_entry_write(sample_entry->video_, buffer);

        buffer = atom_writer(NULL,
                             atom_write_list,
                             sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                             buffer);
      }
      else if(sample_entry->audio_)
      {
        atom_write_list_t atom_write_list[] = {
          { FOURCC('e', 's', 'd', 's'), sample_entry, &esds_write },
        };

        buffer = audio_sample_entry_write(sample_entry->audio_, buffer);

        buffer = atom_writer(NULL,
                             atom_write_list,
                             sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                             buffer);
      }
      write_32(sample_entry_buffer, buffer - sample_entry_buffer);
    }
  }

  return buffer;
}

static unsigned char* stts_write(void const* atom, unsigned char* buffer)
{
  stts_t const* stts = (stts_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, stts->version_);
  buffer = write_24(buffer, stts->flags_);
  buffer = write_32(buffer, stts->entries_);
  for(i = 0; i != stts->entries_; ++i)
  {
    buffer = write_32(buffer, stts->table_[i].sample_count_);
    buffer = write_32(buffer, stts->table_[i].sample_duration_);
  }

  return buffer;
}

static unsigned char* stss_write(void const* atom, unsigned char* buffer)
{
  stss_t const* stss = (stss_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, stss->version_);
  buffer = write_24(buffer, stss->flags_);
  buffer = write_32(buffer, stss->entries_);
  for(i = 0; i != stss->entries_; ++i)
  {
    buffer = write_32(buffer, stss->sample_numbers_[i]);
  }

  return buffer;
}

static unsigned char* stsc_write(void const* atom, unsigned char* buffer)
{
  stsc_t const* stsc = (stsc_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, stsc->version_);
  buffer = write_24(buffer, stsc->flags_);
  buffer = write_32(buffer, stsc->entries_);
  for(i = 0; i != stsc->entries_; ++i)
  {
    buffer = write_32(buffer, stsc->table_[i].chunk_ + 1);
    buffer = write_32(buffer, stsc->table_[i].samples_);
    buffer = write_32(buffer, stsc->table_[i].id_);
  }

  return buffer;
}

static unsigned char* stsz_write(void const* atom, unsigned char* buffer)
{
  stsz_t const* stsz = (stsz_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, stsz->version_);
  buffer = write_24(buffer, stsz->flags_);
  buffer = write_32(buffer, stsz->sample_size_);
  buffer = write_32(buffer, stsz->entries_);
  if(!stsz->sample_size_)
  {
    for(i = 0; i != stsz->entries_; ++i)
    {
      buffer = write_32(buffer, stsz->sample_sizes_[i]);
    }
  }

  return buffer;
}

static unsigned char* stco_write(void const* atom, unsigned char* buffer)
{
  stco_t const* stco = (stco_t const*)atom;
  unsigned int i;

  // newly generated stco (patched inplace)
  ((stco_t*)stco)->stco_inplace_ = buffer;

  buffer = write_8(buffer, stco->version_);
  buffer = write_24(buffer, stco->flags_);
  buffer = write_32(buffer, stco->entries_);
  for(i = 0; i != stco->entries_; ++i)
  {
    buffer = write_32(buffer, (uint32_t)(stco->chunk_offsets_[i]));
  }

  return buffer;
}

static unsigned char* ctts_write(void const* atom, unsigned char* buffer)
{
  ctts_t const* ctts = (ctts_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, ctts->version_);
  buffer = write_24(buffer, ctts->flags_);
  buffer = write_32(buffer, ctts->entries_);
  for(i = 0; i != ctts->entries_; ++i)
  {
    buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_count_));
    buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_offset_));
  }

  return buffer;
}

static unsigned char* stbl_write(void const* atom, unsigned char* buffer)
{
  stbl_t const* stbl = (stbl_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('s', 't', 's', 'd'), stbl->stsd_, &stsd_write },
    { FOURCC('s', 't', 't', 's'), stbl->stts_, &stts_write },
    { FOURCC('c', 't', 't', 's'), stbl->ctts_, &ctts_write },
    { FOURCC('s', 't', 's', 'c'), stbl->stsc_, &stsc_write },
    { FOURCC('s', 't', 's', 'z'), stbl->stsz_, &stsz_write },
    { FOURCC('s', 't', 'c', 'o'), stbl->stco_, &stco_write },
    { FOURCC('s', 't', 's', 's'), stbl->stss_, &stss_write },
  };

  buffer = atom_writer(stbl->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* minf_write(void const* atom, unsigned char* buffer)
{
  minf_t const* minf = (minf_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('v', 'm', 'h', 'd'), minf->vmhd_, &vmhd_write },
    { FOURCC('s', 'm', 'h', 'd'), minf->smhd_, &smhd_write },
    { FOURCC('d', 'i', 'n', 'f'), minf->dinf_, &dinf_write },
    { FOURCC('s', 't', 'b', 'l'), minf->stbl_, &stbl_write }
  };

  buffer = atom_writer(minf->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* mdia_write(void const* atom, unsigned char* buffer)
{
  mdia_t const* mdia = (mdia_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('m', 'd', 'h', 'd'), mdia->mdhd_, &mdhd_write },
    { FOURCC('h', 'd', 'l', 'r'), mdia->hdlr_, &hdlr_write },
    { FOURCC('m', 'i', 'n', 'f'), mdia->minf_, &minf_write }
  };

  buffer = atom_writer(mdia->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* elst_write(void const* atom, unsigned char* buffer)
{
  elst_t const* elst = (elst_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, elst->version_);
  buffer = write_24(buffer, elst->flags_);
  buffer = write_32(buffer, elst->entry_count_);
  for(i = 0; i != elst->entry_count_; ++i)
  {
    if(elst->version_ == 0)
    {
      buffer = write_32(buffer, (uint32_t)(elst->table_[i].segment_duration_));
      buffer = write_32(buffer, (uint32_t)(elst->table_[i].media_time_));
    }
    else
    {
      buffer = write_64(buffer, elst->table_[i].segment_duration_);
      buffer = write_64(buffer, elst->table_[i].media_time_);
    }
    buffer = write_16(buffer, elst->table_[i].media_rate_integer_);
    buffer = write_16(buffer, elst->table_[i].media_rate_fraction_);
  }

  return buffer;
}

static unsigned char* edts_write(void const* atom, unsigned char* buffer)
{
  edts_t const* edts = (edts_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('e', 'l', 's', 't'), edts->elst_, &elst_write }
  };

  buffer = atom_writer(edts->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* trak_write(void const* atom, unsigned char* buffer)
{
  trak_t const* trak = (trak_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('t', 'k', 'h', 'd'), trak->tkhd_, &tkhd_write },
    { FOURCC('m', 'd', 'i', 'a'), trak->mdia_, &mdia_write },
    { FOURCC('e', 'd', 't', 's'), trak->edts_, &edts_write }
  };

  buffer = atom_writer(trak->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

static unsigned char* mvhd_write(void const* atom, unsigned char* buffer)
{
  mvhd_t const* mvhd = (mvhd_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, mvhd->version_);
  buffer = write_24(buffer, mvhd->flags_);

  if(mvhd->version_ == 0)
  {
    buffer = write_32(buffer, (uint32_t)mvhd->creation_time_);
    buffer = write_32(buffer, (uint32_t)mvhd->modification_time_);
    buffer = write_32(buffer, mvhd->timescale_);
    buffer = write_32(buffer, (uint32_t)mvhd->duration_);
  }
  else
  {
    buffer = write_64(buffer, mvhd->creation_time_);
    buffer = write_64(buffer, mvhd->modification_time_);
    buffer = write_32(buffer, mvhd->timescale_);
    buffer = write_64(buffer, mvhd->duration_);
  }

  buffer = write_32(buffer, mvhd->rate_);
  buffer = write_16(buffer, mvhd->volume_);
  buffer = write_16(buffer, mvhd->reserved1_);
  buffer = write_32(buffer, mvhd->reserved2_[0]);
  buffer = write_32(buffer, mvhd->reserved2_[1]);

  for(i = 0; i != 9; ++i)
  {
    buffer = write_32(buffer, mvhd->matrix_[i]);
  }

  for(i = 0; i != 6; ++i)
  {
    buffer = write_32(buffer, mvhd->predefined_[i]);
  }

  buffer = write_32(buffer, mvhd->next_track_id_);

  return buffer;
}

static unsigned char* trex_write(void const* atom, unsigned char* buffer)
{
  trex_t const* trex = (trex_t const*)atom;

  buffer = write_8(buffer, trex->version_);
  buffer = write_24(buffer, trex->flags_);

  buffer = write_32(buffer, trex->track_id_);
  buffer = write_32(buffer, trex->default_sample_description_index_);
  buffer = write_32(buffer, trex->default_sample_duration_);
  buffer = write_32(buffer, trex->default_sample_size_);
  buffer = write_32(buffer, trex->default_sample_flags_);

  return buffer;
}

static unsigned char* mvex_write(void const* atom, unsigned char* buffer)
{
  mvex_t const* mvex = (mvex_t const*)atom;

  unsigned i;

  buffer = atom_writer(mvex->unknown_atoms_, NULL, 0, buffer);

  for(i = 0; i != mvex->tracks_; ++i)
  {
    atom_write_list_t mvex_atom_write_list[] = {
//    { FOURCC('m', 'e', 'h', 'd'), NULL, NULL },
      { FOURCC('t', 'r', 'e', 'x'), mvex->trexs_[i], &trex_write },
    };
    buffer = atom_writer(0,
                         mvex_atom_write_list,
                         sizeof(mvex_atom_write_list) / sizeof(mvex_atom_write_list[0]),
                         buffer);
  }

  return buffer;
}

extern uint32_t moov_write(moov_t* atom, unsigned char* buffer)
{
  unsigned i;

  unsigned char* atom_start = buffer;

  atom_write_list_t atom_write_list[] = {
    { FOURCC('m', 'v', 'h', 'd'), atom->mvhd_, &mvhd_write },
    { FOURCC('m', 'v', 'e', 'x'), atom->mvex_, &mvex_write }
  };

  // atom size
  buffer += 4;

  // atom type
  buffer = write_32(buffer, FOURCC('m', 'o', 'o', 'v'));

  buffer = atom_writer(atom->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  for(i = 0; i != atom->tracks_; ++i)
  {
    atom_write_list_t trak_atom_write_list[] = {
      { FOURCC('t', 'r', 'a', 'k'), atom->traks_[i], &trak_write },
    };
    buffer = atom_writer(0,
                         trak_atom_write_list,
                         sizeof(trak_atom_write_list) / sizeof(trak_atom_write_list[0]),
                         buffer);
  }

  write_32(atom_start, (uint32_t)(buffer - atom_start));

  return buffer - atom_start;
}

static unsigned char* tfra_write(void const* atom, unsigned char* buffer)
{
  tfra_t const* tfra = (tfra_t const*)atom;
  unsigned int i;
  uint32_t length_fields;

  buffer = write_8(buffer, tfra->version_);
  buffer = write_24(buffer, tfra->flags_);

  buffer = write_32(buffer, tfra->track_id_);
  length_fields = ((tfra->length_size_of_traf_num_ - 1) << 4) +
                  ((tfra->length_size_of_trun_num_ - 1) << 2) +
                  ((tfra->length_size_of_sample_num_ - 1) << 0);
  buffer = write_32(buffer, length_fields);

  buffer = write_32(buffer, tfra->number_of_entry_);
  for(i = 0; i != tfra->number_of_entry_; ++i)
  {
    tfra_table_t* table = &tfra->table_[i];
    if(tfra->version_ == 0)
    {
      buffer = write_32(buffer, (uint32_t)table->time_);
      buffer = write_32(buffer, (uint32_t)table->moof_offset_);
    }
    else
    {
      buffer = write_64(buffer, table->time_);
      buffer = write_64(buffer, table->moof_offset_);
    }

    buffer = write_n(buffer, tfra->length_size_of_traf_num_ * 8, 
                     table->traf_number_ + 1);
    buffer = write_n(buffer, tfra->length_size_of_trun_num_ * 8, 
                     table->trun_number_ + 1);
    buffer = write_n(buffer, tfra->length_size_of_sample_num_ * 8, 
                     table->sample_number_ + 1);
  }

  return buffer;
}

extern uint32_t mfra_write(mfra_t const* mfra, unsigned char* buffer)
{
  unsigned i;

  unsigned char* atom_start = buffer;
  uint32_t atom_size;

  // atom size
  buffer += 4;

  // atom type
  buffer = write_32(buffer, FOURCC('m', 'f', 'r', 'a'));

  buffer = atom_writer(mfra->unknown_atoms_, NULL, 0, buffer);

  for(i = 0; i != mfra->tracks_; ++i)
  {
    atom_write_list_t mfra_atom_write_list[] = {
      { FOURCC('t', 'f', 'r', 'a'), mfra->tfras_[i], &tfra_write },
    };
    buffer = atom_writer(0,
                         mfra_atom_write_list,
                         sizeof(mfra_atom_write_list) / sizeof(mfra_atom_write_list[0]),
                         buffer);
  }

  // write Movie Fragment Random Access Offset Box (mfro)
  {
    buffer = write_32(buffer, 16);
    buffer = write_32(buffer, FOURCC('m', 'f', 'r', 'o'));
    buffer = write_32(buffer, 0);
    buffer = write_32(buffer, (uint32_t)(buffer - atom_start + 4));
  }

  atom_size = (uint32_t)(buffer - atom_start);
  write_32(atom_start, atom_size);

  return atom_size;
}

static unsigned char* tfhd_write(void const* atom, unsigned char* buffer)
{
  struct tfhd_t const* tfhd = (struct tfhd_t const*)atom;

  buffer = write_8(buffer, tfhd->version_);
  buffer = write_24(buffer, tfhd->flags_);

  buffer = write_32(buffer, tfhd->track_id_);

  if(tfhd->flags_ & 0x000001)
  {
    buffer = write_64(buffer, tfhd->base_data_offset_);
  }
  if(tfhd->flags_ & 0x000002)
  {
    buffer = write_32(buffer, tfhd->sample_description_index_);
  }
  if(tfhd->flags_ & 0x000008)
  {
    buffer = write_32(buffer, tfhd->default_sample_duration_);
  }
  if(tfhd->flags_ & 0x000010)
  {
    buffer = write_32(buffer, tfhd->default_sample_size_);
  }
  if(tfhd->flags_ & 0x000020)
  {
    buffer = write_32(buffer, tfhd->default_sample_flags_);
  }

  return buffer;
}

static unsigned char* trun_write(void const* atom, unsigned char* buffer)
{
  // TODO: add writing of multiple truns (we can't do that here, as we need to
  // write an atom header for each trun)
  trun_t const* trun = (trun_t const*)atom;
  unsigned int i;

  buffer = write_8(buffer, trun->version_);
  buffer = write_24(buffer, trun->flags_);

  buffer = write_32(buffer, trun->sample_count_);

  // data offset
  if(trun->flags_ & 0x0001)
  {
    buffer = write_32(buffer, trun->data_offset_);
  }
  // first sample flag
  if(trun->flags_ & 0x0004)
  {
    buffer = write_32(buffer, trun->first_sample_flags_);
  }

  for(i = 0; i != trun->sample_count_; ++i)
  {
    if(trun->flags_ & 0x0100)
    {
      buffer = write_32(buffer, trun->table_[i].sample_duration_);
    }
    if(trun->flags_ & 0x0200)
    {
      buffer = write_32(buffer, trun->table_[i].sample_size_);
    }
    if(trun->flags_ & 0x0800)
    {
      buffer = write_32(buffer, trun->table_[i].sample_composition_time_offset_);
    }
  }

  return buffer;
}

static unsigned char* uuid0_write(void const* atom, unsigned char* buffer)
{
  uuid0_t const* uuid = (uuid0_t const*)atom;

  static const unsigned char uuid0[] = {
    0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
    0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
  };

  memcpy(buffer, uuid0, sizeof(uuid0));
  buffer += sizeof(uuid0);

  buffer = write_8(buffer, 0x01);
  buffer = write_24(buffer, 0x00);
  buffer = write_64(buffer, uuid->pts_);
  buffer = write_64(buffer, uuid->duration_);

  return buffer;
}

static unsigned char* uuid1_write(void const* atom, unsigned char* buffer)
{
  uuid1_t const* uuid = (uuid1_t const*)atom;

  static const unsigned char uuid1[] = {
    0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
    0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
  };
  unsigned int i;

  memcpy(buffer, uuid1, sizeof(uuid1));
  buffer += sizeof(uuid1);

  buffer = write_8(buffer, 0x01);
  buffer = write_24(buffer, 0x00);

  buffer = write_8(buffer, uuid->entries_);
  for(i = 0; i != uuid->entries_; ++i)
  {
    buffer = write_64(buffer, uuid->pts_[i]);
    buffer = write_64(buffer, uuid->duration_[i]);
  }

#if 0
  // 0x485482a4d55 = 497048.7893333 = 138:04:08.7893333
  // 0x485498ebf55 = 497051.1253333 = 138:04:11.1253333
  // 0x1647200 = 2.3360000
  // 0x1339e00 = 2.0160000

  buffer = write_64(buffer, 0x00000485482a4d55);
  buffer = write_64(buffer, 0x1647200); // next duration?

  buffer = write_64(buffer, 0x00000485498ebf55);
  buffer = write_64(buffer, 0x1339e00); // next next duration?
#endif

  return buffer;
}

static unsigned char* mfhd_write(void const* atom, unsigned char* buffer)
{
  mfhd_t const* mfhd = (mfhd_t const*)atom;

  buffer = write_8(buffer, mfhd->version_);
  buffer = write_24(buffer, mfhd->flags_);

  buffer = write_32(buffer, mfhd->sequence_number_);

  return buffer;
}

static unsigned char* traf_write(void const* atom, unsigned char* buffer)
{
  traf_t const* traf = (traf_t const*)atom;
  atom_write_list_t atom_write_list[] = {
    { FOURCC('t', 'f', 'h', 'd'), traf->tfhd_, &tfhd_write },
    { FOURCC('t', 'r', 'u', 'n'), traf->trun_, &trun_write },
#if 1 // defined(HACK_LIVE_SMOOTH_STREAMING)
    { FOURCC('u', 'u', 'i', 'd'), traf->uuid0_, &uuid0_write },
    { FOURCC('u', 'u', 'i', 'd'), traf->uuid1_, &uuid1_write }
#endif
  };

  buffer = atom_writer(traf->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  return buffer;
}

extern uint32_t moof_write(struct moof_t* atom, unsigned char* buffer)
{
  unsigned i;

  unsigned char* atom_start = buffer;

  atom_write_list_t atom_write_list[] = {
    { FOURCC('m', 'f', 'h', 'd'), atom->mfhd_, &mfhd_write },
  };

  // atom size
  buffer += 4;

  // atom type
  buffer = write_32(buffer, FOURCC('m', 'o', 'o', 'f'));

  buffer = atom_writer(atom->unknown_atoms_,
                       atom_write_list,
                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
                       buffer);

  for(i = 0; i != atom->tracks_; ++i)
  {
    atom_write_list_t traf_atom_write_list[] = {
      { FOURCC('t', 'r', 'a', 'f'), atom->trafs_[i], &traf_write },
    };
    buffer = atom_writer(0,
                         traf_atom_write_list,
                         sizeof(traf_atom_write_list) / sizeof(traf_atom_write_list[0]),
                         buffer);
  }
  write_32(atom_start, (uint32_t)(buffer - atom_start));

  return buffer - atom_start;
}

// End Of File