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 @@
mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_flv_streaming_la_LIBADD = $(common_libadd)
+lib_LTLIBRARIES += mod_h264_streaming.la
+mod_h264_streaming_la_SOURCES = mod_h264_streaming.c moov.c
+mod_h264_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
+mod_h264_streaming_la_LIBADD = $(common_libadd)
+
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 @@
+/*******************************************************************************
+ 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 .
+******************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include "base.h"
+#include "log.h"
+#include "buffer.h"
+#include "response.h"
+#include "http_chunk.h"
+#include "stat_cache.h"
+
+#include "plugin.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* plugin config for all request/connections */
+
+typedef struct {
+ array *extensions;
+} plugin_config;
+
+typedef struct {
+ PLUGIN_DATA;
+
+ buffer *query_str;
+ array *get_params;
+
+ plugin_config **config_storage;
+
+ plugin_config conf;
+} plugin_data;
+
+/* init the plugin data */
+INIT_FUNC(mod_h264_streaming_init) {
+ plugin_data *p;
+
+ p = calloc(1, sizeof(*p));
+
+ p->query_str = buffer_init();
+ p->get_params = array_init();
+
+ return p;
+}
+
+/* detroy the plugin data */
+FREE_FUNC(mod_h264_streaming_free) {
+ plugin_data *p = p_d;
+
+ UNUSED(srv);
+
+ if (!p) return HANDLER_GO_ON;
+
+ if (p->config_storage) {
+ size_t i;
+
+ for (i = 0; i < srv->config_context->used; i++) {
+ plugin_config *s = p->config_storage[i];
+
+ if (!s) continue;
+
+ array_free(s->extensions);
+
+ free(s);
+ }
+ free(p->config_storage);
+ }
+
+ buffer_free(p->query_str);
+ array_free(p->get_params);
+
+ free(p);
+
+ 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;
+
+ 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;
+
+ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
+
+ for (i = 0; i < srv->config_context->used; i++) {
+ plugin_config *s;
+
+ s = calloc(1, sizeof(plugin_config));
+ s->extensions = array_init();
+
+ cv[0].destination = s->extensions;
+
+ p->config_storage[i] = s;
+
+ if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
+ return HANDLER_ERROR;
+ }
+ }
+
+ return HANDLER_GO_ON;
+}
+
+#define PATCH(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];
+
+ 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];
+
+ /* 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];
+
+ if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
+ PATCH(extensions);
+ }
+ }
+ }
+
+ 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 & */
+
+ /* terminate the value */
+ qrystr->ptr[i] = '\0';
+
+ 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));
+
+ array_insert_unique(get_params, (data_unset *)ds);
+ }
+
+ 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
+
+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]);
+}
+
+int atom_read_header(FILE* infile, struct atom_t* atom)
+{
+ unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
+
+ atom->start_ = ftell(infile);
+
+ 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_;
+
+ return 1;
+}
+
+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]);
+}
+
+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])
+ ;
+}
+
+void atom_skip(FILE* infile, struct atom_t const* atom)
+{
+ fseek(infile, atom->end_, SEEK_SET);
+}
+
+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_);
+}
+
+
+URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
+ plugin_data *p = p_d;
+ int s_len;
+ size_t k;
+
+ UNUSED(srv);
+
+ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
+
+ mod_h264_streaming_patch_connection(srv, con, p);
+
+ s_len = con->physical.path->used - 1;
+
+ 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 (ct_len > s_len) continue;
+ if (ds->value->used == 0) continue;
+
+ 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 */
+
+ array_reset(p->get_params);
+ buffer_copy_string_buffer(p->query_str, con->uri.query);
+ split_get_params(p->get_params, p->query_str);
+
+ if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
+ return HANDLER_GO_ON;
+ }
+
+ /* too short */
+// if (get_param->value->used < 2) return HANDLER_GO_ON;
+
+ /* 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 (start <= 0) return HANDLER_GO_ON;
+ if (start < 0) return HANDLER_GO_ON;
+
+ /* check if start is > filesize */
+ if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
+ return HANDLER_GO_ON;
+ }
+
+// if (start > sce->st.st_size) {
+// 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"));
+
+ con->file_finished = 1;
+
+ return HANDLER_FINISHED;
+ }
+ }
+
+ /* 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->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;
+
+ 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
+
+ moov - A library for splitting 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 .
+******************************************************************************/
+
+/*
+ Uses code snippets from the libquicktime library:
+ http://libquicktime.sourceforge.net
+
+ The QuickTime File Format PDF from Apple:
+ http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+static int read_char(unsigned char const* buffer)
+{
+ return buffer[0];
+}
+
+static int read_int32(void const* buffer)
+{
+ unsigned char* p = (unsigned char*)buffer;
+ return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
+}
+
+static void write_int32(void* outbuffer, uint32_t value)
+{
+ 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);
+}
+
+struct atom_t
+{
+ unsigned char type_[4];
+ unsigned int size_;
+ unsigned char* start_;
+ unsigned char* end_;
+};
+
+#define ATOM_PREAMBLE_SIZE 8
+
+static 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]);
+}
+
+static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
+{
+ atom->start_ = buffer;
+ memcpy(&atom->type_[0], &buffer[4], 4);
+ atom->size_ = atom_header_size(buffer);
+ atom->end_ = atom->start_ + atom->size_;
+
+ return buffer + ATOM_PREAMBLE_SIZE;
+}
+
+static unsigned char* atom_skip(struct atom_t const* atom)
+{
+ return atom->end_;
+}
+
+static 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])
+ ;
+}
+
+static 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_);
+}
+
+#define MAX_TRACKS 8
+
+unsigned int stts_get_entries(unsigned char const* stts)
+{
+ return read_int32(stts + 4);
+}
+
+void stts_get_sample_count_and_duration(unsigned char const* stts,
+ unsigned int idx, unsigned int* sample_count, unsigned int* sample_duration)
+{
+ unsigned char const* table = stts + 8 + idx * 8;
+ *sample_count = read_int32(table);
+ *sample_duration = read_int32(table + 4);
+}
+
+struct stts_table_t
+{
+ uint32_t sample_count_;
+ uint32_t sample_duration_;
+};
+
+unsigned int ctts_get_entries(unsigned char const* ctts)
+{
+ return read_int32(ctts + 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);
+}
+
+unsigned int ctts_get_samples(unsigned char const* ctts)
+{
+ 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;
+}
+
+struct ctts_table_t
+{
+ uint32_t sample_count_;
+ uint32_t sample_offset_;
+};
+
+struct stsc_table_t
+{
+ uint32_t chunk_;
+ uint32_t samples_;
+ uint32_t id_;
+};
+
+unsigned int stsc_get_entries(unsigned char const* stsc)
+{
+ return read_int32(stsc + 4);
+}
+
+void stsc_get_table(unsigned char const* stsc, unsigned int i, struct stsc_table_t *stsc_table)
+{
+ 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_);
+}
+
+unsigned int stsc_get_chunk(unsigned char* stsc, unsigned int sample)
+{
+ unsigned int entries = read_int32(stsc + 4);
+ struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
+
+ 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;
+
+ if(sample < total + range_samples)
+ break;
+
+ chunk1samples = read_int32(&table[chunk2entry].samples_);
+ chunk1 = chunk2;
+
+ if(chunk2entry < entries)
+ {
+ chunk2entry++;
+ total += range_samples;
+ }
+ } while(chunk2entry < entries);
+
+ if(chunk1samples)
+ {
+ 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;
+ }
+ else
+ chunk = 1;
+
+ chunk_sample = total + (chunk - chunk1) * chunk1samples;
+
+ return chunk;
+ }
+}
+
+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)
+ {
+ samples += read_int32(&table[i].samples_);
+ }
+ 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);
+}
+
+unsigned int stsz_get_size(unsigned char const* stsz, unsigned int idx)
+{
+ uint32_t const* table = (uint32_t const*)(stsz + 12);
+ return read_int32(&table[idx]);
+}
+
+unsigned int stts_get_duration(unsigned char const* stts)
+{
+ long duration = 0;
+ long entries = stts_get_entries(stts);
+ int i;
+ for(i = 0; i != entries; ++i)
+ {
+ 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;
+ }
+
+ return duration;
+}
+
+unsigned int stts_get_samples(unsigned char const* stts)
+{
+ long samples = 0;
+ long entries = stts_get_entries(stts);
+ int i;
+ for(i = 0; i != entries; ++i)
+ {
+ unsigned int sample_count;
+ unsigned int sample_duration;
+ stts_get_sample_count_and_duration(stts, i,
+ &sample_count, &sample_duration);
+ samples += sample_count;
+ }
+
+ 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)
+ {
+ 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
+ {
+ time_count += sample_duration * sample_count;
+ ret += sample_count;
+// stts_index++;
+ }
+// if(stts_index >= table_.size())
+// break;
+ }
+// *time = time_count;
+ return ret;
+}
+
+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);
+
+ 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++;
+ }
+ }
+ return ret;
+}
+
+
+struct stbl_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
+};
+
+void stbl_parse(struct stbl_t* stbl, unsigned char* buffer, unsigned int size)
+{
+ struct atom_t leaf_atom;
+ unsigned char* buffer_start = buffer;
+ stbl->stss_ = 0;
+ stbl->ctts_ = 0;
+
+ stbl->start_ = buffer;
+
+ while(buffer < buffer_start + size)
+ {
+ buffer = atom_read_header(buffer, &leaf_atom);
+
+ atom_print(&leaf_atom);
+
+ 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"))
+ {
+ perror("TODO: co64");
+ }
+ else
+ if(atom_is(&leaf_atom, "ctts"))
+ {
+ stbl->ctts_ = buffer;
+ }
+
+ buffer = atom_skip(&leaf_atom);
+ }
+}
+
+struct minf_t
+{
+ unsigned char* start_;
+ struct stbl_t stbl_;
+};
+
+void minf_parse(struct minf_t* minf, unsigned char* buffer, unsigned int size)
+{
+ struct atom_t leaf_atom;
+ unsigned char* buffer_start = buffer;
+
+ minf->start_ = buffer;
+
+ while(buffer < buffer_start + size)
+ {
+ buffer = atom_read_header(buffer, &leaf_atom);
+
+ atom_print(&leaf_atom);
+
+ if(atom_is(&leaf_atom, "stbl"))
+ {
+ stbl_parse(&minf->stbl_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
+ }
+
+ buffer = atom_skip(&leaf_atom);
+ }
+}
+
+struct mdia_t
+{
+ unsigned char* start_;
+ unsigned char* mdhd_;
+ struct minf_t minf_;
+// hdlr hdlr_;
+};
+
+void mdia_parse(struct mdia_t* mdia, unsigned char* buffer, unsigned int size)
+{
+ struct atom_t leaf_atom;
+ unsigned char* buffer_start = buffer;
+
+ mdia->start_ = buffer;
+
+ while(buffer < buffer_start + size)
+ {
+ buffer = atom_read_header(buffer, &leaf_atom);
+
+ atom_print(&leaf_atom);
+
+ 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);
+ }
+
+ buffer = atom_skip(&leaf_atom);
+ }
+}
+
+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
+ unsigned int pos_; // start byte position of chunk
+};
+
+struct samples_t
+{
+ unsigned int pts_; // decoding/presentation time
+ unsigned int size_; // size in bytes
+ unsigned int pos_; // byte offset
+ unsigned int cto_; // composition time offset
+};
+
+struct trak_t
+{
+ unsigned char* start_;
+ unsigned char* tkhd_;
+ struct mdia_t mdia_;
+
+ /* temporary indices */
+ unsigned int chunks_size_;
+ struct chunks_t* chunks_;
+
+ unsigned int samples_size_;
+ struct samples_t* samples_;
+};
+
+void trak_init(struct trak_t* trak)
+{
+ trak->chunks_ = 0;
+ trak->samples_ = 0;
+}
+
+void trak_exit(struct trak_t* trak)
+{
+ if(trak->chunks_)
+ free(trak->chunks_);
+ if(trak->samples_)
+ free(trak->samples_);
+}
+
+void trak_parse(struct trak_t* trak, unsigned char* buffer, unsigned int size)
+{
+ struct atom_t leaf_atom;
+ unsigned char* buffer_start = buffer;
+
+ trak->start_ = buffer;
+
+ while(buffer < buffer_start + size)
+ {
+ buffer = atom_read_header(buffer, &leaf_atom);
+
+ atom_print(&leaf_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);
+ }
+
+ buffer = atom_skip(&leaf_atom);
+ }
+}
+
+struct moov_t
+{
+ unsigned char* start_;
+ unsigned int tracks_;
+ unsigned char* mvhd_;
+ struct trak_t traks_[MAX_TRACKS];
+};
+
+void moov_init(struct moov_t* moov)
+{
+ moov->tracks_ = 0;
+}
+
+void moov_exit(struct moov_t* moov)
+{
+ unsigned int i;
+ for(i = 0; i != moov->tracks_; ++i)
+ {
+ trak_exit(&moov->traks_[i]);
+ }
+}
+
+void trak_build_index(struct trak_t* trak)
+{
+ void const* stco = trak->mdia_.minf_.stbl_.stco_;
+
+ trak->chunks_size_ = stco_get_entries(stco);
+ 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);
+ }
+ }
+
+ // process chunkmap:
+ {
+ void const* stsc = trak->mdia_.minf_.stbl_.stsc_;
+ unsigned int last = trak->chunks_size_;
+ unsigned int i = stsc_get_entries(stsc);
+ 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++)
+ {
+ trak->chunks_[j].id_ = stsc_table.id_;
+ trak->chunks_[j].size_ = stsc_table.samples_;
+ }
+ last = stsc_table.chunk_;
+ }
+ }
+
+ // calc pts of chunks:
+ {
+ void const* stsz = trak->mdia_.minf_.stbl_.stsz_;
+ unsigned int sample_size = stsz_get_sample_size(stsz);
+ unsigned int s = 0;
+ {
+ unsigned int j;
+ for(j = 0; j < trak->chunks_size_; j++)
+ {
+ trak->chunks_[j].sample_ = s;
+ s += trak->chunks_[j].size_;
+ }
+ }
+
+ if(sample_size == 0)
+ {
+ trak->samples_size_ = stsz_get_entries(stsz);
+ }
+ else
+ {
+ trak->samples_size_ = s;
+ }
+
+ trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t));
+
+ if(sample_size == 0)
+ {
+ unsigned int i;
+ for(i = 0; i != trak->samples_size_ ; ++i)
+ trak->samples_[i].size_ = stsz_get_size(stsz, i);
+ }
+ else
+ {
+ unsigned int i;
+ for(i = 0; i != trak->samples_size_ ; ++i)
+ trak->samples_[i].size_ = sample_size;
+ }
+ }
+
+// i = 0;
+// for (j = 0; j < trak->durmap_size; j++)
+// i += trak->durmap[j].num;
+// if (i != s) {
+// mp_msg(MSGT_DEMUX, MSGL_WARN,
+// "MOV: durmap and chunkmap sample count differ (%i vs %i)\n", i, s);
+// if (i > s) s = i;
+// }
+
+ // calc pts:
+ {
+ void const* stts = trak->mdia_.minf_.stbl_.stts_;
+ unsigned int s = 0;
+ unsigned int entries = stts_get_entries(stts);
+ 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);
+ for(i = 0; i < sample_count; i++)
+ {
+ trak->samples_[s].pts_ = pts;
+ ++s;
+ pts += sample_duration;
+ }
+ }
+ }
+
+ // calc composition times:
+ {
+ void const* ctts = trak->mdia_.minf_.stbl_.ctts_;
+ if(ctts)
+ {
+ unsigned int s = 0;
+ unsigned int entries = ctts_get_entries(ctts);
+ unsigned int 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);
+ for(i = 0; i < sample_count; i++)
+ {
+ trak->samples_[s].cto_ = sample_offset;
+ ++s;
+ }
+ }
+ }
+ }
+
+ // calc sample offsets
+ {
+ unsigned int s = 0;
+ unsigned int j;
+ for(j = 0; j < trak->chunks_size_; j++)
+ {
+ unsigned int pos = trak->chunks_[j].pos_;
+ unsigned int i;
+ for(i = 0; i < trak->chunks_[j].size_; i++)
+ {
+ trak->samples_[s].pos_ = pos;
+ pos += trak->samples_[s].size_;
+ ++s;
+ }
+ }
+ }
+}
+
+void trak_write_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_;
+ 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;
+ unsigned int sample_duration =
+ trak->samples_[s + 1].pts_ - trak->samples_[s].pts_;
+ while(s != end - 1)
+ {
+ if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration)
+ break;
+ ++sample_count;
+ ++s;
+ }
+ // write entry
+ write_int32(&table[entries].sample_count_, sample_count);
+ write_int32(&table[entries].sample_duration_, sample_duration);
+ ++entries;
+ }
+ write_int32(stts + 4, entries);
+ if(stts_get_samples(stts) != end - start)
+ {
+ printf("ERROR: stts_get_samples=%d, should be %d\n",
+ stts_get_samples(stts), end - start);
+ }
+ }
+
+ // ctts = [entries * [sample_count, sample_offset]
+ {
+ unsigned char* 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;
+ unsigned int sample_offset = trak->samples_[s].cto_;
+ while(s != end - 1)
+ {
+ if(trak->samples_[s + 1].cto_ != sample_offset)
+ break;
+ ++sample_count;
+ ++s;
+ }
+ // write entry
+ write_int32(&table[entries].sample_count_, sample_count);
+ write_int32(&table[entries].sample_offset_, sample_offset);
+ ++entries;
+ }
+ write_int32(ctts + 4, entries);
+ if(ctts_get_samples(ctts) != end - start)
+ {
+ printf("ERROR: ctts_get_samples=%d, should be %d\n",
+ ctts_get_samples(ctts), end - start);
+ }
+ }
+ }
+
+ // process chunkmap:
+ {
+ unsigned char* stsc = trak->mdia_.minf_.stbl_.stsc_;
+ struct stsc_table_t* stsc_table = (struct stsc_table_t*)(stsc + 8);
+ unsigned int i;
+ for(i = 0; i != trak->chunks_size_; ++i)
+ {
+ if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start)
+ break;
+ }
+
+ {
+ 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_)
+ {
+ for(i += 1; i != trak->chunks_size_; ++i)
+ {
+ if(trak->chunks_[i].size_ != samples)
+ {
+ 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;
+ }
+ }
+ }
+ write_int32(stsc + 4, 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);
+
+ // patch first chunk with correct sample offset
+// uint32_t* stco_table = (uint32_t*)(stco + 8);
+ write_int32(stco_table, trak->samples_[start].pos_);
+ }
+ }
+ }
+
+ // process sync samples:
+ 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);
+ unsigned int stss_start;
+ unsigned int i;
+ for(i = 0; i != entries; ++i)
+ {
+ if(read_int32(&table[i]) >= start + 1)
+ break;
+ }
+ stss_start = i;
+ for(; i != entries; ++i)
+ {
+ unsigned int sync_sample = read_int32(&table[i]);
+ if(sync_sample >= end + 1)
+ break;
+ write_int32(&table[i - stss_start], sync_sample - start);
+
+ }
+// memmove(table, table + stss_start, (i - stss_start) * sizeof(uint32_t));
+ write_int32(stss + 4, i - stss_start);
+ }
+
+ // process sample sizes
+ {
+ 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);
+ }
+ }
+}
+
+int moov_parse(struct moov_t* moov, unsigned char* buffer, unsigned int size)
+{
+ struct atom_t leaf_atom;
+ unsigned char* buffer_start = buffer;
+
+ moov->start_ = buffer;
+
+ while(buffer < buffer_start + size)
+ {
+ buffer = atom_read_header(buffer, &leaf_atom);
+
+ atom_print(&leaf_atom);
+
+ 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);
+ }
+
+ // build the indexing tables
+ {
+ unsigned int i;
+ for(i = 0; i != moov->tracks_; ++i)
+ {
+ trak_build_index(&moov->traks_[i]);
+ }
+ }
+
+ return 1;
+}
+
+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));
+}
+
+void trak_shift_offsets(struct trak_t* trak, int offset)
+{
+ unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
+ stco_shift_offsets(stco, offset);
+}
+
+void moov_shift_offsets(struct moov_t* moov, int offset)
+{
+ unsigned int i;
+ for(i = 0; i != moov->tracks_; ++i)
+ {
+ trak_shift_offsets(&moov->traks_[i], offset);
+ }
+}
+
+long mvhd_get_time_scale(unsigned char* mvhd)
+{
+ int version = read_char(mvhd);
+ unsigned char* p = mvhd + (version == 0 ? 12 : 20);
+ return read_int32(p);
+}
+
+void mvhd_set_duration(unsigned char* mvhd, long duration)
+{
+ int version = read_char(mvhd);
+ if(version == 0)
+ {
+ write_int32(mvhd + 16, duration);
+ }
+ else
+ {
+ perror("mvhd_set_duration");
+// write_int64(mvhd + 24, duration);
+ }
+}
+
+long mdhd_get_time_scale(unsigned char* mdhd)
+{
+ return read_int32(mdhd + 12);
+}
+
+void mdhd_set_duration(unsigned char* mdhd, unsigned int duration)
+{
+ write_int32(mdhd + 16, duration);
+}
+
+void tkhd_set_duration(unsigned char* tkhd, unsigned int duration)
+{
+ int version = read_char(tkhd);
+ if(version == 0)
+ {
+ write_int32(tkhd + 20, duration);
+ }
+ else
+ {
+ perror("tkhd_set_duration");
+// write_int64(tkhd + 28, duration);
+ }
+}
+
+unsigned int stss_get_entries(unsigned char const* stss)
+{
+ return read_int32(stss + 4);
+}
+
+unsigned int stss_get_sample(unsigned char const* stss, unsigned int idx)
+{
+ unsigned char const* p = stss + 8 + idx * 4;
+ return read_int32(p);
+}
+
+unsigned int stss_get_nearest_keyframe(unsigned char const* stss, unsigned int sample)
+{
+ // 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)
+ {
+ table_sample = stss_get_sample(stss, i);
+ if(table_sample >= sample)
+ break;
+ }
+ 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)
+{
+ // 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 moov_seek(unsigned char* moov_data, unsigned int size,
+ float start_time,
+ unsigned int* mdat_start,
+ unsigned int* mdat_size,
+ unsigned int offset)
+{
+ struct moov_t* moov = malloc(sizeof(struct moov_t));
+ moov_init(moov);
+ if(!moov_parse(moov, moov_data, size))
+ {
+ moov_exit(moov);
+ free(moov);
+ return 0;
+ }
+
+ {
+ long moov_time_scale = mvhd_get_time_scale(moov->mvhd_);
+ 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 i;
+
+ // 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 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)
+ {
+ 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;
+ }
+
+ for(i = 0; i != moov->tracks_; ++i)
+ {
+ 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);
+ }
+
+ 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_;
+
+ unsigned int start_sample = trak_sample_start[i];
+ unsigned int end_sample = trak->samples_size_;
+
+ trak_write_index(trak, start_sample, end_sample);
+
+ {
+ unsigned 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);
+ }
+
+ {
+ // fixup trak (duration)
+ unsigned int trak_duration = stts_get_duration(stbl->stts_);
+ long trak_time_scale = mdhd_get_time_scale(trak->mdia_.mdhd_);
+ 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);
+
+ 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_));
+ }
+ mvhd_set_duration(moov->mvhd_, moov_duration);
+
+ offset -= bytes_to_skip;
+
+ printf("shifting offsets by %d\n", offset);
+ moov_shift_offsets(moov, offset);
+
+ *mdat_start += bytes_to_skip;
+ *mdat_size -= bytes_to_skip;
+ }
+
+ moov_exit(moov);
+ free(moov);
+
+ return 1;
+}
+
+// End Of File
+