-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 @@
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>
+#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
+*/
+#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;
+
+ if(sample_size == 0)
+ {
-+ trak->samples_size_ = stsz_get_entries(stsz);
++ trak->samples_size_ = stsz->entries_;
+ }
+ else
+ {
+ {
+ 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
+ {
+
+ // 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;
+
+ // 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;
+ {
+ 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_;
+ }
+}
+
-+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;
+ ++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",
+
+ // 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;
+ ++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",
+
+ // 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)
+ {
+ 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
+