]> git.pld-linux.org Git - packages/lighttpd.git/blobdiff - lighttpd-mod_h264_streaming.patch
add mod_vhostdb_pgsql build
[packages/lighttpd.git] / lighttpd-mod_h264_streaming.patch
index 27e0795890c562df545d1af99ce84c31bf012f62..e9430b686ff254f61c7fa0c819e349df59fbe80e 100644 (file)
@@ -1,4 +1,3 @@
-diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compile -x 'config.*' -x configure -x missing -x mkinstalldirs -x autom4te.cache -Nur -x .svn lighttpd-1.4.18/src/Makefile.am lighttpd-mod_h264_streaming-1.4.18/src/Makefile.am
 --- lighttpd-1.4.18/src/Makefile.am    2007-09-03 01:23:53.000000000 +0300
 +++ lighttpd-mod_h264_streaming-1.4.18/src/Makefile.am 2007-10-23 23:42:37.736979478 +0300
 @@ -77,6 +77,11 @@
@@ -13,30 +12,16 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
  lib_LTLIBRARIES += mod_evasive.la
  mod_evasive_la_SOURCES = mod_evasive.c
  mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
-diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compile -x 'config.*' -x configure -x missing -x mkinstalldirs -x autom4te.cache -Nur -x .svn lighttpd-1.4.18/src/mod_h264_streaming.c lighttpd-mod_h264_streaming-1.4.18/src/mod_h264_streaming.c
---- lighttpd-1.4.18/src/mod_h264_streaming.c   1970-01-01 03:00:00.000000000 +0300
-+++ lighttpd-mod_h264_streaming-1.4.18/src/mod_h264_streaming.c        2007-10-23 23:42:37.696978564 +0300
-@@ -0,0 +1,467 @@
+--- /dev/null  2008-11-04 20:33:38.146691408 +0200
++++ lighttpd-1.4.18/src/mod_h264_streaming.c   2009-01-26 20:59:51.385271731 +0200
+@@ -0,0 +1,337 @@
 +/*******************************************************************************
 + mod_h264_streaming.c
 +
 + mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files.
 + http://h264.code-shop.com
 +
-+ Copyright (C) 2007 CodeShop B.V.
-+
-+ This program is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation, either version 3 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ Copyright (C) 2007-2009 CodeShop B.V.
 +******************************************************************************/ 
 +
 +#include <ctype.h>
@@ -57,463 +42,357 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +#include "config.h"
 +#endif
 +
++#include "moov.h"
++
 +/* plugin config for all request/connections */
 +
 +typedef struct {
-+      array *extensions;
++  array *extensions;
 +} plugin_config;
 +
 +typedef struct {
-+      PLUGIN_DATA;
++  PLUGIN_DATA;
 +
-+      buffer *query_str;
-+      array *get_params;
++  buffer *query_str;
++  array *get_params;
 +
-+      plugin_config **config_storage;
++  plugin_config **config_storage;
 +
-+      plugin_config conf;
++  plugin_config conf;
 +} plugin_data;
 +
 +/* init the plugin data */
 +INIT_FUNC(mod_h264_streaming_init) {
-+      plugin_data *p;
++  plugin_data *p;
 +
-+      p = calloc(1, sizeof(*p));
++  p = calloc(1, sizeof(*p));
 +
-+      p->query_str = buffer_init();
-+      p->get_params = array_init();
++  p->query_str = buffer_init();
++  p->get_params = array_init();
 +
-+      return p;
++  return p;
 +}
 +
 +/* detroy the plugin data */
 +FREE_FUNC(mod_h264_streaming_free) {
-+      plugin_data *p = p_d;
++  plugin_data *p = p_d;
 +
-+      UNUSED(srv);
++  UNUSED(srv);
 +
-+      if (!p) return HANDLER_GO_ON;
++  if (!p) return HANDLER_GO_ON;
 +
-+      if (p->config_storage) {
-+              size_t i;
++  if (p->config_storage) {
++    size_t i;
 +
-+              for (i = 0; i < srv->config_context->used; i++) {
-+                      plugin_config *s = p->config_storage[i];
++    for (i = 0; i < srv->config_context->used; i++) {
++      plugin_config *s = p->config_storage[i];
 +
-+                      if (!s) continue;
++      if (!s) continue;
 +
-+                      array_free(s->extensions);
++      array_free(s->extensions);
 +
-+                      free(s);
-+              }
-+              free(p->config_storage);
-+      }
++      free(s);
++    }
++    free(p->config_storage);
++  }
 +
-+      buffer_free(p->query_str);
-+      array_free(p->get_params);
++  buffer_free(p->query_str);
++  array_free(p->get_params);
 +
-+      free(p);
++  free(p);
 +
-+      return HANDLER_GO_ON;
++  return HANDLER_GO_ON;
 +}
 +
 +/* handle plugin config and check values */
 +
 +SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) {
-+      plugin_data *p = p_d;
-+      size_t i = 0;
++  plugin_data *p = p_d;
++  size_t i = 0;
 +
-+      config_values_t cv[] = {
-+              { "h264-streaming.extensions",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
-+              { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
-+      };
++  config_values_t cv[] = {
++    { "h264-streaming.extensions",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
++    { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
++  };
 +
-+      if (!p) return HANDLER_ERROR;
++  if (!p) return HANDLER_ERROR;
 +
-+      p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
++  p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
 +
-+      for (i = 0; i < srv->config_context->used; i++) {
-+              plugin_config *s;
++  for (i = 0; i < srv->config_context->used; i++) {
++    plugin_config *s;
 +
-+              s = calloc(1, sizeof(plugin_config));
-+              s->extensions     = array_init();
++    s = calloc(1, sizeof(plugin_config));
++    s->extensions     = array_init();
 +
-+              cv[0].destination = s->extensions;
++    cv[0].destination = s->extensions;
 +
-+              p->config_storage[i] = s;
++    p->config_storage[i] = s;
 +
-+              if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
-+                      return HANDLER_ERROR;
-+              }
-+      }
++    if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
++      return HANDLER_ERROR;
++    }
++  }
 +
-+      return HANDLER_GO_ON;
++  return HANDLER_GO_ON;
 +}
 +
 +#define PATCH(x) \
-+      p->conf.x = s->x;
++  p->conf.x = s->x;
 +static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
-+      size_t i, j;
-+      plugin_config *s = p->config_storage[0];
++  size_t i, j;
++  plugin_config *s = p->config_storage[0];
 +
-+      PATCH(extensions);
++  PATCH(extensions);
 +
-+      /* skip the first, the global context */
-+      for (i = 1; i < srv->config_context->used; i++) {
-+              data_config *dc = (data_config *)srv->config_context->data[i];
-+              s = p->config_storage[i];
++  /* skip the first, the global context */
++  for (i = 1; i < srv->config_context->used; i++) {
++    data_config *dc = (data_config *)srv->config_context->data[i];
++    s = p->config_storage[i];
 +
-+              /* condition didn't match */
-+              if (!config_check_cond(srv, con, dc)) continue;
++    /* condition didn't match */
++    if (!config_check_cond(srv, con, dc)) continue;
 +
-+              /* merge config */
-+              for (j = 0; j < dc->value->used; j++) {
-+                      data_unset *du = dc->value->data[j];
++    /* merge config */
++    for (j = 0; j < dc->value->used; j++) {
++      data_unset *du = dc->value->data[j];
 +
-+                      if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
-+                              PATCH(extensions);
-+                      }
-+              }
-+      }
++      if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
++        PATCH(extensions);
++      }
++    }
++  }
 +
-+      return 0;
++  return 0;
 +}
 +#undef PATCH
 +
 +static int split_get_params(array *get_params, buffer *qrystr) {
-+      size_t is_key = 1;
-+      size_t i;
-+      char *key = NULL, *val = NULL;
-+
-+      key = qrystr->ptr;
-+
-+      /* we need the \0 */
-+      for (i = 0; i < qrystr->used; i++) {
-+              switch(qrystr->ptr[i]) {
-+              case '=':
-+                      if (is_key) {
-+                              val = qrystr->ptr + i + 1;
-+
-+                              qrystr->ptr[i] = '\0';
-+
-+                              is_key = 0;
-+                      }
-+
-+                      break;
-+              case '&':
-+              case '\0': /* fin symbol */
-+                      if (!is_key) {
-+                              data_string *ds;
-+                              /* we need at least a = since the last & */
++  size_t is_key = 1;
++  size_t i;
++  char *key = NULL, *val = NULL;
 +
-+                              /* terminate the value */
-+                              qrystr->ptr[i] = '\0';
++  key = qrystr->ptr;
 +
-+                              if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
-+                                      ds = data_string_init();
-+                              }
-+                              buffer_copy_string_len(ds->key, key, strlen(key));
-+                              buffer_copy_string_len(ds->value, val, strlen(val));
++  /* we need the \0 */
++  for (i = 0; i < qrystr->used; i++) {
++    switch(qrystr->ptr[i]) {
++    case '=':
++      if (is_key) {
++        val = qrystr->ptr + i + 1;
 +
-+                              array_insert_unique(get_params, (data_unset *)ds);
-+                      }
++        qrystr->ptr[i] = '\0';
 +
-+                      key = qrystr->ptr + i + 1;
-+                      val = NULL;
-+                      is_key = 1;
-+                      break;
-+              }
-+      }
-+
-+      return 0;
-+}
-+
-+extern unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
-+                              float start_time,
-+                              unsigned int* mdat_start, unsigned int* mdat_size,
-+                              unsigned int offset);
-+
-+void write_char(unsigned char* outbuffer, int value)
-+{
-+  outbuffer[0] = (unsigned char)(value);
-+}
-+
-+void write_int32(unsigned char* outbuffer, long value)
-+{
-+  outbuffer[0] = (unsigned char)((value >> 24) & 0xff);
-+  outbuffer[1] = (unsigned char)((value >> 16) & 0xff);
-+  outbuffer[2] = (unsigned char)((value >> 8) & 0xff);
-+  outbuffer[3] = (unsigned char)((value >> 0) & 0xff);
-+}
-+
-+struct atom_t
-+{
-+  unsigned char type_[4];
-+  unsigned int size_;
-+  unsigned int start_;
-+  unsigned int end_;
-+};
-+
-+#define ATOM_PREAMBLE_SIZE 8
++        is_key = 0;
++      }
 +
-+unsigned int atom_header_size(unsigned char* atom_bytes)
-+{
-+  return (atom_bytes[0] << 24) +
-+         (atom_bytes[1] << 16) +
-+         (atom_bytes[2] << 8) +
-+         (atom_bytes[3]);
-+}
++      break;
++    case '&':
++    case '\0': /* fin symbol */
++      if (!is_key) {
++        data_string *ds;
++        /* we need at least a = since the last & */
 +
-+int atom_read_header(FILE* infile, struct atom_t* atom)
-+{
-+  unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
++        /* terminate the value */
++        qrystr->ptr[i] = '\0';
 +
-+  atom->start_ = ftell(infile);
++        if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
++          ds = data_string_init();
++        }
++        buffer_copy_string_len(ds->key, key, strlen(key));
++        buffer_copy_string_len(ds->value, val, strlen(val));
 +
-+  fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile);
-+  memcpy(&atom->type_[0], &atom_bytes[4], 4);
-+  atom->size_ = atom_header_size(atom_bytes);
-+  atom->end_ = atom->start_ + atom->size_;
++        array_insert_unique(get_params, (data_unset *)ds);
++      }
 +
-+  return 1;
-+}
++      key = qrystr->ptr + i + 1;
++      val = NULL;
++      is_key = 1;
++      break;
++    }
++  }
 +
-+void atom_write_header(unsigned char* outbuffer, struct atom_t* atom)
-+{
-+  int i;
-+  write_int32(outbuffer, atom->size_);
-+  for(i = 0; i != 4; ++i)
-+    write_char(outbuffer + 4 + i, atom->type_[i]);
++  return 0;
 +}
 +
-+int atom_is(struct atom_t const* atom, const char* type)
-+{
-+  return (atom->type_[0] == type[0] &&
-+          atom->type_[1] == type[1] &&
-+          atom->type_[2] == type[2] &&
-+          atom->type_[3] == type[3])
-+         ;
-+}
++URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
++  plugin_data *p = p_d;
++  int s_len;
++  size_t k;
 +
-+void atom_skip(FILE* infile, struct atom_t const* atom)
-+{
-+  fseek(infile, atom->end_, SEEK_SET);
-+}
++  UNUSED(srv);
 +
-+void atom_print(struct atom_t const* atom)
-+{
-+  printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
-+          atom->type_[2], atom->type_[3], atom->size_);
-+}
++  if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
 +
++  mod_h264_streaming_patch_connection(srv, con, p);
 +
-+URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
-+      plugin_data *p = p_d;
-+      int s_len;
-+      size_t k;
++  s_len = con->physical.path->used - 1;
 +
-+      UNUSED(srv);
++  for (k = 0; k < p->conf.extensions->used; k++) {
++    data_string *ds = (data_string *)p->conf.extensions->data[k];
++    int ct_len = ds->value->used - 1;
 +
-+      if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
++    if (ct_len > s_len) continue;
++    if (ds->value->used == 0) continue;
 +
-+      mod_h264_streaming_patch_connection(srv, con, p);
++    if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
++      data_string *get_param;
++      stat_cache_entry *sce = NULL;
++      double start = 0.0;
++      double end = 0.0;
++      char *err = NULL;
++      int client_is_flash = 0;
 +
-+      s_len = con->physical.path->used - 1;
++      array_reset(p->get_params);
++      buffer_copy_string_buffer(p->query_str, con->uri.query);
++      split_get_params(p->get_params, p->query_str);
 +
-+      for (k = 0; k < p->conf.extensions->used; k++) {
-+              data_string *ds = (data_string *)p->conf.extensions->data[k];
-+              int ct_len = ds->value->used - 1;
++      /* if there is a start=[0-9]+ in the header use it as start,
++       * otherwise send the full file */
++      if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start")))
++                        {
++        /* check if it is a number */
++        start = strtod(get_param->value->ptr, &err);
++        if (*err != '\0') {
++          return HANDLER_GO_ON;
++        }
++        if (start < 0) return HANDLER_GO_ON;
++                        }
++
++      /* if there is an end=[0-9]+ in the header use it as end */
++      if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end")))
++                        {
++        /* check if it is a number */
++        end = strtod(get_param->value->ptr, &err);
++        if (*err != '\0') {
++          return HANDLER_GO_ON;
++                          }
++        if (end < 0 || start >= end) return HANDLER_GO_ON;
++      }
 +
-+              if (ct_len > s_len) continue;
-+              if (ds->value->used == 0) continue;
++      if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "client")))
++      {
++        client_is_flash = starts_with(get_param->value->ptr, "FLASH");
++      }
 +
-+              if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
-+                      data_string *get_param;
-+                      stat_cache_entry *sce = NULL;
-+                      buffer *b;
-+                      double start;
-+                      char *err = NULL;
-+                      /* if there is a start=[0-9]+ in the header use it as start,
-+                       * otherwise send the full file */
++      /* get file info */
++      if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
++        return HANDLER_GO_ON;
++      }
 +
-+                      array_reset(p->get_params);
-+                      buffer_copy_string_buffer(p->query_str, con->uri.query);
-+                      split_get_params(p->get_params, p->query_str);
++      /* we are safe now, let's build a h264 header */
++      {
++        {
++          unsigned int filesize = sce->st.st_size;
 +
-+                      if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
-+                              return HANDLER_GO_ON;
-+                      }
++          void* mp4_header;
++          uint32_t mp4_header_size;
++          uint64_t mdat_offset;
++          uint64_t mdat_size;
 +
-+                      /* too short */
-+//                    if (get_param->value->used < 2) return HANDLER_GO_ON;
++          int result = mp4_split(con->physical.path->ptr, filesize, start, end,
++                                 &mp4_header, &mp4_header_size,
++                                 &mdat_offset, &mdat_size, client_is_flash);
 +
-+                      /* check if it is a number */
-+//                    start = strtol(get_param->value->ptr, &err, 10);
-+                      start = strtod(get_param->value->ptr, &err);
-+                      if (*err != '\0') {
-+                              return HANDLER_GO_ON;
-+                      }
++          if(result)
++          {
++            buffer* b = chunkqueue_get_append_buffer(con->write_queue);
++            buffer_append_memory(b, mp4_header, mp4_header_size);
++            b->used++; /* add virtual \0 */
 +
-+//                    if (start <= 0) return HANDLER_GO_ON;
-+                      if (start < 0) return HANDLER_GO_ON;
++            http_chunk_append_file(srv, con, con->physical.path,
++                                   mdat_offset, mdat_size);
++          }
 +
-+                      /* check if start is > filesize */
-+                      if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
-+                              return HANDLER_GO_ON;
-+                      }
++          if(mp4_header)
++          {
++            free(mp4_header);
++          }
 +
-+//                    if (start > sce->st.st_size) {
-+//                            return HANDLER_GO_ON;
-+//                    }
++          if(!result)
++          {
++            return HANDLER_GO_ON;
++          }
++        }
++      }
 +
-+                      /* we are safe now, let's build a h264 header */
-+                      b = chunkqueue_get_append_buffer(con->write_queue);
-+#if 0
-+                      BUFFER_COPY_STRING_CONST(b, "FLV\x1\x1\0\0\0\x9\0\0\0\x9");
-+                      http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);
-+#else
-+                      {
-+                      FILE* infile;
-+                      struct atom_t ftyp_atom;
-+                      struct atom_t moov_atom;
-+                      struct atom_t mdat_atom;
-+                      unsigned char* moov_data = 0;
-+                      unsigned char* ftyp_data = 0;
-+
-+                      infile = fopen(con->physical.path->ptr, "rb");
-+                      if(!infile) {
-+                              return HANDLER_GO_ON;
-+                      }
-+
-+                      {
-+                              unsigned int filesize = sce->st.st_size;
-+
-+                              struct atom_t leaf_atom;
-+                              while(ftell(infile) < filesize)
-+                              {
-+                                      if(!atom_read_header(infile, &leaf_atom))
-+                                              break;
-+
-+                                      atom_print(&leaf_atom);
-+
-+                                      if(atom_is(&leaf_atom, "ftyp"))
-+                                      {
-+                                              ftyp_atom = leaf_atom;
-+                                              ftyp_data = malloc(ftyp_atom.size_);
-+                                              fseek(infile, ftyp_atom.start_, SEEK_SET);
-+                                              fread(ftyp_data, ftyp_atom.size_, 1, infile);
-+                                      }
-+                                      else
-+                                      if(atom_is(&leaf_atom, "moov"))
-+                                      {
-+                                              moov_atom = leaf_atom;
-+                                              moov_data = malloc(moov_atom.size_);
-+                                              fseek(infile, moov_atom.start_, SEEK_SET);
-+                                              fread(moov_data, moov_atom.size_, 1, infile);
-+                                      }
-+                                      else
-+                                      if(atom_is(&leaf_atom, "mdat"))
-+                                      {
-+                                              mdat_atom = leaf_atom;
-+                                      }
-+                                      atom_skip(infile, &leaf_atom);
-+                              }
-+                      }
-+                      fclose(infile);
-+
-+                      if(!moov_data)
-+                              return HANDLER_GO_ON;
-+
-+                      {
-+
-+                      unsigned int mdat_start = (ftyp_data ? ftyp_atom.size_ : 0) + moov_atom.size_;
-+                      if(!moov_seek(moov_data + ATOM_PREAMBLE_SIZE,
-+                                      moov_atom.size_ - ATOM_PREAMBLE_SIZE,
-+                                      start,
-+                                      &mdat_atom.start_, &mdat_atom.size_,
-+                                      mdat_start - mdat_atom.start_))
-+                              return HANDLER_GO_ON;
-+
-+                      if(ftyp_data)
-+                      {
-+                              buffer_append_memory(b, ftyp_data, ftyp_atom.size_);
-+                              free(ftyp_data);
-+                      }
-+
-+                      buffer_append_memory(b, moov_data, moov_atom.size_);
-+                      free(moov_data);
-+
-+                      {
-+                      unsigned char mdat_bytes[ATOM_PREAMBLE_SIZE];
-+//                    mdat_atom.size_ -= bytes_to_skip;
-+                      atom_write_header(mdat_bytes, &mdat_atom);
-+                      buffer_append_memory(b, mdat_bytes, ATOM_PREAMBLE_SIZE);
-+                      b->used++; /* add virtual \0 */
-+                      }
-+
-+                      http_chunk_append_file(srv, con, con->physical.path, mdat_atom.start_ + ATOM_PREAMBLE_SIZE,
-+                                              mdat_atom.size_ - ATOM_PREAMBLE_SIZE);
-+                      }
-+                      }
-+#endif
-+                      response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4"));
++      response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4"));
++      if(!client_is_flash)
++      {
++        response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0"));
++      }
++      else
++      {
++        response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0,client=flash"));
++      }
 +
-+                      con->file_finished = 1;
++      con->file_finished = 1;
 +
-+                      return HANDLER_FINISHED;
-+              }
-+      }
++      return HANDLER_FINISHED;
++    }
++  }
 +
-+      /* not found */
-+      return HANDLER_GO_ON;
++  /* not found */
++  return HANDLER_GO_ON;
 +}
 +
 +/* this function is called at dlopen() time and inits the callbacks */
 +
 +int mod_h264_streaming_plugin_init(plugin *p) {
-+      p->version     = LIGHTTPD_VERSION_ID;
-+      p->name        = buffer_init_string("h264_streaming");
++  p->version     = LIGHTTPD_VERSION_ID;
++  p->name        = buffer_init_string("h264_streaming");
 +
-+      p->init        = mod_h264_streaming_init;
-+      p->handle_physical = mod_h264_streaming_path_handler;
-+      p->set_defaults  = mod_h264_streaming_set_defaults;
-+      p->cleanup     = mod_h264_streaming_free;
++  p->init        = mod_h264_streaming_init;
++  p->handle_physical = mod_h264_streaming_path_handler;
++  p->set_defaults  = mod_h264_streaming_set_defaults;
++  p->cleanup     = mod_h264_streaming_free;
 +
-+      p->data        = NULL;
++  p->data        = NULL;
 +
-+      return 0;
++  return 0;
 +}
 +
-diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compile -x 'config.*' -x configure -x missing -x mkinstalldirs -x autom4te.cache -Nur -x .svn lighttpd-1.4.18/src/moov.c lighttpd-mod_h264_streaming-1.4.18/src/moov.c
---- lighttpd-1.4.18/src/moov.c 1970-01-01 03:00:00.000000000 +0300
-+++ lighttpd-mod_h264_streaming-1.4.18/src/moov.c      2007-10-23 23:42:37.776980392 +0300
-@@ -0,0 +1,1188 @@
-+/******************************************************************************
-+ moov.c
+--- /dev/null  2008-11-04 20:33:38.146691408 +0200
++++ lighttpd-1.4.18/src/moov.c 2009-01-26 21:00:05.071936866 +0200
+@@ -0,0 +1,3031 @@
++/*******************************************************************************
++ moov.c (version 2)
 +
 + moov - A library for splitting Quicktime/MPEG4 files.
 + http://h264.code-shop.com
 +
-+ Copyright (C) 2007 CodeShop B.V.
++ Copyright (C) 2007-2009 CodeShop B.V.
 +
-+ This program is free software: you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation, either version 3 of the License, or
-+ (at your option) any later version.
++ Licensing
++ The H264 Streaming Module is licened under a Creative Common License. It allows
++ you to use, modify and redistribute the module, but only for *noncommercial*
++ purposes. For corporate use, please apply for a commercial license.
 +
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ GNU General Public License for more details.
++ Creative Commons License:
++ http://creativecommons.org/licenses/by-nc-sa/3.0/
 +
-+ You should have received a copy of the GNU General Public License
-+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ Commercial License:
++ http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2
 +******************************************************************************/ 
 +
-+/* 
-+  Uses code snippets from the libquicktime library:
-+   http://libquicktime.sourceforge.net
++#include "moov.h"
 +
++#ifdef _MSVC_VER
++#define _CRTDBG_MAP_ALLOC
++#include <stdlib.h>
++#include <crtdbg.h>
++#endif
++
++#ifdef UNUSED
++#elif defined(__GNUC__)
++# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
++#elif defined(__LCLINT__)
++# define UNUSED(x) /*@unused@*/ x
++#else
++# define UNUSED(x) x
++#endif
++
++/* 
 +  The QuickTime File Format PDF from Apple:
 +    http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
 +*/
@@ -522,616 +401,1830 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +#include <stdio.h>
 +#include <string.h>
 +#include <limits.h>
-+#include <inttypes.h>
++#include <stdint.h>
 +
-+static int read_char(unsigned char const* buffer)
-+{
-+  return buffer[0];
-+}
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#ifdef HAVE_STDINT_H
++# include <stdint.h>
++#endif
++#ifdef HAVE_INTTYPES_H
++# include <inttypes.h>
++#endif
++#if defined HAVE_ZLIB_H && defined HAVE_LIBZ
++// Compress the MOOV atom. Turn this off for Flash as it doesn't support it.
++// # define COMPRESS_MOOV_ATOM
++# include <zlib.h>
++#endif
++
++#ifdef WIN32
++#define ftello _ftelli64
++#define fseeko _fseeki64
++#endif
 +
-+static int read_int32(void const* buffer)
++#define MAX_TRACKS 8
++
++#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24) + \
++                           ((uint32_t)(b) << 16) + \
++                           ((uint32_t)(c) << 8) + \
++                           ((uint32_t)(d))
++
++/* Returns true when the test string is a prefix of the input */
++int starts_with(const char* input, const char* test)
 +{
-+  unsigned char* p = (unsigned char*)buffer;
-+  return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
++  while(*input && *test)
++  {
++    if(*input != *test)
++      return 0;
++    ++input;
++    ++test;
++  }
++
++  return *test == '\0';
 +}
 +
-+static void write_int32(void* outbuffer, uint32_t value)
++static unsigned int read_8(unsigned char const* buffer)
 +{
-+  unsigned char* p = (unsigned char*)outbuffer;
-+  p[0] = (unsigned char)((value >> 24) & 0xff);
-+  p[1] = (unsigned char)((value >> 16) & 0xff);
-+  p[2] = (unsigned char)((value >> 8) & 0xff);
-+  p[3] = (unsigned char)((value >> 0) & 0xff);
++  return buffer[0];
 +}
 +
-+struct atom_t
++static unsigned char* write_8(unsigned char* buffer, unsigned char v)
 +{
-+  unsigned char type_[4];
-+  unsigned int size_;
-+  unsigned char* start_;
-+  unsigned char* end_;
-+};
++  buffer[0] = v;
 +
-+#define ATOM_PREAMBLE_SIZE 8
++  return buffer + 1;
++}
 +
-+static unsigned int atom_header_size(unsigned char* atom_bytes)
++static uint16_t read_16(unsigned char const* buffer)
 +{
-+  return (atom_bytes[0] << 24) +
-+         (atom_bytes[1] << 16) +
-+         (atom_bytes[2] << 8) +
-+         (atom_bytes[3]);
++  return (buffer[0] << 8) |
++         (buffer[1] << 0);
 +}
 +
-+static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
++static unsigned char* write_16(unsigned char* buffer, unsigned int v)
 +{
-+  atom->start_ = buffer;
-+  memcpy(&atom->type_[0], &buffer[4], 4);
-+  atom->size_ = atom_header_size(buffer);
-+  atom->end_ = atom->start_ + atom->size_;
++  buffer[0] = (unsigned char)(v >> 8);
++  buffer[1] = (unsigned char)(v >> 0);
 +
-+  return buffer + ATOM_PREAMBLE_SIZE;
++  return buffer + 2;
 +}
 +
-+static unsigned char* atom_skip(struct atom_t const* atom)
++static unsigned int read_24(unsigned char const* buffer)
 +{
-+  return atom->end_;
++  return (buffer[0] << 16) |
++         (buffer[1] << 8) |
++         (buffer[2] << 0);
 +}
 +
-+static int atom_is(struct atom_t const* atom, const char* type)
++static unsigned char* write_24(unsigned char* buffer, unsigned int v)
 +{
-+  return (atom->type_[0] == type[0] &&
-+          atom->type_[1] == type[1] &&
-+          atom->type_[2] == type[2] &&
-+          atom->type_[3] == type[3])
-+         ;
++  buffer[0] = (unsigned char)(v >> 16);
++  buffer[1] = (unsigned char)(v >> 8);
++  buffer[2] = (unsigned char)(v >> 0);
++
++  return buffer + 3;
 +}
 +
-+static void atom_print(struct atom_t const* atom)
++static uint32_t read_32(unsigned char const* buffer)
 +{
-+  printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
-+          atom->type_[2], atom->type_[3], atom->size_);
++  return (buffer[0] << 24) |
++         (buffer[1] << 16) |
++         (buffer[2] << 8) |
++         (buffer[3] << 0);
 +}
 +
-+#define MAX_TRACKS 8
++static unsigned char* write_32(unsigned char* buffer, uint32_t v)
++{
++  buffer[0] = (unsigned char)(v >> 24);
++  buffer[1] = (unsigned char)(v >> 16);
++  buffer[2] = (unsigned char)(v >> 8);
++  buffer[3] = (unsigned char)(v >> 0);
++
++  return buffer + 4;
++}
 +
-+unsigned int stts_get_entries(unsigned char const* stts)
++static uint64_t read_64(unsigned char const* buffer)
 +{
-+  return read_int32(stts + 4);
++  return ((uint64_t)(read_32(buffer)) << 32) + read_32(buffer + 4);
 +}
 +
-+void stts_get_sample_count_and_duration(unsigned char const* stts,
-+  unsigned int idx, unsigned int* sample_count, unsigned int* sample_duration)
++static unsigned char* write_64(unsigned char* buffer, uint64_t v)
 +{
-+  unsigned char const* table = stts + 8 + idx * 8;
-+  *sample_count = read_int32(table);
-+  *sample_duration = read_int32(table + 4);
++  write_32(buffer + 0, (uint32_t)(v >> 32));
++  write_32(buffer + 4, (uint32_t)(v >> 0));
++
++  return buffer + 8;
 +}
 +
-+struct stts_table_t
++#define ATOM_PREAMBLE_SIZE 8
++
++struct atom_t
 +{
-+  uint32_t sample_count_;
-+  uint32_t sample_duration_;
++ uint32_t type_;
++ uint32_t short_size_;
++ uint64_t size_;
++ unsigned char* start_;
++ unsigned char* end_;
 +};
 +
-+unsigned int ctts_get_entries(unsigned char const* ctts)
++static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
 +{
-+  return read_int32(ctts + 4);
-+}
++  atom->start_ = buffer;
++  atom->short_size_ = read_32(buffer);
++  atom->type_ = read_32(buffer + 4);
 +
-+void ctts_get_sample_count_and_offset(unsigned char const* ctts,
-+  unsigned int idx, unsigned int* sample_count, unsigned int* sample_offset)
-+{
-+  unsigned char const* table = ctts + 8 + idx * 8;
-+  *sample_count = read_int32(table);
-+  *sample_offset = read_int32(table + 4);
++  if(atom->short_size_ == 1)
++    atom->size_ = read_64(buffer + 8);
++  else
++    atom->size_ = atom->short_size_;
++
++  atom->end_ = atom->start_ + atom->size_;
++
++  return buffer + ATOM_PREAMBLE_SIZE + (atom->short_size_ == 1 ? 8 : 0);
 +}
 +
-+unsigned int ctts_get_samples(unsigned char const* ctts)
++static void atom_print(struct atom_t const* atom)
 +{
-+  long samples = 0;
-+  long entries = ctts_get_entries(ctts);
-+  int i;
-+  for(i = 0; i != entries; ++i)
-+  {
-+    unsigned int sample_count;
-+    unsigned int sample_offset;
-+    ctts_get_sample_count_and_offset(ctts, i, &sample_count, &sample_offset);
-+    samples += sample_count;
-+  }
-+
-+  return samples;
++  printf("Atom(%c%c%c%c,%lld)\n",
++         atom->type_ >> 24,
++         atom->type_ >> 16,
++         atom->type_ >> 8,
++         atom->type_,
++         atom->size_);
 +}
 +
-+struct ctts_table_t
++struct unknown_atom_t
 +{
-+  uint32_t sample_count_;
-+  uint32_t sample_offset_;
++  void* atom_;
++  struct unknown_atom_t* next_;
 +};
 +
-+struct stsc_table_t
++static struct unknown_atom_t* unknown_atom_init()
 +{
-+  uint32_t chunk_;
-+  uint32_t samples_;
-+  uint32_t id_;
-+};
++  struct unknown_atom_t* atom = (struct unknown_atom_t*)malloc(sizeof(struct unknown_atom_t));
++  atom->atom_ = 0;
++  atom->next_ = 0;
++
++  return atom;
++}
 +
-+unsigned int stsc_get_entries(unsigned char const* stsc)
++static void unknown_atom_exit(struct unknown_atom_t* atom)
 +{
-+  return read_int32(stsc + 4);
++  while(atom)
++  {
++    struct unknown_atom_t* next = atom->next_;
++    free(atom->atom_);
++    free(atom);
++    atom = next;
++  }
 +}
 +
-+void stsc_get_table(unsigned char const* stsc, unsigned int i, struct stsc_table_t *stsc_table)
++static struct unknown_atom_t* unknown_atom_add_atom(struct unknown_atom_t* parent, void* atom)
 +{
-+  struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
-+  stsc_table->chunk_ = read_int32(&table[i].chunk_) - 1;
-+  stsc_table->samples_ = read_int32(&table[i].samples_);
-+  stsc_table->id_ = read_int32(&table[i].id_);
++  size_t size = read_32(atom);
++  struct unknown_atom_t* unknown = unknown_atom_init();
++  unknown->atom_ = malloc(size);
++  memcpy(unknown->atom_, atom, size);
++  unknown->next_ = parent;
++  return unknown;
 +}
 +
-+unsigned int stsc_get_chunk(unsigned char* stsc, unsigned int sample)
++struct atom_read_list_t
 +{
-+  unsigned int entries = read_int32(stsc + 4);
-+  struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
++  uint32_t type_;
++  void* parent_;
++  int (*destination_)(void* parent, void* child);
++  void* (*reader_)(void* parent, unsigned char* buffer, uint64_t size);
++};
 +
-+  if(entries == 0)
-+  {
-+    return 0;
-+  }
-+  else
-+//  if(entries == 1)
-+//  {
-+//    unsigned int table_samples = read_int32(&table[0].samples_);
-+//    unsigned int chunk = (sample + 1) / table_samples;
-+//    return chunk - 1;
-+//  }
-+//  else
-+  {
-+    unsigned int total = 0;
-+    unsigned int chunk1 = 1;
-+    unsigned int chunk1samples = 0;
-+    unsigned int chunk2entry = 0;
-+    unsigned int chunk, chunk_sample;
-+  
-+    do
-+    {
-+      unsigned int range_samples;
-+      unsigned int chunk2 = read_int32(&table[chunk2entry].chunk_);
-+      chunk = chunk2 - chunk1;
-+      range_samples = chunk * chunk1samples;
++static int atom_reader(struct atom_read_list_t* atom_read_list,
++                       unsigned int atom_read_list_size,
++                       void* parent,
++                       unsigned char* buffer, uint64_t size)
++{
++  struct atom_t leaf_atom;
++  unsigned char* buffer_start = buffer;
 +
-+      if(sample < total + range_samples)
-+        break;
++  while(buffer < buffer_start + size)
++  {
++    unsigned int i;
++    buffer = atom_read_header(buffer, &leaf_atom);
 +
-+      chunk1samples = read_int32(&table[chunk2entry].samples_);
-+      chunk1 = chunk2;
++    atom_print(&leaf_atom);
 +
-+      if(chunk2entry < entries)
++    for(i = 0; i != atom_read_list_size; ++i)
++    {
++      if(leaf_atom.type_ == atom_read_list[i].type_)
 +      {
-+        chunk2entry++;
-+        total += range_samples;
++        break;
 +      }
-+    } while(chunk2entry < entries);
++    }
 +
-+    if(chunk1samples)
++    if(i == atom_read_list_size)
 +    {
-+      unsigned int sample_in_chunk = (sample - total) % chunk1samples;
-+      if(sample_in_chunk != 0)
-+      {
-+        printf("ERROR: sample must be chunk aligned: %d\n", sample_in_chunk);
-+      }
-+      chunk = (sample - total) / chunk1samples + chunk1;
++      // add to unkown chunks
++      (*(struct unknown_atom_t**)parent) =
++        unknown_atom_add_atom(*(struct unknown_atom_t**)(parent), buffer - ATOM_PREAMBLE_SIZE);
 +    }
 +    else
-+      chunk = 1;
-+
-+    chunk_sample = total + (chunk - chunk1) * chunk1samples;
-+
-+    return chunk;
++    {
++      void* child =
++        atom_read_list[i].reader_(parent, buffer,
++          leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
++      if(!child)
++        break;
++      if(!atom_read_list[i].destination_(parent, child))
++        break;
++    }
++    buffer = leaf_atom.end_;
 +  }
-+}
 +
-+unsigned int stsc_get_samples(unsigned char* stsc)
-+{
-+  unsigned int entries = read_int32(stsc + 4);
-+  struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
-+  unsigned int samples = 0;
-+  unsigned int i;
-+  for(i = 0; i != entries; ++i)
++  if(buffer < buffer_start + size)
 +  {
-+    samples += read_int32(&table[i].samples_);
++    return 0;
 +  }
-+  return samples;
-+}
 +
-+unsigned int stco_get_entries(unsigned char const* stco)
-+{
-+  return read_int32(stco + 4);
-+}
-+
-+unsigned int stco_get_offset(unsigned char const* stco, int idx)
-+{
-+  uint32_t const* table = (uint32_t const*)(stco + 8);
-+  return read_int32(&table[idx]);
-+}
-+
-+unsigned int stsz_get_sample_size(unsigned char const* stsz)
-+{
-+  return read_int32(stsz + 4);
-+}
-+
-+unsigned int stsz_get_entries(unsigned char const* stsz)
-+{
-+  return read_int32(stsz + 8);
++  return 1;
 +}
 +
-+unsigned int stsz_get_size(unsigned char const* stsz, unsigned int idx)
++struct atom_write_list_t
 +{
-+  uint32_t const* table = (uint32_t const*)(stsz + 12);
-+  return read_int32(&table[idx]);
-+}
++  uint32_t type_;
++  void* parent_;
++  void* source_;
++  unsigned char* (*writer_)(void* parent, void* atom, unsigned char* buffer);
++};
 +
-+unsigned int stts_get_duration(unsigned char const* stts)
++static unsigned char* atom_writer_unknown(struct unknown_atom_t* atoms,
++                                          unsigned char* buffer)
 +{
-+  long duration = 0;
-+  long entries = stts_get_entries(stts);
-+  int i;
-+  for(i = 0; i != entries; ++i)
++  while(atoms)
 +  {
-+    unsigned int sample_count;
-+    unsigned int sample_duration;
-+    stts_get_sample_count_and_duration(stts, i,
-+                                       &sample_count, &sample_duration);
-+    duration += sample_duration * sample_count;
++    size_t size = read_32(atoms->atom_);
++    memcpy(buffer, atoms->atom_, size);
++    buffer += size;
++    atoms = atoms->next_;
 +  }
 +
-+  return duration;
++  return buffer;
 +}
 +
-+unsigned int stts_get_samples(unsigned char const* stts)
++static unsigned char* atom_writer(struct unknown_atom_t* unknown_atoms,
++                                  struct atom_write_list_t* atom_write_list,
++                                  unsigned int atom_write_list_size,
++                                  unsigned char* buffer)
 +{
-+  long samples = 0;
-+  long entries = stts_get_entries(stts);
-+  int i;
-+  for(i = 0; i != entries; ++i)
++  unsigned i;
++  const int write_box64 = 0;
++
++  if(unknown_atoms)
 +  {
-+    unsigned int sample_count;
-+    unsigned int sample_duration;
-+    stts_get_sample_count_and_duration(stts, i,
-+                                       &sample_count, &sample_duration);
-+    samples += sample_count;
++    buffer = atom_writer_unknown(unknown_atoms, buffer);
 +  }
 +
-+  return samples;
-+}
-+
-+unsigned int stts_get_sample(unsigned char const* stts, unsigned int time)
-+{
-+  unsigned int stts_index = 0;
-+  unsigned int stts_count;
-+
-+  unsigned int ret = 0;
-+  unsigned int time_count = 0;
-+
-+  unsigned int entries = stts_get_entries(stts);
-+  for(; stts_index != entries; ++stts_index)
++  for(i = 0; i != atom_write_list_size; ++i)
 +  {
-+    unsigned int sample_count;
-+    unsigned int sample_duration;
-+    stts_get_sample_count_and_duration(stts, stts_index,
-+                                       &sample_count, &sample_duration);
-+    if(time_count + sample_duration * sample_count >= time)
-+    {
-+      stts_count = (time - time_count) / sample_duration;
-+      time_count += stts_count * sample_duration;
-+      ret += stts_count;
-+      break;
-+    }
-+    else
++    if(atom_write_list[i].source_ != 0)
 +    {
-+      time_count += sample_duration * sample_count;
-+      ret += sample_count;
-+//      stts_index++;
-+    }
-+//    if(stts_index >= table_.size())
-+//      break;
-+  }
-+//  *time = time_count;
-+  return ret;
-+}
++      unsigned char* atom_start = buffer;
++      // atom size
++      if(write_box64)
++      {
++        write_32(buffer, 1); // box64
++      }
++      buffer += 4;
 +
-+unsigned int stts_get_time(unsigned char const* stts, unsigned int sample)
-+{
-+  unsigned int ret = 0;
-+  unsigned int stts_index = 0;
-+  unsigned int sample_count = 0;
-+  
-+  for(;;)
-+  {
-+    unsigned int table_sample_count;
-+    unsigned int table_sample_duration;
-+    stts_get_sample_count_and_duration(stts, stts_index,
-+                                       &table_sample_count, &table_sample_duration);
++      // atom type
++      buffer = write_32(buffer, atom_write_list[i].type_);
++      if(write_box64)
++      {
++        buffer += 8; // box64
++      }
 +
-+    if(sample_count + table_sample_count > sample)
-+    {
-+      unsigned int stts_count = (sample - sample_count);
-+      ret += stts_count * table_sample_duration;
-+      break;
-+    }
-+    else
-+    {
-+      sample_count += table_sample_count;
-+      ret += table_sample_count * table_sample_duration;
-+      stts_index++;
++      // atom payload
++      buffer = atom_write_list[i].writer_(atom_write_list[i].parent_,
++                                          atom_write_list[i].source_, buffer);
++
++      if(write_box64)
++        write_64(atom_start + 8, buffer - atom_start);
++      else
++        write_32(atom_start, buffer - atom_start);
 +    }
 +  }
-+  return ret;
-+}
 +
++  return buffer;
++}
++
++struct tkhd_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint64_t creation_time_;
++  uint64_t modification_time_;
++  uint32_t track_id_;
++  uint32_t reserved_;
++  uint64_t duration_;
++  uint32_t reserved2_[2];
++  uint16_t layer_;
++  uint16_t predefined_;
++  uint16_t volume_;
++  uint16_t reserved3_;
++  uint32_t matrix_[9];
++  uint32_t width_;
++  uint32_t height_;
++};
 +
-+struct stbl_t
++struct mdhd_t
 +{
-+  unsigned char* start_;
-+//stsd stsd_;               // sample description
-+  unsigned char* stts_;     // decoding time-to-sample
-+  unsigned char* stss_;     // sync sample
-+  unsigned char* stsc_;     // sample-to-chunk
-+  unsigned char* stsz_;     // sample size
-+  unsigned char* stco_;     // chunk offset
-+  unsigned char* ctts_;     // composition time-to-sample
++  unsigned int version_;
++  unsigned int flags_;
++  uint64_t creation_time_;
++  uint64_t modification_time_;
++  uint32_t timescale_;
++  uint64_t duration_;
++  unsigned int language_[3];
++  uint16_t predefined_;
 +};
 +
-+void stbl_parse(struct stbl_t* stbl, unsigned char* buffer, unsigned int size)
++struct vmhd_t
 +{
-+  struct atom_t leaf_atom;
-+  unsigned char* buffer_start = buffer;
-+  stbl->stss_ = 0;
-+  stbl->ctts_ = 0;
++  unsigned int version_;
++  unsigned int flags_;
++  uint16_t graphics_mode_;
++  uint16_t opcolor_[3];
++};
 +
-+  stbl->start_ = buffer;
-+  
-+  while(buffer < buffer_start + size)
++struct hdlr_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t predefined_;
++  uint32_t handler_type_;
++  uint32_t reserved1_;
++  uint32_t reserved2_;
++  uint32_t reserved3_;
++  char* name_;
++};
++
++struct stbl_t
++{
++  struct unknown_atom_t* unknown_atoms_;
++//struct stsd_t* stsd_;     // sample description
++  struct stts_t* stts_;     // decoding time-to-sample
++  struct stss_t* stss_;     // sync sample
++  struct stsc_t* stsc_;     // sample-to-chunk
++  struct stsz_t* stsz_;     // sample size
++  struct stco_t* stco_;     // chunk offset
++  struct ctts_t* ctts_;     // composition time-to-sample
++
++  void* stco_inplace_;      // newly generated stco (patched inplace)
++};
++
++struct stts_table_t
++{
++  uint32_t sample_count_;
++  uint32_t sample_duration_;
++};
++
++struct stts_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t entries_;
++  struct stts_table_t* table_;
++};
++
++struct stss_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t entries_;
++  uint32_t* sample_numbers_;
++};
++
++struct stsc_table_t
++{
++  uint32_t chunk_;
++  uint32_t samples_;
++  uint32_t id_;
++};
++
++struct stsc_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t entries_;
++  struct stsc_table_t* table_;
++};
++
++struct stsz_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t sample_size_;
++  uint32_t entries_;
++  uint32_t* sample_sizes_;
++};
++
++struct stco_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t entries_;
++  uint64_t* chunk_offsets_;
++};
++
++struct ctts_table_t
++{
++  uint32_t sample_count_;
++  uint32_t sample_offset_;
++};
++
++struct ctts_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint32_t entries_;
++  struct ctts_table_t* table_;
++};
++
++struct minf_t
++{
++  struct unknown_atom_t* unknown_atoms_;
++  struct vmhd_t* vmhd_;
++//  struct dinf_t* dinf_;
++  struct stbl_t* stbl_;
++};
++
++struct mdia_t
++{
++  struct unknown_atom_t* unknown_atoms_;
++  struct mdhd_t* mdhd_;
++  struct hdlr_t* hdlr_;
++  struct minf_t* minf_;
++};
++
++struct chunks_t
++{
++  unsigned int sample_;   // number of the first sample in the chunk
++  unsigned int size_;     // number of samples in the chunk
++  int id_;                // for multiple codecs mode - not used
++  uint64_t pos_;          // start byte position of chunk
++};
++
++struct samples_t
++{
++  unsigned int pts_;      // decoding/presentation time
++  unsigned int size_;     // size in bytes
++  uint64_t pos_;          // byte offset
++  unsigned int cto_;      // composition time offset
++};
++
++struct trak_t
++{
++  struct unknown_atom_t* unknown_atoms_;
++  struct tkhd_t* tkhd_;
++  struct mdia_t* mdia_;
++
++  /* temporary indices */
++  unsigned int chunks_size_;
++  struct chunks_t* chunks_;
++
++  unsigned int samples_size_;
++  struct samples_t* samples_;
++};
++
++struct mvhd_t
++{
++  unsigned int version_;
++  unsigned int flags_;
++  uint64_t creation_time_;
++  uint64_t modification_time_;
++  uint32_t timescale_;
++  uint64_t duration_;
++  uint32_t rate_;
++  uint16_t volume_;
++  uint16_t reserved1_;
++  uint32_t reserved2_[2];
++  uint32_t matrix_[9];
++  uint32_t predefined_[6];
++  uint32_t next_track_id_;
++};
++
++struct moov_t
++{
++  struct unknown_atom_t* unknown_atoms_;
++  struct mvhd_t* mvhd_;
++  unsigned int tracks_;
++  struct trak_t* traks_[MAX_TRACKS];
++};
++
++
++static struct tkhd_t* tkhd_init()
++{
++  struct tkhd_t* tkhd = (struct tkhd_t*)malloc(sizeof(struct tkhd_t));
++
++  return tkhd;
++}
++
++static void tkhd_exit(struct tkhd_t* tkhd)
++{
++  free(tkhd);
++}
++
++static void* tkhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct tkhd_t* tkhd = tkhd_init();
++
++  tkhd->version_ = read_8(buffer + 0);
++  tkhd->flags_ = read_24(buffer + 1);
++  if(tkhd->version_ == 0)
 +  {
-+    buffer = atom_read_header(buffer, &leaf_atom);
++    if(size < 92-8)
++      return 0;
 +
-+    atom_print(&leaf_atom);
++    tkhd->creation_time_ = read_32(buffer + 4);
++    tkhd->modification_time_ = read_32(buffer + 8);
++    tkhd->track_id_ = read_32(buffer + 12);
++    tkhd->reserved_ = read_32(buffer + 16);
++    tkhd->duration_ = read_32(buffer + 20);
++    buffer += 24;
++  }
++  else
++  {
++    if(size < 104-8)
++      return 0;
 +
-+    if(atom_is(&leaf_atom, "stts"))
-+    {
-+      stbl->stts_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "stss"))
-+    {
-+      stbl->stss_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "stsc"))
-+    {
-+      stbl->stsc_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "stsz"))
-+    {
-+      stbl->stsz_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "stco"))
-+    {
-+      stbl->stco_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "co64"))
++    tkhd->creation_time_ = read_64(buffer + 4);
++    tkhd->modification_time_ = read_64(buffer + 12);
++    tkhd->track_id_ = read_32(buffer + 20);
++    tkhd->reserved_ = read_32(buffer + 24);
++    tkhd->duration_ = read_64(buffer + 28);
++    buffer += 36;
++  }
++
++  tkhd->reserved2_[0] = read_32(buffer + 0);
++  tkhd->reserved2_[1] = read_32(buffer + 4);
++  tkhd->layer_ = read_16(buffer + 8);
++  tkhd->predefined_ = read_16(buffer + 10);
++  tkhd->volume_ = read_16(buffer + 12);
++  tkhd->reserved3_ = read_16(buffer + 14);
++  buffer += 16;
++
++  for(i = 0; i != 9; ++i)
++  {
++    tkhd->matrix_[i] = read_32(buffer);
++    buffer += 4;
++  }
++
++  tkhd->width_ = read_32(buffer + 0);
++  tkhd->height_ = read_32(buffer + 4);
++
++  return tkhd;
++}
++
++static unsigned char* tkhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct tkhd_t const* tkhd = 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 struct mdhd_t* mdhd_init()
++{
++  struct mdhd_t* mdhd = (struct mdhd_t*)malloc(sizeof(struct mdhd_t));
++
++  return mdhd;
++}
++
++static void mdhd_exit(struct mdhd_t* mdhd)
++{
++  free(mdhd);
++}
++
++static void* mdhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t UNUSED(size))
++{
++  uint16_t language;
++  unsigned int i;
++
++  struct mdhd_t* mdhd = mdhd_init();
++  mdhd->version_ = read_8(buffer + 0);
++  mdhd->flags_ = read_24(buffer + 1);
++  if(mdhd->version_ == 0)
++  {
++    mdhd->creation_time_ = read_32(buffer + 4);
++    mdhd->modification_time_ = read_32(buffer + 8);
++    mdhd->timescale_ = read_32(buffer + 12);
++    mdhd->duration_ = read_32(buffer + 16);
++    buffer += 20;
++  }
++  else
++  {
++    mdhd->creation_time_ = read_64(buffer + 4);
++    mdhd->modification_time_ = read_64(buffer + 12);
++    mdhd->timescale_ = read_32(buffer + 20);
++    mdhd->duration_ = read_64(buffer + 24);
++    buffer += 32;
++  }
++
++  language = read_16(buffer + 0);
++  for(i = 0; i != 3; ++i)
++  {
++    mdhd->language_[i] = ((language >> ((2 - i) * 5)) & 0x1f) + 0x60;
++  }
++
++  mdhd->predefined_ = read_16(buffer + 2);
++
++  return mdhd;
++}
++
++static unsigned char* mdhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct mdhd_t const* mdhd = 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 struct vmhd_t* vmhd_init()
++{
++  struct vmhd_t* atom = (struct vmhd_t*)malloc(sizeof(struct vmhd_t));
++
++  return atom;
++}
++
++void vmhd_exit(struct vmhd_t* atom)
++{
++  free(atom);
++}
++
++static void* vmhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct vmhd_t* atom;
++
++  if(size < 20-8)
++    return 0;
++
++  atom = vmhd_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++
++  atom->graphics_mode_ = read_16(buffer + 4);
++  buffer += 6;
++  for(i = 0; i != 3; ++i)
++  {
++    atom->opcolor_[i] = read_16(buffer);
++    buffer += 2;
++  }
++
++  return atom;
++}
++
++static unsigned char* vmhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct vmhd_t const* vmhd = 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 struct hdlr_t* hdlr_init()
++{
++  struct hdlr_t* atom = (struct hdlr_t*)malloc(sizeof(struct hdlr_t));
++  atom->name_ = 0;
++
++  return atom;
++}
++
++static void hdlr_exit(struct hdlr_t* atom)
++{
++  if(atom->name_)
++  {
++    free(atom->name_);
++  }
++  free(atom);
++}
++
++static void* hdlr_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  struct hdlr_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = hdlr_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->predefined_ = read_32(buffer + 4);
++  atom->handler_type_ = read_32(buffer + 8);
++  atom->reserved1_ = read_32(buffer + 12);
++  atom->reserved2_ = read_32(buffer + 16);
++  atom->reserved3_ = read_32(buffer + 20);
++  buffer += 24;
++  size -= 24;
++  if(size > 0)
++  {
++    size_t length = (size_t)size;
++    atom->name_ = malloc(length + 1);
++    if(atom->predefined_ == FOURCC('m', 'h', 'l', 'r'))
 +    {
-+      perror("TODO: co64");
++      length = read_8(buffer);
++      buffer += 1;
++      if(size < length)
++        length = (size_t)size;
 +    }
-+    else
-+    if(atom_is(&leaf_atom, "ctts"))
++    memcpy(atom->name_, buffer, length);
++    atom->name_[length] = '\0';
++  }
++
++  return atom;
++}
++
++static unsigned char* hdlr_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct hdlr_t* hdlr = 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'))
 +    {
-+      stbl->ctts_ = buffer;
++      buffer = write_8(buffer, strlen(hdlr->name_));
 +    }
 +
-+    buffer = atom_skip(&leaf_atom);
-+  }
++    for(p = hdlr->name_; *p; ++p)
++      buffer = write_8(buffer, *p);
++  }
++
++  return buffer;
++}
++
++static struct stts_t* stts_init()
++{
++  struct stts_t* atom = (struct stts_t*)malloc(sizeof(struct stts_t));
++  atom->table_ = 0;
++
++  return atom;
++}
++
++void stts_exit(struct stts_t* atom)
++{
++  if(atom->table_)
++  {
++    free(atom->table_);
++  }
++  free(atom);
++}
++
++static void* stts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stts_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = stts_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++
++  if(size < 8 + atom->entries_ * sizeof(struct stts_table_t))
++    return 0;
++
++  buffer += 8;
++
++  atom->table_ = (struct stts_table_t*)(malloc(atom->entries_ * sizeof(struct stts_table_t)));
++
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->table_[i].sample_count_ = read_32(buffer + 0);
++    atom->table_[i].sample_duration_ = read_32(buffer + 4);
++    buffer += 8;
++  }
++
++  return atom;
++}
++
++static unsigned char* stts_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct stts_t* stts = 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 int stts_get_sample(struct stts_t const* stts, uint64_t time)
++{
++  unsigned int stts_index = 0;
++  unsigned int stts_count;
++
++  unsigned int ret = 0;
++  uint64_t time_count = 0;
++
++  for(; stts_index != stts->entries_; ++stts_index)
++  {
++    unsigned int sample_count = stts->table_[stts_index].sample_count_;
++    unsigned int sample_duration = stts->table_[stts_index].sample_duration_;
++    if(time_count + (uint64_t)sample_duration * (uint64_t)sample_count >= time)
++    {
++      stts_count = (unsigned int)((time - time_count) / sample_duration);
++      time_count += (uint64_t)stts_count * (uint64_t)sample_duration;
++      ret += stts_count;
++      break;
++    }
++    else
++    {
++      time_count += (uint64_t)sample_duration * (uint64_t)sample_count;
++      ret += sample_count;
++    }
++  }
++  return ret;
++}
++
++static uint64_t stts_get_time(struct stts_t const* stts, unsigned int sample)
++{
++  uint64_t ret = 0;
++  unsigned int stts_index = 0;
++  unsigned int sample_count = 0;
++  
++  for(;;)
++  {
++    unsigned int table_sample_count = stts->table_[stts_index].sample_count_;
++    unsigned int table_sample_duration = stts->table_[stts_index].sample_duration_;
++    if(sample_count + table_sample_count > sample)
++    {
++      unsigned int stts_count = (sample - sample_count);
++      ret += (uint64_t)stts_count * (uint64_t)table_sample_duration;
++      break;
++    }
++    else
++    {
++      sample_count += table_sample_count;
++      ret += (uint64_t)table_sample_count * (uint64_t)table_sample_duration;
++      stts_index++;
++    }
++  }
++  return ret;
++}
++
++static uint64_t stts_get_duration(struct stts_t const* stts)
++{
++  uint64_t duration = 0;
++  unsigned int i;
++  for(i = 0; i != stts->entries_; ++i)
++  {
++    unsigned int sample_count = stts->table_[i].sample_count_;
++    unsigned int sample_duration = stts->table_[i].sample_duration_;
++    duration += (uint64_t)sample_duration * (uint64_t)sample_count;
++  }
++
++  return duration;
++}
++
++static unsigned int stts_get_samples(struct stts_t const* stts)
++{
++  unsigned int samples = 0;
++  unsigned int entries = stts->entries_;
++  unsigned int i;
++  for(i = 0; i != entries; ++i)
++  {
++    unsigned int sample_count = stts->table_[i].sample_count_;
++//  unsigned int sample_duration = stts->table_[i].sample_duration_;
++    samples += sample_count;
++  }
++
++  return samples;
++}
++
++static struct stss_t* stss_init()
++{
++  struct stss_t* atom = (struct stss_t*)malloc(sizeof(struct stss_t));
++  atom->sample_numbers_ = 0;
++
++  return atom;
++}
++
++void stss_exit(struct stss_t* atom)
++{
++  if(atom->sample_numbers_)
++  {
++    free(atom->sample_numbers_);
++  }
++  free(atom);
++}
++
++static void* stss_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stss_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = stss_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++
++  if(size < 8 + atom->entries_ * sizeof(uint32_t))
++    return 0;
++
++  buffer += 8;
++
++  atom->sample_numbers_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
++
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->sample_numbers_[i] = read_32(buffer);
++    buffer += 4;
++  }
++
++  return atom;
++}
++
++static unsigned char* stss_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct stss_t const* stss = 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 int stss_get_nearest_keyframe(struct stss_t const* stss, unsigned int sample)
++{
++  // scan the sync samples to find the key frame that precedes the sample number
++  unsigned int i;
++  unsigned int table_sample = 0;
++  for(i = 0; i != stss->entries_; ++i)
++  {
++    table_sample = stss->sample_numbers_[i];
++    if(table_sample >= sample)
++      break;
++  }
++  if(table_sample == sample)
++    return table_sample;
++  else
++    return stss->sample_numbers_[i - 1];
++}
++
++static struct stsc_t* stsc_init()
++{
++  struct stsc_t* atom = (struct stsc_t*)malloc(sizeof(struct stsc_t));
++  atom->table_ = 0;
++
++  return atom;
++}
++
++static void stsc_exit(struct stsc_t* atom)
++{
++  if(atom->table_)
++  {
++    free(atom->table_);
++  }
++  free(atom);
++}
++
++static void* stsc_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stsc_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = stsc_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++
++  if(size < 8 + atom->entries_ * sizeof(struct stsc_table_t))
++    return 0;
++
++  buffer += 8;
++
++  // reserve space for one extra entry as when splitting the video we may have to
++  // split the first entry
++  atom->table_ = (struct stsc_table_t*)(malloc((atom->entries_ + 1) * sizeof(struct stsc_table_t)));
++
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->table_[i].chunk_ = read_32(buffer + 0) - 1; // Note: we use zero based
++    atom->table_[i].samples_ = read_32(buffer + 4);
++    atom->table_[i].id_ = read_32(buffer + 8);
++    buffer += 12;
++  }
++
++  return atom;
++}
++
++static unsigned char* stsc_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct stsc_t* stsc = 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 struct stsz_t* stsz_init()
++{
++  struct stsz_t* atom = (struct stsz_t*)malloc(sizeof(struct stsz_t));
++  atom->sample_sizes_ = 0;
++
++  return atom;
++}
++
++static void stsz_exit(struct stsz_t* atom)
++{
++  if(atom->sample_sizes_)
++  {
++    free(atom->sample_sizes_);
++  }
++  free(atom);
++}
++
++static void* stsz_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stsz_t* atom;
++
++  if(size < 12)
++  {
++    printf("Error: not enough bytes for stsz atom\n");
++    return 0;
++  }
++
++  atom = stsz_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->sample_size_ = read_32(buffer + 4);
++  atom->entries_ = read_32(buffer + 8);
++  buffer += 12;
++
++  // fix for clayton.mp4, it mistakenly says there is 1 entry
++  if(atom->sample_size_ && atom->entries_)
++    atom->entries_ = 0;
++
++  if(size < 12 + atom->entries_ * sizeof(uint32_t))
++  {
++    printf("Error: stsz.entries don't match with size\n");
++    stsz_exit(atom);
++    return 0;
++  }
++
++  if(!atom->sample_size_)
++  {
++    atom->sample_sizes_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
++    for(i = 0; i != atom->entries_; ++i)
++    {
++      atom->sample_sizes_[i] = read_32(buffer);
++      buffer += 4;
++    }
++  }
++
++  return atom;
++}
++
++static unsigned char* stsz_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct stsz_t* stsz = atom;
++  unsigned int i;
++  unsigned int entries = stsz->sample_size_ ? 0 : stsz->entries_;
++
++  buffer = write_8(buffer, stsz->version_);
++  buffer = write_24(buffer, stsz->flags_);
++  buffer = write_32(buffer, stsz->sample_size_);
++  buffer = write_32(buffer, entries);
++  for(i = 0; i != entries; ++i)
++  {
++    buffer = write_32(buffer, stsz->sample_sizes_[i]);
++  }
++
++  return buffer;
++}
++
++static struct stco_t* stco_init()
++{
++  struct stco_t* atom = (struct stco_t*)malloc(sizeof(struct stco_t));
++  atom->chunk_offsets_ = 0;
++
++  return atom;
++}
++
++static void stco_exit(struct stco_t* atom)
++{
++  if(atom->chunk_offsets_)
++  {
++    free(atom->chunk_offsets_);
++  }
++  free(atom);
++}
++
++static void* stco_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stco_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = stco_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++  buffer += 8;
++
++  if(size < 8 + atom->entries_ * sizeof(uint32_t))
++    return 0;
++
++  atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->chunk_offsets_[i] = read_32(buffer);
++    buffer += 4;
++  }
++
++  return atom;
++}
++
++static void* co64_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct stco_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = stco_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++  buffer += 8;
++
++  if(size < 8 + atom->entries_ * sizeof(uint64_t))
++    return 0;
++
++  atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->chunk_offsets_[i] = read_64(buffer);
++    buffer += 8;
++  }
++
++  return atom;
++}
++
++static unsigned char* stco_write(void* parent, void* atom, unsigned char* buffer)
++{
++  struct stbl_t* stbl = parent;
++  struct stco_t* stco = atom;
++  unsigned int i;
++
++  stbl->stco_inplace_ = buffer;      // newly generated stco (patched inplace)
++
++  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 void stco_shift_offsets(struct stco_t* stco, int offset)
++{
++  unsigned int i;
++  for(i = 0; i != stco->entries_; ++i)
++    stco->chunk_offsets_[i] += offset;
++}
++
++static void stco_shift_offsets_inplace(unsigned char* stco, int offset)
++{
++  unsigned int entries = read_32(stco + 4);
++  unsigned int* table = (unsigned int*)(stco + 8);
++  unsigned int i;
++  for(i = 0; i != entries; ++i)
++    write_32((unsigned char*)&table[i], (read_32((unsigned char*)&table[i]) + offset));
++}
++
++static struct ctts_t* ctts_init()
++{
++  struct ctts_t* atom = (struct ctts_t*)malloc(sizeof(struct ctts_t));
++  atom->table_ = 0;
++
++  return atom;
++}
++
++static void ctts_exit(struct ctts_t* atom)
++{
++  if(atom->table_)
++  {
++    free(atom->table_);
++  }
++  free(atom);
++}
++
++static void* ctts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct ctts_t* atom;
++
++  if(size < 8)
++    return 0;
++
++  atom = ctts_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  atom->entries_ = read_32(buffer + 4);
++
++  if(size < 8 + atom->entries_ * sizeof(struct ctts_table_t))
++    return 0;
++
++  buffer += 8;
++
++  atom->table_ = (struct ctts_table_t*)(malloc(atom->entries_ * sizeof(struct ctts_table_t)));
++
++  for(i = 0; i != atom->entries_; ++i)
++  {
++    atom->table_[i].sample_count_ = read_32(buffer + 0);
++    atom->table_[i].sample_offset_ = read_32(buffer + 4);
++    buffer += 8;
++  }
++
++  return atom;
++}
++
++static unsigned char* ctts_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct ctts_t const* ctts = 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 int ctts_get_samples(struct ctts_t const* ctts)
++{
++  unsigned int samples = 0;
++  unsigned int entries = ctts->entries_;
++  unsigned int i;
++  for(i = 0; i != entries; ++i)
++  {
++    unsigned int sample_count = ctts->table_[i].sample_count_;
++//  unsigned int sample_offset = ctts->table_[i].sample_offset_;
++    samples += sample_count;
++  }
++
++  return samples;
++}
++
++static struct stbl_t* stbl_init()
++{
++  struct stbl_t* atom = (struct stbl_t*)malloc(sizeof(struct stbl_t));
++  atom->unknown_atoms_ = 0;
++  atom->stts_ = 0;
++  atom->stss_ = 0;
++  atom->stsc_ = 0;
++  atom->stsz_ = 0;
++  atom->stco_ = 0;
++  atom->ctts_ = 0;
++
++  return atom;
++}
++
++static void stbl_exit(struct stbl_t* atom)
++{
++  if(atom->unknown_atoms_)
++  {
++    unknown_atom_exit(atom->unknown_atoms_);
++  }
++  if(atom->stts_)
++  {
++    stts_exit(atom->stts_);
++  }
++  if(atom->stss_)
++  {
++    stss_exit(atom->stss_);
++  }
++  if(atom->stsc_)
++  {
++    stsc_exit(atom->stsc_);
++  }
++  if(atom->stsz_)
++  {
++    stsz_exit(atom->stsz_);
++  }
++  if(atom->stco_)
++  {
++    stco_exit(atom->stco_);
++  }
++  if(atom->ctts_)
++  {
++    ctts_exit(atom->ctts_);
++  }
++
++  free(atom);
++}
++
++static int stbl_add_stts(void* parent, void* child)
++{
++  struct stbl_t* stbl = parent;
++  stbl->stts_ = child;
++
++  return 1;
++}
++
++static int stbl_add_stss(void* parent, void* child)
++{
++  struct stbl_t* stbl = parent;
++  stbl->stss_ = child;
++
++  return 1;
++}
++
++static int stbl_add_stsc(void* parent, void* child)
++{
++  struct stbl_t* stbl = parent;
++  stbl->stsc_ = child;
++
++  return 1;
++}
++
++static int stbl_add_stsz(void* parent, void* child)
++{
++  struct stbl_t* stbl = parent;
++  stbl->stsz_ = child;
++
++  return 1;
++}
++
++static int stbl_add_stco(void* parent, void* child)
++{
++  struct stbl_t* stbl = parent;
++  stbl->stco_ = child;
++
++  return 1;
 +}
 +
-+struct minf_t
++static int stbl_add_ctts(void* parent, void* child)
 +{
-+  unsigned char* start_;
-+  struct stbl_t stbl_;
-+};
++  struct stbl_t* stbl = parent;
++  stbl->ctts_ = child;
 +
-+void minf_parse(struct minf_t* minf, unsigned char* buffer, unsigned int size)
++  return 1;
++}
++
++static void* stbl_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
 +{
-+  struct atom_t leaf_atom;
-+  unsigned char* buffer_start = buffer;
++  struct stbl_t* atom = stbl_init();
 +
-+  minf->start_ = buffer;
-+  
-+  while(buffer < buffer_start + size)
++  struct atom_read_list_t atom_read_list[] = {
++    { FOURCC('s', 't', 't', 's'), atom, &stbl_add_stts, &stts_read },
++    { FOURCC('s', 't', 's', 's'), atom, &stbl_add_stss, &stss_read },
++    { FOURCC('s', 't', 's', 'c'), atom, &stbl_add_stsc, &stsc_read },
++    { FOURCC('s', 't', 's', 'z'), atom, &stbl_add_stsz, &stsz_read },
++    { FOURCC('s', 't', 'c', 'o'), atom, &stbl_add_stco, &stco_read },
++    { FOURCC('c', 'o', '6', '4'), atom, &stbl_add_stco, &co64_read },
++    { FOURCC('c', 't', 't', 's'), atom, &stbl_add_ctts, &ctts_read },
++  };
++
++  int result = atom_reader(atom_read_list,
++                  sizeof(atom_read_list) / sizeof(atom_read_list[0]),
++                  atom,
++                  buffer, size);
++
++  // check for mandatory atoms
++  if(!atom->stts_)
 +  {
-+    buffer = atom_read_header(buffer, &leaf_atom);
++    printf("stbl: missing stts\n");
++    result = 0;
++  }
 +
-+    atom_print(&leaf_atom);
++  if(!atom->stsc_)
++  {
++    printf("stbl: missing stsc\n");
++    result = 0;
++  }
 +
-+    if(atom_is(&leaf_atom, "stbl"))
-+    {
-+      stbl_parse(&minf->stbl_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
-+    }
++  if(!atom->stsz_)
++  {
++    printf("stbl: missing stsz\n");
++    result = 0;
++  }
++
++  if(!atom->stco_)
++  {
++    printf("stbl: missing stco\n");
++    result = 0;
++  }
 +
-+    buffer = atom_skip(&leaf_atom);
++  if(!result)
++  {
++    stbl_exit(atom);
++    return 0;
 +  }
++
++  return atom;
 +}
 +
-+struct mdia_t
++static unsigned char* stbl_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
 +{
-+  unsigned char* start_;
-+  unsigned char* mdhd_;
-+  struct minf_t minf_;
-+//  hdlr hdlr_;
-+};
++  struct stbl_t* stbl = atom;
++  struct atom_write_list_t atom_write_list[] = {
++    { FOURCC('s', 't', 't', 's'), stbl, stbl->stts_, &stts_write },
++    { FOURCC('s', 't', 's', 's'), stbl, stbl->stss_, &stss_write },
++    { FOURCC('s', 't', 's', 'c'), stbl, stbl->stsc_, &stsc_write },
++    { FOURCC('s', 't', 's', 'z'), stbl, stbl->stsz_, &stsz_write },
++    { FOURCC('s', 't', 'c', 'o'), stbl, stbl->stco_, &stco_write },
++    { FOURCC('c', 't', 't', 's'), stbl, stbl->ctts_, &ctts_write },
++  };
++
++  buffer = atom_writer(stbl->unknown_atoms_,
++                       atom_write_list,
++                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
++                       buffer);
++
++  return buffer;
++}
 +
-+void mdia_parse(struct mdia_t* mdia, unsigned char* buffer, unsigned int size)
++static unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample)
 +{
-+  struct atom_t leaf_atom;
-+  unsigned char* buffer_start = buffer;
++  // If the sync atom is not present, all samples are implicit sync samples.
++  if(!stbl->stss_)
++    return sample;
 +
-+  mdia->start_ = buffer;
-+  
-+  while(buffer < buffer_start + size)
-+  {
-+    buffer = atom_read_header(buffer, &leaf_atom);
++  return stss_get_nearest_keyframe(stbl->stss_, sample);
++}
 +
-+    atom_print(&leaf_atom);
++static struct minf_t* minf_init()
++{
++  struct minf_t* atom = (struct minf_t*)malloc(sizeof(struct minf_t));
++  atom->unknown_atoms_ = 0;
++  atom->vmhd_ = 0;
++  atom->stbl_ = 0;
 +
-+    if(atom_is(&leaf_atom, "mdhd"))
-+    {
-+      mdia->mdhd_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "minf"))
-+    {
-+      minf_parse(&mdia->minf_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
-+    }
++  return atom;
++}
 +
-+    buffer = atom_skip(&leaf_atom);
++static void minf_exit(struct minf_t* atom)
++{
++  if(atom->unknown_atoms_)
++  {
++    unknown_atom_exit(atom->unknown_atoms_);
++  }
++  if(atom->vmhd_)
++  {
++    vmhd_exit(atom->vmhd_);
 +  }
++  if(atom->stbl_)
++  {
++    stbl_exit(atom->stbl_);
++  }
++  free(atom);
 +}
 +
-+struct chunks_t
++static int minf_add_vmhd(void* parent, void* child)
 +{
-+  unsigned int sample_;   // number of the first sample in the chunk
-+  unsigned int size_;     // number of samples in the chunk
-+  int id_;                // for multiple codecs mode - not used
-+  unsigned int pos_;      // start byte position of chunk
-+};
++  struct minf_t* minf = parent;
++  minf->vmhd_ = child;
 +
-+struct samples_t
++  return 1;
++}
++
++static int minf_add_stbl(void* parent, void* child)
 +{
-+  unsigned int pts_;      // decoding/presentation time
-+  unsigned int size_;     // size in bytes
-+  unsigned int pos_;      // byte offset
-+  unsigned int cto_;      // composition time offset
-+};
++  struct minf_t* minf = parent;
++  minf->stbl_ = child;
 +
-+struct trak_t
++  return 1;
++}
++
++static void* minf_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
 +{
-+  unsigned char* start_;
-+  unsigned char* tkhd_;
-+  struct mdia_t mdia_;
++  struct minf_t* atom = minf_init();
 +
-+  /* temporary indices */
-+  unsigned int chunks_size_;
-+  struct chunks_t* chunks_;
++  struct atom_read_list_t atom_read_list[] = {
++    { FOURCC('v', 'm', 'h', 'd'), atom, &minf_add_vmhd, &vmhd_read },
++    { FOURCC('s', 't', 'b', 'l'), atom, &minf_add_stbl, &stbl_read }
++  };
 +
-+  unsigned int samples_size_;
-+  struct samples_t* samples_;
-+};
++  int result = atom_reader(atom_read_list,
++                  sizeof(atom_read_list) / sizeof(atom_read_list[0]),
++                  atom,
++                  buffer, size);
 +
-+void trak_init(struct trak_t* trak)
-+{
-+  trak->chunks_ = 0;
-+  trak->samples_ = 0;
++  // check for mandatory atoms
++  if(!atom->stbl_)
++  {
++    printf("minf: missing stbl\n");
++    result = 0;
++  }
++
++  if(!result)
++  {
++    minf_exit(atom);
++    return 0;
++  }
++
++  return atom;
 +}
 +
-+void trak_exit(struct trak_t* trak)
++static unsigned char* minf_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
 +{
-+  if(trak->chunks_)
-+      free(trak->chunks_);
-+  if(trak->samples_)
-+    free(trak->samples_);
++  struct minf_t const* minf = atom;
++  struct atom_write_list_t atom_write_list[] = {
++    { FOURCC('v', 'm', 'h', 'd'), atom, minf->vmhd_, &vmhd_write },
++    { FOURCC('s', 't', 'b', 'l'), atom, 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;
 +}
 +
-+void trak_parse(struct trak_t* trak, unsigned char* buffer, unsigned int size)
++static struct mdia_t* mdia_init()
 +{
-+  struct atom_t leaf_atom;
-+  unsigned char* buffer_start = buffer;
++  struct mdia_t* atom = (struct mdia_t*)malloc(sizeof(struct mdia_t));
++  atom->unknown_atoms_ = 0;
++  atom->mdhd_ = 0;
++  atom->hdlr_ = 0;
++  atom->minf_ = 0;
 +
-+  trak->start_ = buffer;
-+  
-+  while(buffer < buffer_start + size)
-+  {
-+    buffer = atom_read_header(buffer, &leaf_atom);
++  return atom;
++}
 +
-+    atom_print(&leaf_atom);
++static void mdia_exit(struct mdia_t* atom)
++{
++  if(atom->unknown_atoms_)
++  {
++    unknown_atom_exit(atom->unknown_atoms_);
++  }
++  if(atom->mdhd_)
++  {
++    mdhd_exit(atom->mdhd_);
++  }
++  if(atom->hdlr_)
++  {
++    hdlr_exit(atom->hdlr_);
++  }
++  if(atom->minf_)
++  {
++    minf_exit(atom->minf_);
++  }
++  free(atom);
++}
 +
-+    if(atom_is(&leaf_atom, "tkhd"))
-+    {
-+      trak->tkhd_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "mdia"))
-+    {
-+      mdia_parse(&trak->mdia_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
-+    }
++static int mdia_add_mdhd(void* parent, void* child)
++{
++  struct mdia_t* mdia = parent;
++  mdia->mdhd_ = child;
 +
-+    buffer = atom_skip(&leaf_atom);
-+  }
++  return 1;
 +}
 +
-+struct moov_t
++static int mdia_add_hdlr(void* parent, void* child)
 +{
-+  unsigned char* start_;
-+  unsigned int tracks_;
-+  unsigned char* mvhd_;
-+  struct trak_t traks_[MAX_TRACKS];
-+};
++  struct mdia_t* mdia = parent;
++  mdia->hdlr_ = child;
++
++  return 1;
++}
 +
-+void moov_init(struct moov_t* moov)
++static int mdia_add_minf(void* parent, void* child)
 +{
-+  moov->tracks_ = 0;
++  struct mdia_t* mdia = parent;
++  mdia->minf_ = child;
++
++  return 1;
 +}
 +
-+void moov_exit(struct moov_t* moov)
++static void* mdia_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
 +{
-+  unsigned int i;
-+  for(i = 0; i != moov->tracks_; ++i)
++  struct mdia_t* atom = mdia_init();
++
++  struct atom_read_list_t atom_read_list[] = {
++    { FOURCC('m', 'd', 'h', 'd'), atom, &mdia_add_mdhd, &mdhd_read },
++    { FOURCC('h', 'd', 'l', 'r'), atom, &mdia_add_hdlr, &hdlr_read },
++    { FOURCC('m', 'i', 'n', 'f'), atom, &mdia_add_minf, &minf_read }
++  };
++
++  int result = atom_reader(atom_read_list,
++                  sizeof(atom_read_list) / sizeof(atom_read_list[0]),
++                  atom,
++                  buffer, size);
++
++  // check for mandatory atoms
++  if(!atom->mdhd_)
++  {
++    printf("mdia: missing mdhd\n");
++    result = 0;
++  }
++
++  if(!atom->hdlr_)
++  {
++    printf("mdia: missing hdlr\n");
++    result = 0;
++  }
++
++  if(!atom->minf_)
++  {
++    printf("mdia: missing minf\n");
++    result = 0;
++  }
++
++  if(!result)
 +  {
-+    trak_exit(&moov->traks_[i]);
++    mdia_exit(atom);
++    return 0;
 +  }
++
++  return atom;
++}
++
++static unsigned char* mdia_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct mdia_t const* mdia = atom;
++  struct atom_write_list_t atom_write_list[] = {
++    { FOURCC('m', 'd', 'h', 'd'), atom, mdia->mdhd_, &mdhd_write },
++    { FOURCC('h', 'd', 'l', 'r'), atom, mdia->hdlr_, &hdlr_write },
++    { FOURCC('m', 'i', 'n', 'f'), atom, 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;
 +}
 +
 +void trak_build_index(struct trak_t* trak)
 +{
-+  void const* stco = trak->mdia_.minf_.stbl_.stco_;
++  struct stco_t const* stco = trak->mdia_->minf_->stbl_->stco_;
 +
-+  trak->chunks_size_ = stco_get_entries(stco);
++  trak->chunks_size_ = stco->entries_;
 +  trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t));
 +
 +  {
 +    unsigned int i;
 +    for(i = 0; i != trak->chunks_size_; ++i)
 +    {
-+      trak->chunks_[i].pos_ = stco_get_offset(stco, i);
++      trak->chunks_[i].pos_ = stco->chunk_offsets_[i];
 +    }
 +  }
 +
 +  // process chunkmap:
 +  {
-+    void const* stsc = trak->mdia_.minf_.stbl_.stsc_;
++    struct stsc_t const* stsc = trak->mdia_->minf_->stbl_->stsc_;
 +    unsigned int last = trak->chunks_size_;
-+    unsigned int i = stsc_get_entries(stsc);
++    unsigned int i = stsc->entries_;
 +    while(i > 0)
 +    {
-+      struct stsc_table_t stsc_table;
 +      unsigned int j;
 +
 +      --i;
 +
-+      stsc_get_table(stsc, i, &stsc_table);
-+      for(j = stsc_table.chunk_; j < last; j++)
++      for(j = stsc->table_[i].chunk_; j < last; j++)
 +      {
-+        trak->chunks_[j].id_ = stsc_table.id_;
-+        trak->chunks_[j].size_ = stsc_table.samples_;
++        trak->chunks_[j].id_ = stsc->table_[i].id_;
++        trak->chunks_[j].size_ = stsc->table_[i].samples_;
 +      }
-+      last = stsc_table.chunk_;
++      last = stsc->table_[i].chunk_;
 +    }
 +  }
 +
 +  // calc pts of chunks:
 +  {
-+    void const* stsz = trak->mdia_.minf_.stbl_.stsz_;
-+    unsigned int sample_size = stsz_get_sample_size(stsz);
++    struct stsz_t const* stsz = trak->mdia_->minf_->stbl_->stsz_;
++    unsigned int sample_size = stsz->sample_size_;
 +    unsigned int s = 0;
 +    {
 +      unsigned int j;
@@ -1144,7 +2237,7 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +
 +    if(sample_size == 0)
 +    {
-+      trak->samples_size_ = stsz_get_entries(stsz);
++      trak->samples_size_ = stsz->entries_;
 +    }
 +    else
 +    {
@@ -1157,7 +2250,7 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +    {
 +      unsigned int i;
 +      for(i = 0; i != trak->samples_size_ ; ++i)
-+        trak->samples_[i].size_ = stsz_get_size(stsz, i);
++        trak->samples_[i].size_ = stsz->sample_sizes_[i];
 +    }
 +    else
 +    {
@@ -1178,18 +2271,16 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +
 +  // calc pts:
 +  {
-+    void const* stts = trak->mdia_.minf_.stbl_.stts_;
++    struct stts_t const* stts = trak->mdia_->minf_->stbl_->stts_;
 +    unsigned int s = 0;
-+    unsigned int entries = stts_get_entries(stts);
++    unsigned int pts = 0;
++    unsigned int entries = stts->entries_;
 +    unsigned int j;
 +    for(j = 0; j < entries; j++)
 +    {
 +      unsigned int i;
-+      unsigned int pts = 0;
-+      unsigned int sample_count;
-+      unsigned int sample_duration;
-+      stts_get_sample_count_and_duration(stts, j,
-+                                         &sample_count, &sample_duration);
++      unsigned int sample_count = stts->table_[j].sample_count_;
++      unsigned int sample_duration = stts->table_[j].sample_duration_;
 +      for(i = 0; i < sample_count; i++)
 +      {
 +        trak->samples_[s].pts_ = pts;
@@ -1201,18 +2292,17 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +
 +  // calc composition times:
 +  {
-+    void const* ctts = trak->mdia_.minf_.stbl_.ctts_;
++    struct ctts_t const* ctts = trak->mdia_->minf_->stbl_->ctts_;
 +    if(ctts)
 +    {
 +      unsigned int s = 0;
-+      unsigned int entries = ctts_get_entries(ctts);
++      unsigned int entries = ctts->entries_;
 +      unsigned int j;
-+      for(j = 0; j < entries; j++)
++      for(j = 0; j != entries; j++)
 +      {
 +        unsigned int i;
-+        unsigned int sample_count;
-+        unsigned int sample_offset;
-+        ctts_get_sample_count_and_offset(ctts, j, &sample_count, &sample_offset);
++        unsigned int sample_count = ctts->table_[j].sample_count_;
++        unsigned int sample_offset = ctts->table_[j].sample_offset_;
 +        for(i = 0; i < sample_count; i++)
 +        {
 +          trak->samples_[s].cto_ = sample_offset;
@@ -1226,11 +2316,11 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +  {
 +    unsigned int s = 0;
 +    unsigned int j;
-+    for(j = 0; j < trak->chunks_size_; j++)
++    for(j = 0; j != trak->chunks_size_; j++)
 +    {
-+      unsigned int pos = trak->chunks_[j].pos_;
++      uint64_t pos = trak->chunks_[j].pos_;
 +      unsigned int i;
-+      for(i = 0; i < trak->chunks_[j].size_; i++)
++      for(i = 0; i != trak->chunks_[j].size_; i++)
 +      {
 +        trak->samples_[s].pos_ = pos;
 +        pos += trak->samples_[s].size_;
@@ -1240,16 +2330,17 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +  }
 +}
 +
-+void trak_write_index(struct trak_t* trak, unsigned int start, unsigned int end)
++void trak_update_index(struct trak_t* trak, unsigned int start, unsigned int end)
 +{
 +  // write samples [start,end>
 +
 +  // stts = [entries * [sample_count, sample_duration]
 +  {
-+    unsigned char* stts = trak->mdia_.minf_.stbl_.stts_;
++    struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_;
++
 +    unsigned int entries = 0;
-+    struct stts_table_t* table = (struct stts_table_t*)(stts + 8);
 +    unsigned int s;
++
 +    for(s = start; s != end; ++s)
 +    {
 +      unsigned int sample_count = 1;
@@ -1262,12 +2353,12 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +        ++sample_count;
 +        ++s;
 +      }
-+      // write entry
-+      write_int32(&table[entries].sample_count_, sample_count);
-+      write_int32(&table[entries].sample_duration_, sample_duration);
++      stts->table_[entries].sample_count_ = sample_count;
++      stts->table_[entries].sample_duration_ = sample_duration;
 +      ++entries;
 +    }
-+    write_int32(stts + 4, entries);
++    stts->entries_ = entries;
++
 +    if(stts_get_samples(stts) != end - start)
 +    {
 +      printf("ERROR: stts_get_samples=%d, should be %d\n",
@@ -1277,12 +2368,12 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +
 +  // ctts = [entries * [sample_count, sample_offset]
 +  {
-+    unsigned char* ctts = trak->mdia_.minf_.stbl_.ctts_;
++    struct ctts_t* ctts = trak->mdia_->minf_->stbl_->ctts_;
 +    if(ctts)
 +    {
 +      unsigned int entries = 0;
-+      struct ctts_table_t* table = (struct ctts_table_t*)(ctts + 8);
 +      unsigned int s;
++
 +      for(s = start; s != end; ++s)
 +      {
 +        unsigned int sample_count = 1;
@@ -1295,11 +2386,11 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +          ++s;
 +        }
 +        // write entry
-+        write_int32(&table[entries].sample_count_, sample_count);
-+        write_int32(&table[entries].sample_offset_, sample_offset);
++        ctts->table_[entries].sample_count_ = sample_count;
++        ctts->table_[entries].sample_offset_ = sample_offset;
 +        ++entries;
 +      }
-+      write_int32(ctts + 4, entries);
++      ctts->entries_ = entries;
 +      if(ctts_get_samples(ctts) != end - start)
 +      {
 +        printf("ERROR: ctts_get_samples=%d, should be %d\n",
@@ -1310,9 +2401,9 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +
 +  // process chunkmap:
 +  {
-+    unsigned char* stsc = trak->mdia_.minf_.stbl_.stsc_;
-+    struct stsc_table_t* stsc_table = (struct stsc_table_t*)(stsc + 8);
++    struct stsc_t* stsc = trak->mdia_->minf_->stbl_->stsc_;
 +    unsigned int i;
++
 +    for(i = 0; i != trak->chunks_size_; ++i)
 +    {
 +      if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start)
@@ -1322,357 +2413,1028 @@ diff -ur -x '*.m4' -x ltmain.sh -x install-sh -x depcomp -x Makefile.in -x compi
 +    {
 +      unsigned int stsc_entries = 0;
 +      unsigned int chunk_start = i;
-+      unsigned int samples =
-+        trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start;
-+      unsigned int id = trak->chunks_[i].id_;
-+
-+      // write entry [chunk,samples,id]
-+      write_int32(&stsc_table[stsc_entries].chunk_, 1);
-+      write_int32(&stsc_table[stsc_entries].samples_, samples);
-+      write_int32(&stsc_table[stsc_entries].id_, id);
-+      ++stsc_entries;
-+      if(i != trak->chunks_size_)
++      unsigned int chunk_end;
++      // problem.mp4: reported by Jin-seok Lee. Second track contains no samples
++      if(trak->chunks_size_ != 0)
 +      {
-+        for(i += 1; i != trak->chunks_size_; ++i)
++        unsigned int samples =
++          trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start;
++        unsigned int id = trak->chunks_[i].id_;
++
++        // write entry [chunk,samples,id]
++        stsc->table_[stsc_entries].chunk_ = 0;
++        stsc->table_[stsc_entries].samples_ = samples;
++        stsc->table_[stsc_entries].id_ = id;
++        ++stsc_entries;
++
++        if(i != trak->chunks_size_)
 +        {
-+          if(trak->chunks_[i].size_ != samples)
++          for(i += 1; i != trak->chunks_size_; ++i)
 +          {
-+            samples = trak->chunks_[i].size_;
-+            id = trak->chunks_[i].id_;
-+            write_int32(&stsc_table[stsc_entries].chunk_, i - chunk_start + 1);
-+            write_int32(&stsc_table[stsc_entries].samples_, samples);
-+            write_int32(&stsc_table[stsc_entries].id_, id);
-+            ++stsc_entries;
++            if(trak->chunks_[i].sample_ >= end)
++              break;
++
++            if(trak->chunks_[i].size_ != samples)
++            {
++              samples = trak->chunks_[i].size_;
++              id = trak->chunks_[i].id_;
++
++              stsc->table_[stsc_entries].chunk_ = i - chunk_start;
++              stsc->table_[stsc_entries].samples_ = samples;
++              stsc->table_[stsc_entries].id_ = id;
++              ++stsc_entries;
++            }
 +          }
 +        }
 +      }
-+      write_int32(stsc + 4, stsc_entries);
++      chunk_end = i;
++      stsc->entries_ = stsc_entries;
++
 +      {
-+        unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
-+//        stco_erase(stco, chunk_start);
-+        unsigned int entries = read_int32(stco + 4);
-+        uint32_t* stco_table = (uint32_t*)(stco + 8);
-+        memmove(stco_table, &stco_table[chunk_start],
-+                (entries - chunk_start) * sizeof(uint32_t));
-+        write_int32(stco + 4, entries - chunk_start);
++        struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_;
++        unsigned int entries = 0;
++        for(i = chunk_start; i != chunk_end; ++i)
++        {
++          stco->chunk_offsets_[entries] = stco->chunk_offsets_[i];
++          ++entries;
++        }
++        stco->entries_ = entries;
 +
 +        // patch first chunk with correct sample offset
-+//        uint32_t* stco_table = (uint32_t*)(stco + 8);
-+        write_int32(stco_table, trak->samples_[start].pos_);
++        stco->chunk_offsets_[0] = (uint32_t)trak->samples_[start].pos_;
 +      }
 +    }
 +  }
 +
 +  // process sync samples:
-+  if(trak->mdia_.minf_.stbl_.stss_)
++  if(trak->mdia_->minf_->stbl_->stss_)
 +  {
-+    unsigned char* stss = trak->mdia_.minf_.stbl_.stss_;
-+    unsigned int entries = read_int32(stss + 4);
-+    uint32_t* table = (uint32_t*)(stss + 8);
++    struct stss_t* stss = trak->mdia_->minf_->stbl_->stss_;
++    unsigned int entries = 0;
 +    unsigned int stss_start;
 +    unsigned int i;
-+    for(i = 0; i != entries; ++i)
++
++    for(i = 0; i != stss->entries_; ++i)
 +    {
-+      if(read_int32(&table[i]) >= start + 1)
++      if(stss->sample_numbers_[i] >= start + 1)
 +        break;
 +    }
 +    stss_start = i;
-+    for(; i != entries; ++i)
++    for(; i != stss->entries_; ++i)
 +    {
-+      unsigned int sync_sample = read_int32(&table[i]);
++      unsigned int sync_sample = stss->sample_numbers_[i];
 +      if(sync_sample >= end + 1)
 +        break;
-+      write_int32(&table[i - stss_start], sync_sample - start);
++      stss->sample_numbers_[entries] = sync_sample - start;
++      ++entries;
++    }
++    stss->entries_ = entries;
++  }
++
++  // process sample sizes
++  {
++    struct stsz_t* stsz = trak->mdia_->minf_->stbl_->stsz_;
++
++    if(stsz->sample_size_ == 0)
++    {
++      unsigned int entries = 0;
++      unsigned int i;
++      for(i = start; i != end; ++i)
++      {
++        stsz->sample_sizes_[entries] = stsz->sample_sizes_[i];
++        ++entries;
++      }
++      stsz->entries_ = entries;
++    }
++  }
++}
++
++static struct trak_t* trak_init()
++{
++  struct trak_t* trak = (struct trak_t*)malloc(sizeof(struct trak_t));
++  trak->unknown_atoms_ = 0;
++  trak->tkhd_ = 0;
++  trak->mdia_ = 0;
++  trak->chunks_size_ = 0;
++  trak->chunks_ = 0;
++  trak->samples_size_ = 0;
++  trak->samples_ = 0;
++
++  return trak;
++}
++
++static void trak_exit(struct trak_t* trak)
++{
++  if(trak->unknown_atoms_)
++  {
++    unknown_atom_exit(trak->unknown_atoms_);
++  }
++  if(trak->tkhd_)
++  {
++    tkhd_exit(trak->tkhd_);
++  }
++  if(trak->mdia_)
++  {
++    mdia_exit(trak->mdia_);
++  }
++  if(trak->chunks_)
++  {
++    free(trak->chunks_);
++  }
++  if(trak->samples_)
++  {
++    free(trak->samples_);
++  }
++  free(trak);
++}
++
++static int trak_add_tkhd(void* parent, void* tkhd)
++{
++  struct trak_t* trak = parent;
++  trak->tkhd_ = tkhd;
++
++  return 1;
++}
++
++static int trak_add_mdia(void* parent, void* mdia)
++{
++  struct trak_t* trak = parent;
++  trak->mdia_ = mdia;
++
++  return 1;
++}
++
++static void* trak_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  struct trak_t* atom = trak_init();
++
++  struct atom_read_list_t atom_read_list[] = {
++    { FOURCC('t', 'k', 'h', 'd'), atom, &trak_add_tkhd, &tkhd_read },
++    { FOURCC('m', 'd', 'i', 'a'), atom, &trak_add_mdia, &mdia_read }
++  };
++
++  int result = atom_reader(atom_read_list,
++                  sizeof(atom_read_list) / sizeof(atom_read_list[0]),
++                  atom,
++                  buffer, size);
++
++  // check for mandatory atoms
++  if(!atom->tkhd_)
++  {
++    printf("trak: missing tkhd\n");
++    result = 0;
++  }
++
++  if(!atom->mdia_)
++  {
++    printf("trak: missing mdia\n");
++    result = 0;
++  }
++
++  if(!result)
++  {
++    trak_exit(atom);
++    return 0;
++  }
++
++  trak_build_index(atom);
++
++  return atom;
++}
++
++static unsigned char* trak_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
++{
++  struct trak_t* trak = atom;
++  struct atom_write_list_t atom_write_list[] = {
++    { FOURCC('t', 'k', 'h', 'd'), atom, trak->tkhd_, &tkhd_write },
++    { FOURCC('m', 'd', 'i', 'a'), atom, trak->mdia_, &mdia_write }
++  };
++
++  buffer = atom_writer(trak->unknown_atoms_,
++                       atom_write_list,
++                       sizeof(atom_write_list) / sizeof(atom_write_list[0]),
++                       buffer);
++
++  return buffer;
++}
++
++void trak_shift_offsets(struct trak_t* trak, int64_t offset)
++{
++  struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_;
++  stco_shift_offsets(stco, (int32_t)offset);
++}
++
++void trak_shift_offsets_inplace(struct trak_t* trak, int64_t offset)
++{
++  void* stco = trak->mdia_->minf_->stbl_->stco_inplace_;
++  stco_shift_offsets_inplace(stco, (int32_t)offset);
++}
++
++static struct mvhd_t* mvhd_init()
++{
++  struct mvhd_t* atom = (struct mvhd_t*)malloc(sizeof(struct mvhd_t));
++
++  return atom;
++}
++
++void mvhd_exit(struct mvhd_t* atom)
++{
++  free(atom);
++}
++
++static void* mvhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
++{
++  unsigned int i;
++
++  struct mvhd_t* atom = mvhd_init();
++  atom->version_ = read_8(buffer + 0);
++  atom->flags_ = read_24(buffer + 1);
++  if(atom->version_ == 0)
++  {
++    if(size < 108-8)
++      return 0;
 +
-+    }
-+//    memmove(table, table + stss_start, (i - stss_start) * sizeof(uint32_t));
-+    write_int32(stss + 4, i - stss_start);
++    atom->creation_time_ = read_32(buffer + 4);
++    atom->modification_time_ = read_32(buffer + 8);
++    atom->timescale_ = read_32(buffer + 12);
++    atom->duration_ = read_32(buffer + 16);
++    buffer += 20;
 +  }
++  else
++  {
++    if(size < 120-8)
++      return 0;
 +
-+  // process sample sizes
++    atom->creation_time_ = read_64(buffer + 4);
++    atom->modification_time_ = read_64(buffer + 12);
++    atom->timescale_ = read_32(buffer + 20);
++    atom->duration_ = read_64(buffer + 24);
++    buffer += 32;
++  }
++  atom->rate_ = read_32(buffer + 0);
++  atom->volume_ = read_16(buffer + 4);
++  atom->reserved1_ = read_16(buffer + 6);
++  atom->reserved2_[0] = read_32(buffer + 8);
++  atom->reserved2_[1] = read_32(buffer + 12);
++  buffer += 16;
++
++  for(i = 0; i != 9; ++i)
 +  {
-+    unsigned char* stsz = trak->mdia_.minf_.stbl_.stsz_;
-+    if(stsz_get_sample_size(stsz) == 0)
-+    {
-+      uint32_t* table = (uint32_t*)(stsz + 12);
-+      memmove(table, &table[start], (end - start) * sizeof(uint32_t));
-+      write_int32(stsz + 8, end - start);
-+    }
++    atom->matrix_[i] = read_32(buffer);
++    buffer += 4;
++  }
++
++  for(i = 0; i != 6; ++i)
++  {
++    atom->predefined_[i] = read_32(buffer);
++    buffer += 4;
 +  }
++
++  atom->next_track_id_ = read_32(buffer + 0);
++
++  return atom;
 +}
 +
-+int moov_parse(struct moov_t* moov, unsigned char* buffer, unsigned int size)
++static unsigned char* mvhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
 +{
-+  struct atom_t leaf_atom;
-+  unsigned char* buffer_start = buffer;
++  struct mvhd_t const* mvhd = atom;
++  unsigned int i;
 +
-+  moov->start_ = buffer;
-+  
-+  while(buffer < buffer_start + size)
++  buffer = write_8(buffer, mvhd->version_);
++  buffer = write_24(buffer, mvhd->flags_);
++
++  if(mvhd->version_ == 0)
 +  {
-+    buffer = atom_read_header(buffer, &leaf_atom);
++    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_);
++  }
 +
-+    atom_print(&leaf_atom);
++  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]);
 +
-+    if(atom_is(&leaf_atom, "cmov"))
-+    {
-+      return 0;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "mvhd"))
-+    {
-+      moov->mvhd_ = buffer;
-+    }
-+    else
-+    if(atom_is(&leaf_atom, "trak"))
-+    {
-+      if(moov->tracks_ == MAX_TRACKS)
-+        return 0;
-+      else
-+      {
-+        struct trak_t* trak = &moov->traks_[moov->tracks_];
-+        trak_init(trak);
-+        trak_parse(trak, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
-+        ++moov->tracks_;
-+      }
-+    }
-+    buffer = atom_skip(&leaf_atom);
++  for(i = 0; i != 9; ++i)
++  {
++    buffer = write_32(buffer, mvhd->matrix_[i]);
 +  }
 +
-+  // build the indexing tables
++  for(i = 0; i != 6; ++i)
 +  {
-+    unsigned int i;
-+    for(i = 0; i != moov->tracks_; ++i)
-+    {
-+      trak_build_index(&moov->traks_[i]);
-+    }
++    buffer = write_32(buffer, mvhd->predefined_[i]);
 +  }
 +
-+  return 1;
-+}
++  buffer = write_32(buffer, mvhd->next_track_id_);
 +
-+void stco_shift_offsets(unsigned char* stco, int offset)
-+{
-+  unsigned int entries = read_int32(stco + 4);
-+  unsigned int* table = (unsigned int*)(stco + 8);
-+  unsigned int i;
-+  for(i = 0; i != entries; ++i)
-+    write_int32(&table[i], (read_int32(&table[i]) + offset));
++  return buffer;
 +}
 +
-+void trak_shift_offsets(struct trak_t* trak, int offset)
++static struct moov_t* moov_init()
 +{
-+  unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
-+  stco_shift_offsets(stco, offset);
++  struct moov_t* moov = malloc(sizeof(struct moov_t));
++  moov->unknown_atoms_ = 0;
++  moov->mvhd_ = 0;
++  moov->tracks_ = 0;
++
++  return moov;
 +}
 +
-+void moov_shift_offsets(struct moov_t* moov, int offset)
++static void moov_exit(struct moov_t* atom)
 +{
 +  unsigned int i;
-+  for(i = 0; i != moov->tracks_; ++i)
++  if(atom->unknown_atoms_)
++  {
++    unknown_atom_exit(atom->unknown_atoms_);
++  }
++  if(atom->mvhd_)
++  {
++    mvhd_exit(atom->mvhd_);
++  }
++  for(i = 0; i != atom->tracks_; ++i)
 +  {
-+    trak_shift_offsets(&moov->traks_[i], offset);
++    trak_exit(atom->traks_[i]);
 +  }
++  free(atom);
 +}
 +
-+long mvhd_get_time_scale(unsigned char* mvhd)
++static int moov_add_mvhd(void* parent, void* mvhd)
 +{
-+  int version = read_char(mvhd);
-+  unsigned char* p = mvhd + (version == 0 ? 12 : 20);
-+  return read_int32(p);
++  struct moov_t* moov = parent;
++  moov->mvhd_ = mvhd;
++
++  return 1;
 +}
 +
-+void mvhd_set_duration(unsigned char* mvhd, long duration)
++static int moov_add_trak(void* parent, void* child)
 +{
-+  int version = read_char(mvhd);
-+  if(version == 0)
++  struct moov_t* moov = parent;
++  struct trak_t* trak = child;
++  if(moov->tracks_ == MAX_TRACKS)
 +  {
-+    write_int32(mvhd + 16, duration);
++    trak_exit(trak);
++    return 0;
 +  }
-+  else
++
++  if(trak->mdia_->hdlr_->handler_type_ != FOURCC('v', 'i', 'd', 'e') &&
++     trak->mdia_->hdlr_->handler_type_ != FOURCC('s', 'o', 'u', 'n'))
 +  {
-+    perror("mvhd_set_duration");
-+//    write_int64(mvhd + 24, duration);
++    printf("Trak ignored (handler_type=%c%c%c%c, name=%s)\n",
++      trak->mdia_->hdlr_->handler_type_ >> 24,
++      trak->mdia_->hdlr_->handler_type_ >> 16,
++      trak->mdia_->hdlr_->handler_type_ >> 8,
++      trak->mdia_->hdlr_->handler_type_,
++      trak->mdia_->hdlr_->name_);
++    trak_exit(trak);
++    return 1; // continue
 +  }
-+}
++  
++  moov->traks_[moov->tracks_] = trak;
++  ++moov->tracks_;
 +
-+long mdhd_get_time_scale(unsigned char* mdhd)
-+{
-+  return read_int32(mdhd + 12);
++  return 1;
 +}
 +
-+void mdhd_set_duration(unsigned char* mdhd, unsigned int duration)
++static void* moov_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
 +{
-+  write_int32(mdhd + 16, duration);
-+}
++  struct moov_t* atom = moov_init();
 +
-+void tkhd_set_duration(unsigned char* tkhd, unsigned int duration)
-+{
-+  int version = read_char(tkhd);
-+  if(version == 0)
++  struct atom_read_list_t atom_read_list[] = {
++    { FOURCC('m', 'v', 'h', 'd'), atom, &moov_add_mvhd, &mvhd_read },
++    { FOURCC('t', 'r', 'a', 'k'), atom, &moov_add_trak, &trak_read }
++  };
++
++  int result = atom_reader(atom_read_list,
++                  sizeof(atom_read_list) / sizeof(atom_read_list[0]),
++                  atom,
++                  buffer, size);
++
++  // check for mandatory atoms
++  if(!atom->mvhd_)
 +  {
-+    write_int32(tkhd + 20, duration);
++    printf("moov: missing mvhd\n");
++    result = 0;
 +  }
-+  else
++
++  if(!atom->tracks_)
 +  {
-+    perror("tkhd_set_duration");
-+//    write_int64(tkhd + 28, duration);
++    printf("moov: missing trak\n");
++    result = 0;
 +  }
-+}
 +
-+unsigned int stss_get_entries(unsigned char const* stss)
-+{
-+  return read_int32(stss + 4);
++  if(!result)
++  {
++    moov_exit(atom);
++    return 0;
++  }
++
++  return atom;
 +}
 +
-+unsigned int stss_get_sample(unsigned char const* stss, unsigned int idx)
++static void moov_write(struct moov_t* atom, unsigned char* buffer)
 +{
-+  unsigned char const* p = stss + 8 + idx * 4;
-+  return read_int32(p);
++  unsigned i;
++
++  unsigned char* atom_start = buffer;
++
++  struct atom_write_list_t atom_write_list[] = {
++    { FOURCC('m', 'v', 'h', 'd'), atom, atom->mvhd_, &mvhd_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)
++  {
++    struct atom_write_list_t trak_atom_write_list[] = {
++      { FOURCC('t', 'r', 'a', 'k'), atom, 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, buffer - atom_start);
 +}
 +
-+unsigned int stss_get_nearest_keyframe(unsigned char const* stss, unsigned int sample)
++void moov_shift_offsets(struct moov_t* moov, int64_t offset)
 +{
-+  // scan the sync samples to find the key frame that precedes the sample number
 +  unsigned int i;
-+  unsigned int entries = stss_get_entries(stss);
-+  unsigned int table_sample = 0;
-+  for(i = 0; i != entries; ++i)
++  for(i = 0; i != moov->tracks_; ++i)
 +  {
-+    table_sample = stss_get_sample(stss, i);
-+    if(table_sample >= sample)
-+      break;
++    trak_shift_offsets(moov->traks_[i], offset);
 +  }
-+  if(table_sample == sample)
-+    return table_sample;
-+  else
-+    return stss_get_sample(stss, i - 1);
 +}
 +
-+unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample)
++void moov_shift_offsets_inplace(struct moov_t* moov, int64_t offset)
 +{
-+  // If the sync atom is not present, all samples are implicit sync samples.
-+  if(!stbl->stss_)
-+    return sample;
-+
-+  return stss_get_nearest_keyframe(stbl->stss_, sample);
++  unsigned int i;
++  for(i = 0; i != moov->tracks_; ++i)
++  {
++    trak_shift_offsets_inplace(moov->traks_[i], offset);
++  }
 +}
 +
-+unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
++unsigned int moov_seek(unsigned char* moov_data,
++                       uint64_t* moov_size,
 +                       float start_time,
-+                       unsigned int* mdat_start,
-+                       unsigned int* mdat_size,
-+                       unsigned int offset)
++                       float end_time,
++                       uint64_t* mdat_start,
++                       uint64_t* mdat_size,
++                       uint64_t offset,
++                       int client_is_flash)
 +{
-+  struct moov_t* moov = malloc(sizeof(struct moov_t));
-+  moov_init(moov);
-+  if(!moov_parse(moov, moov_data, size))
++  struct moov_t* moov = moov_read(NULL, moov_data + ATOM_PREAMBLE_SIZE,
++                                  *moov_size - ATOM_PREAMBLE_SIZE);
++
++  if(moov == 0 || moov->mvhd_ == 0)
 +  {
-+    moov_exit(moov);
-+    free(moov);
++    printf("Error parsing moov header\n");
 +    return 0;
 +  }
 +
 +  {
-+    long moov_time_scale = mvhd_get_time_scale(moov->mvhd_);
++    long moov_time_scale = moov->mvhd_->timescale_;
 +    unsigned int start = (unsigned int)(start_time * moov_time_scale);
-+//  unsigned int end = (unsigned int)(end_time * moov_time_scale);
-+    unsigned int bytes_to_skip = UINT_MAX;
++    unsigned int end = (unsigned int)(end_time * moov_time_scale);
++    uint64_t skip_from_start = UINT64_MAX;
++    uint64_t end_offset = 0;
 +    unsigned int i;
++    unsigned int pass;
 +
 +    // for every trak, convert seconds to sample (time-to-sample).
 +    // adjust sample to keyframe
 +    unsigned int trak_sample_start[MAX_TRACKS];
-+//  unsigned int trak_sample_end[MAX_TRACKS];
++    unsigned int trak_sample_end[MAX_TRACKS];
 +
-+    unsigned int moov_duration = 0;
++    uint64_t moov_duration = 0;
 +
 +    // clayton.mp4 has a third track with one sample that lasts the whole clip.
 +    // Assuming the first two tracks are the audio and video track, we patch
 +    // the remaining tracks to 'free' atoms.
-+    if(moov->tracks_ > 2)
++//    if(moov->tracks_ > 2)
++//    {
++//      for(i = 2; i != moov->tracks_; ++i)
++//      {
++//        // patch 'trak' to 'free'
++//        unsigned char* p = moov->traks_[i].start_ - 4;
++//        p[0] = 'f';
++//        p[1] = 'r';
++//        p[2] = 'e';
++//        p[3] = 'e';
++//      }
++//      moov->tracks_ = 2;
++//    }
++
++    // reported by everwanna:
++    // av out of sync because: 
++    // audio track 0 without stss, seek to the exact time. 
++    // video track 1 with stss, seek to the nearest key frame time.
++    //
++    // fixed:
++    // first pass we get the new aligned times for traks with an stss present
++    // second pass is for traks without an stss
++    for(pass = 0; pass != 2; ++pass)
 +    {
-+      for(i = 2; i != moov->tracks_; ++i)
++      for(i = 0; i != moov->tracks_; ++i)
 +      {
-+        // patch 'trak' to 'free'
-+        unsigned char* p = moov->traks_[i].start_ - 4;
-+        p[0] = 'f';
-+        p[1] = 'r';
-+        p[2] = 'e';
-+        p[3] = 'e';
++        struct trak_t* trak = moov->traks_[i];
++        struct stbl_t* stbl = trak->mdia_->minf_->stbl_;
++        long trak_time_scale = trak->mdia_->mdhd_->timescale_;
++        float moov_to_trak_time = (float)trak_time_scale / (float)moov_time_scale;
++        float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
++
++        // 1st pass: stss present, 2nd pass: no stss present
++        if(pass == 0 && !stbl->stss_)
++          continue;
++        if(pass == 1 && stbl->stss_)
++          continue;
++
++        // ignore empty track
++        if(trak->mdia_->mdhd_->duration_ == 0)
++          continue;
++
++        // get start
++        if(start == 0)
++        {
++          trak_sample_start[i] = start;
++        }
++        else
++        {
++          start = stts_get_sample(stbl->stts_, (uint64_t)(start * moov_to_trak_time));
++          printf("start=%u (trac time)=%.2f (seconds)", start,
++            stts_get_time(stbl->stts_, start) / (float)trak_time_scale);
++          start = stbl_get_nearest_keyframe(stbl, start + 1) - 1;
++          printf("=%u (zero based keyframe)", start);
++          trak_sample_start[i] = start;
++          start = (unsigned int)(stts_get_time(stbl->stts_, start) * trak_to_moov_time);
++          printf("=%u (moov time)\n", start);
++        }
++
++        // get end
++        if(end == 0)
++        {
++          trak_sample_end[i] = trak->samples_size_;
++        }
++        else
++        {
++          end = stts_get_sample(stbl->stts_, (uint64_t)(end * moov_to_trak_time));
++          if(end >= trak->samples_size_)
++          {
++            end = trak->samples_size_;
++          }
++          else
++          {
++            end = stbl_get_nearest_keyframe(stbl, end + 1) - 1;
++          }
++          trak_sample_end[i] = end;
++          printf("endframe=%u, samples_size_=%u\n", end, trak->samples_size_);
++          end = (unsigned int)(stts_get_time(stbl->stts_, end) * trak_to_moov_time);
++        }
 +      }
-+      moov->tracks_ = 2;
 +    }
 +
-+    for(i = 0; i != moov->tracks_; ++i)
++    printf("start=%u\n", start);
++    printf("end=%u\n", end);
++
++    if(end && start >= end)
 +    {
-+      struct trak_t* trak = &moov->traks_[i];
-+      struct stbl_t* stbl = &trak->mdia_.minf_.stbl_;
-+      long trak_time_scale = mdhd_get_time_scale(trak->mdia_.mdhd_);
-+      float moov_to_trak_time = (float)trak_time_scale / (float)moov_time_scale;
-+      float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
-+
-+      start = stts_get_sample(stbl->stts_, (unsigned int)(start * moov_to_trak_time));
-+      start = stbl_get_nearest_keyframe(stbl, start + 1) - 1;
-+      trak_sample_start[i] = start;
-+      start = (unsigned int)(stts_get_time(stbl->stts_, start) * trak_to_moov_time);
++      moov_exit(moov);
++      return 0;
 +    }
 +
-+    printf("start=%u\n", start);
-+
 +    for(i = 0; i != moov->tracks_; ++i)
 +    {
-+      struct trak_t* trak = &moov->traks_[i];
-+      struct stbl_t* stbl = &trak->mdia_.minf_.stbl_;
++      struct trak_t* trak = moov->traks_[i];
++      struct stbl_t* stbl = trak->mdia_->minf_->stbl_;
 +
 +      unsigned int start_sample = trak_sample_start[i];
-+      unsigned int end_sample = trak->samples_size_;
++      unsigned int end_sample = trak_sample_end[i];
++
++      // ignore empty track
++      if(trak->mdia_->mdhd_->duration_ == 0)
++        continue;
 +
-+      trak_write_index(trak, start_sample, end_sample);
++      trak_update_index(trak, start_sample, end_sample);
 +
 +      {
-+        unsigned skip =
++        uint64_t skip =
 +          trak->samples_[start_sample].pos_ - trak->samples_[0].pos_;
-+        if(skip < bytes_to_skip)
-+          bytes_to_skip = skip;
-+        printf("Trak can skip %u bytes\n", skip);
++        if(skip < skip_from_start)
++          skip_from_start = skip;
++        printf("Trak can skip %llu bytes\n", skip);
++
++        if(end_sample != trak->samples_size_)
++        {
++          uint64_t end_pos = trak->samples_[end_sample].pos_;
++          if(end_pos > end_offset)
++            end_offset = end_pos;
++          printf("New endpos=%llu\n", end_pos);
++          printf("Trak can skip %llu bytes at end\n",
++                 *mdat_start + *mdat_size - end_offset);
++        }
 +      }
 +
 +      {
 +        // fixup trak (duration)
-+        unsigned int trak_duration = stts_get_duration(stbl->stts_);
-+        long trak_time_scale = mdhd_get_time_scale(trak->mdia_.mdhd_);
++        uint64_t trak_duration = stts_get_duration(stbl->stts_);
++        long trak_time_scale = trak->mdia_->mdhd_->timescale_;
 +        float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
-+        unsigned int duration = (long)((float)trak_duration * trak_to_moov_time);
-+        mdhd_set_duration(trak->mdia_.mdhd_, trak_duration);
-+        tkhd_set_duration(trak->tkhd_, duration);
-+        printf("trak: new_duration=%d\n", duration);
++        {
++        uint64_t duration = (long)((float)trak_duration * trak_to_moov_time);
++        trak->mdia_->mdhd_->duration_= trak_duration;
++        trak->tkhd_->duration_ = duration;
++        printf("trak: new_duration=%lld\n", duration);
 +
 +        if(duration > moov_duration)
 +          moov_duration = duration;
++        }
 +      }
 +
-+      printf("stco.size=%d, ", read_int32(stbl->stco_ + 4));
-+      printf("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_));
-+      printf("stsz.size=%d\n", read_int32(stbl->stsz_ + 8));
-+      printf("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_));
++//      printf("stco.size=%d, ", read_int32(stbl->stco_ + 4));
++//      printf("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_));
++//      printf("stsz.size=%d\n", read_int32(stbl->stsz_ + 8));
++//      printf("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_));
 +    }
-+    mvhd_set_duration(moov->mvhd_, moov_duration);
++    moov->mvhd_->duration_ = moov_duration;
++
++    // subtract bytes we skip at the front of the mdat atom
++    offset -= skip_from_start;
 +
-+    offset -= bytes_to_skip;
++    // subtract old moov size
++    offset -= *moov_size;
++
++    printf("moov: writing header\n");
++
++    moov_write(moov, moov_data);
++    *moov_size = read_32(moov_data);
++
++    // add new moov size
++    offset += *moov_size;
++
++    printf("shifting offsets by %lld\n", offset);
++    moov_shift_offsets_inplace(moov, offset);
++
++//    moov_write(moov, moov_data);
++
++#ifdef COMPRESS_MOOV_ATOM
++    if(!client_is_flash)
++    {
++      uLong sourceLen = *moov_size - ATOM_PREAMBLE_SIZE;
++      uLong destLen = compressBound(sourceLen);
++      unsigned char* cmov = malloc(destLen);
++      int zstatus = compress(cmov, &destLen, moov_data, sourceLen);
++      if(zstatus == Z_OK)
++      {
++        printf("cmov size = %lu (%ld%%)\n", destLen, 100 * destLen / sourceLen);
++      }
 +
-+    printf("shifting offsets by %d\n", offset);
-+    moov_shift_offsets(moov, offset);
++      {
++        const int extra_space = 4096;
++        if(destLen + extra_space < sourceLen)
++        {
++          const int bytes_saved = sourceLen - destLen;
++          uLong destLen2;
++          int extra = 0;
++          printf("shifting offsets by %d\n", -bytes_saved);
++          moov_shift_offsets_inplace(moov, -bytes_saved);
++
++          extra += ATOM_PREAMBLE_SIZE + 4;            // dcom
++          extra += ATOM_PREAMBLE_SIZE + 4;            // cmvd
++          extra += ATOM_PREAMBLE_SIZE;                // cmov
++          extra += ATOM_PREAMBLE_SIZE + extra_space;  // free
++
++          printf("shifting offsets by %d\n", extra);
++          moov_shift_offsets_inplace(moov, extra);
++
++          // recompress
++          destLen2 = compressBound(sourceLen);
++          zstatus = compress(cmov, &destLen2, moov_data, sourceLen);
++          if(zstatus == Z_OK)
++          {
++            printf("cmov size = %lu (%ld%%)\n", destLen2, 100 * destLen2 / sourceLen);
++
++            if(destLen2 < destLen + extra_space)
++            {
++              // copy compressed movie atom
++              unsigned char* outbuffer = moov_data;
++
++              uint32_t dcom_size = ATOM_PREAMBLE_SIZE + 4;
++              uint32_t cmvd_size = ATOM_PREAMBLE_SIZE + 4 + destLen2;
++              uint32_t cmov_size = ATOM_PREAMBLE_SIZE + dcom_size + cmvd_size;
++              uint32_t free_size = ATOM_PREAMBLE_SIZE + extra_space + destLen - destLen2;
++              *moov_size = ATOM_PREAMBLE_SIZE + cmov_size + free_size;
++
++              outbuffer = write_32(outbuffer, (uint32_t)*moov_size);
++
++              // skip 'moov'
++              outbuffer += 4;
++
++              outbuffer = write_32(outbuffer, cmov_size);
++              {
++                outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'o', 'v'));
++                outbuffer = write_32(outbuffer, dcom_size);
++                outbuffer = write_32(outbuffer, FOURCC('d', 'c', 'o', 'm'));
++                outbuffer = write_32(outbuffer, FOURCC('z', 'l', 'i', 'b'));
++
++                outbuffer = write_32(outbuffer, cmvd_size);
++                {
++                  outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'v', 'd'));
++                  outbuffer = write_32(outbuffer, sourceLen);
++                  memcpy(outbuffer, cmov, destLen2);
++                  outbuffer += destLen2;
++                }
++              }
++
++              // add final padding
++              outbuffer = write_32(outbuffer, free_size);
++              outbuffer = write_32(outbuffer, FOURCC('f', 'r', 'e', 'e'));
++              {
++                const char free_bytes[8] =
++                {
++                  'C', 'o', 'd', 'e','S','h', 'o', 'p'
++                };
++                uint32_t padding_index;
++                for(padding_index = ATOM_PREAMBLE_SIZE; padding_index != free_size; ++padding_index)
++                {
++                  outbuffer[padding_index] = free_bytes[padding_index % 8];
++                }
++              }
++            }
++            else
++            {
++              printf("2nd pass compress overflow\n");
++            }
++          }
++        }
++      }
++      free(cmov);
++    }
++#endif
 +
-+    *mdat_start += bytes_to_skip;
-+    *mdat_size -= bytes_to_skip;
++    *mdat_start += skip_from_start;
++    if(end_offset != 0)
++    {
++      *mdat_size = end_offset;
++    }
++    *mdat_size -= skip_from_start;
 +  }
 +
 +  moov_exit(moov);
-+  free(moov);
 +
 +  return 1;
 +}
 +
++////////////////////////////////////////////////////////////////////////////////
++
++struct mp4_atom_t
++{
++  uint32_t type_;
++  uint32_t short_size_;
++  uint64_t size_;
++  uint64_t start_;
++  uint64_t end_;
++};
++
++static int mp4_atom_read_header(FILE* infile, struct mp4_atom_t* atom)
++{
++  unsigned char atom_header[8];
++
++  atom->start_ = ftell(infile);
++  fread(atom_header, 8, 1, infile);
++  atom->short_size_ = read_32(&atom_header[0]);
++  atom->type_ = read_32(&atom_header[4]);
++
++  if(atom->short_size_ == 1)
++  {
++    fread(atom_header, 8, 1, infile);
++    atom->size_ = read_64(&atom_header[0]);
++  }
++  else
++  {
++    atom->size_ = atom->short_size_;
++  }
++
++  atom->end_ = atom->start_ + atom->size_;
++
++  return 1;
++}
++
++static int mp4_atom_write_header(unsigned char* outbuffer, struct mp4_atom_t* atom)
++{
++  int write_box64 = atom->short_size_ == 1 ? 1 : 0;
++
++  if(write_box64)
++    write_32(outbuffer, 1);
++  else
++    write_32(outbuffer, (uint32_t)atom->size_);
++
++  write_32(outbuffer + 4, atom->type_);
++
++  if(write_box64)
++  {
++    write_64(outbuffer + 8, atom->size_);
++    return 16;
++  }
++  else
++  {
++    return 8;
++  }
++}
++
++int mp4_split(const char* filename, int64_t filesize,
++              float start_time, float end_time,
++              void** mp4_header, uint32_t* mp4_header_size,
++              uint64_t* mdat_offset, uint64_t* mdat_size,
++              int client_is_flash)
++{
++  FILE* infile;
++  struct mp4_atom_t ftyp_atom;
++  struct mp4_atom_t moov_atom;
++  struct mp4_atom_t mdat_atom;
++  unsigned char* moov_data = 0;
++  unsigned char* buffer;
++  uint64_t new_mdat_start;
++
++  *mp4_header = 0;
++  memset(&ftyp_atom, 0, sizeof(ftyp_atom));
++  memset(&moov_atom, 0, sizeof(moov_atom));
++  memset(&mdat_atom, 0, sizeof(mdat_atom));
++
++  infile = fopen(filename, "rb");
++  if(infile == NULL)
++  {
++    return 0;
++  }
++
++  while(ftello(infile) < filesize)
++  {
++    struct mp4_atom_t leaf_atom;
++
++    if(!mp4_atom_read_header(infile, &leaf_atom))
++      break;
++
++    printf("Atom(%c%c%c%c,%lld)\n",
++           leaf_atom.type_ >> 24, leaf_atom.type_ >> 16,
++           leaf_atom.type_ >> 8, leaf_atom.type_,
++           leaf_atom.size_);
++
++    switch(leaf_atom.type_)
++    {
++    case FOURCC('f', 't', 'y', 'p'):
++      ftyp_atom = leaf_atom;
++      break;
++    case FOURCC('m', 'o', 'o', 'v'):
++      moov_atom = leaf_atom;
++      moov_data = (unsigned char*)malloc((size_t)moov_atom.size_);
++      fseeko(infile, moov_atom.start_, SEEK_SET);
++      fread(moov_data, (off_t)moov_atom.size_, 1, infile);
++      break;
++    case FOURCC('m', 'd', 'a', 't'):
++      mdat_atom = leaf_atom;
++      break;
++    }
++    fseeko(infile, leaf_atom.end_, SEEK_SET);
++  }
++
++  if(moov_atom.size_ == 0)
++  {
++    printf("Error: moov atom not found\n");
++    fclose(infile);
++    return 0;
++  }
++
++  if(mdat_atom.size_ == 0)
++  {
++    printf("Error: mdat atom not found\n");
++    fclose(infile);
++    return 0;
++  }
++
++  buffer = (unsigned char*)malloc((uint32_t)moov_atom.size_ + 4 * 1024);
++  *mp4_header = buffer;
++
++  if(ftyp_atom.size_)
++  {
++    fseeko(infile, ftyp_atom.start_, SEEK_SET);
++    fread(buffer, (off_t)ftyp_atom.size_, 1, infile);
++    buffer += ftyp_atom.size_;
++  }
++
++  {
++    static char const free_data[] = {
++      0x0, 0x0, 0x0,  42, 'f', 'r', 'e', 'e',
++      'v', 'i', 'd', 'e', 'o', ' ', 's', 'e',
++      'r', 'v', 'e', 'd', ' ', 'b', 'y', ' ',
++      'm', 'o', 'd', '_', 'h', '2', '6', '4',
++      '_', 's', 't', 'r', 'e', 'a', 'm', 'i',
++      'n', 'g'
++    };
++    memcpy(buffer, free_data, sizeof(free_data));
++    buffer += sizeof(free_data);
++  }
++
++  new_mdat_start = buffer - (unsigned char*)(*mp4_header) + moov_atom.size_;
++  if(!moov_seek(moov_data,
++                &moov_atom.size_,
++                start_time,
++                end_time,
++                &mdat_atom.start_,
++                &mdat_atom.size_,
++                new_mdat_start - mdat_atom.start_,
++                client_is_flash))
++  {
++    free(moov_data);
++    fclose(infile);
++    return 0;
++  }
++
++  memcpy(buffer, moov_data, (uint32_t)moov_atom.size_);
++  buffer += moov_atom.size_;
++  free(moov_data);
++
++  {
++    int mdat_header_size = mp4_atom_write_header(buffer, &mdat_atom);
++    buffer += mdat_header_size;
++    *mdat_offset = mdat_atom.start_ + mdat_header_size;
++    *mdat_size = mdat_atom.size_ - mdat_header_size;
++  }
++
++  *mp4_header_size = (uint32_t)(buffer - (unsigned char*)(*mp4_header));
++
++  fclose(infile);
++
++  return 1;
++}
++
++// End Of File
++
+--- /dev/null  2008-11-04 20:33:38.146691408 +0200
++++ lighttpd-1.4.18/src/moov.h 2009-01-26 21:00:05.071936866 +0200
+@@ -0,0 +1,49 @@
++/*******************************************************************************
++ moov.h (version 2)
++
++ moov - A library for splitting Quicktime/MPEG4 files.
++ http://h264.code-shop.com
++
++ Copyright (C) 2007-2009 CodeShop B.V.
++
++ Licensing
++ The H264 Streaming Module is licened under a Creative Common License. It allows
++ you to use, modify and redistribute the module, but only for *noncommercial*
++ purposes. For corporate use, please apply for a commercial license.
++
++ Creative Commons License:
++ http://creativecommons.org/licenses/by-nc-sa/3.0/
++
++ Commercial License:
++ http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2
++******************************************************************************/ 
++
++// NOTE: don't include stdio.h (for FILE) or sys/types.h (for off_t).
++// nginx redefines _FILE_OFFSET_BITS and off_t will have different sizes
++// depending on include order
++
++#ifndef _MSC_VER
++#include <inttypes.h>
++#else
++#include "inttypes.h"
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++extern int mp4_split(const char* infile, int64_t filesize,
++                     float start_time, float end_time,
++                     void** mp4_header, uint32_t* mp4_header_size,
++                     uint64_t* mdat_offset, uint64_t* mdat_size,
++                     int client_is_flash);
++
++/* Returns true when the test string is a prefix of the input */
++extern int starts_with(const char* input, const char* test);
++
++#ifdef __cplusplus
++} /* extern C definitions */
++#endif
++
 +// End Of File
 +
This page took 0.203711 seconds and 4 git commands to generate.