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 +