1 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
2 --- lighttpd-1.4.18/src/Makefile.am 2007-09-03 01:23:53.000000000 +0300
3 +++ lighttpd-mod_h264_streaming-1.4.18/src/Makefile.am 2007-10-23 23:42:37.736979478 +0300
5 mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
6 mod_flv_streaming_la_LIBADD = $(common_libadd)
8 +lib_LTLIBRARIES += mod_h264_streaming.la
9 +mod_h264_streaming_la_SOURCES = mod_h264_streaming.c moov.c
10 +mod_h264_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
11 +mod_h264_streaming_la_LIBADD = $(common_libadd)
13 lib_LTLIBRARIES += mod_evasive.la
14 mod_evasive_la_SOURCES = mod_evasive.c
15 mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
16 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
17 --- lighttpd-1.4.18/src/mod_h264_streaming.c 1970-01-01 03:00:00.000000000 +0300
18 +++ lighttpd-mod_h264_streaming-1.4.18/src/mod_h264_streaming.c 2007-10-23 23:42:37.696978564 +0300
20 +/*******************************************************************************
21 + mod_h264_streaming.c
23 + mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files.
24 + http://h264.code-shop.com
26 + Copyright (C) 2007 CodeShop B.V.
28 + This program is free software: you can redistribute it and/or modify
29 + it under the terms of the GNU General Public License as published by
30 + the Free Software Foundation, either version 3 of the License, or
31 + (at your option) any later version.
33 + This program is distributed in the hope that it will be useful,
34 + but WITHOUT ANY WARRANTY; without even the implied warranty of
35 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 + GNU General Public License for more details.
38 + You should have received a copy of the GNU General Public License
39 + along with this program. If not, see <http://www.gnu.org/licenses/>.
40 +******************************************************************************/
50 +#include "response.h"
51 +#include "http_chunk.h"
52 +#include "stat_cache.h"
60 +/* plugin config for all request/connections */
72 + plugin_config **config_storage;
77 +/* init the plugin data */
78 +INIT_FUNC(mod_h264_streaming_init) {
81 + p = calloc(1, sizeof(*p));
83 + p->query_str = buffer_init();
84 + p->get_params = array_init();
89 +/* detroy the plugin data */
90 +FREE_FUNC(mod_h264_streaming_free) {
91 + plugin_data *p = p_d;
95 + if (!p) return HANDLER_GO_ON;
97 + if (p->config_storage) {
100 + for (i = 0; i < srv->config_context->used; i++) {
101 + plugin_config *s = p->config_storage[i];
105 + array_free(s->extensions);
109 + free(p->config_storage);
112 + buffer_free(p->query_str);
113 + array_free(p->get_params);
117 + return HANDLER_GO_ON;
120 +/* handle plugin config and check values */
122 +SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) {
123 + plugin_data *p = p_d;
126 + config_values_t cv[] = {
127 + { "h264-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
128 + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
131 + if (!p) return HANDLER_ERROR;
133 + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
135 + for (i = 0; i < srv->config_context->used; i++) {
138 + s = calloc(1, sizeof(plugin_config));
139 + s->extensions = array_init();
141 + cv[0].destination = s->extensions;
143 + p->config_storage[i] = s;
145 + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
146 + return HANDLER_ERROR;
150 + return HANDLER_GO_ON;
155 +static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
157 + plugin_config *s = p->config_storage[0];
161 + /* skip the first, the global context */
162 + for (i = 1; i < srv->config_context->used; i++) {
163 + data_config *dc = (data_config *)srv->config_context->data[i];
164 + s = p->config_storage[i];
166 + /* condition didn't match */
167 + if (!config_check_cond(srv, con, dc)) continue;
170 + for (j = 0; j < dc->value->used; j++) {
171 + data_unset *du = dc->value->data[j];
173 + if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
183 +static int split_get_params(array *get_params, buffer *qrystr) {
186 + char *key = NULL, *val = NULL;
190 + /* we need the \0 */
191 + for (i = 0; i < qrystr->used; i++) {
192 + switch(qrystr->ptr[i]) {
195 + val = qrystr->ptr + i + 1;
197 + qrystr->ptr[i] = '\0';
204 + case '\0': /* fin symbol */
207 + /* we need at least a = since the last & */
209 + /* terminate the value */
210 + qrystr->ptr[i] = '\0';
212 + if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
213 + ds = data_string_init();
215 + buffer_copy_string_len(ds->key, key, strlen(key));
216 + buffer_copy_string_len(ds->value, val, strlen(val));
218 + array_insert_unique(get_params, (data_unset *)ds);
221 + key = qrystr->ptr + i + 1;
231 +extern unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
233 + unsigned int* mdat_start, unsigned int* mdat_size,
234 + unsigned int offset);
236 +void write_char(unsigned char* outbuffer, int value)
238 + outbuffer[0] = (unsigned char)(value);
241 +void write_int32(unsigned char* outbuffer, long value)
243 + outbuffer[0] = (unsigned char)((value >> 24) & 0xff);
244 + outbuffer[1] = (unsigned char)((value >> 16) & 0xff);
245 + outbuffer[2] = (unsigned char)((value >> 8) & 0xff);
246 + outbuffer[3] = (unsigned char)((value >> 0) & 0xff);
251 + unsigned char type_[4];
252 + unsigned int size_;
253 + unsigned int start_;
257 +#define ATOM_PREAMBLE_SIZE 8
259 +unsigned int atom_header_size(unsigned char* atom_bytes)
261 + return (atom_bytes[0] << 24) +
262 + (atom_bytes[1] << 16) +
263 + (atom_bytes[2] << 8) +
267 +int atom_read_header(FILE* infile, struct atom_t* atom)
269 + unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
271 + atom->start_ = ftell(infile);
273 + fread(atom_bytes, ATOM_PREAMBLE_SIZE, 1, infile);
274 + memcpy(&atom->type_[0], &atom_bytes[4], 4);
275 + atom->size_ = atom_header_size(atom_bytes);
276 + atom->end_ = atom->start_ + atom->size_;
281 +void atom_write_header(unsigned char* outbuffer, struct atom_t* atom)
284 + write_int32(outbuffer, atom->size_);
285 + for(i = 0; i != 4; ++i)
286 + write_char(outbuffer + 4 + i, atom->type_[i]);
289 +int atom_is(struct atom_t const* atom, const char* type)
291 + return (atom->type_[0] == type[0] &&
292 + atom->type_[1] == type[1] &&
293 + atom->type_[2] == type[2] &&
294 + atom->type_[3] == type[3])
298 +void atom_skip(FILE* infile, struct atom_t const* atom)
300 + fseek(infile, atom->end_, SEEK_SET);
303 +void atom_print(struct atom_t const* atom)
305 + printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
306 + atom->type_[2], atom->type_[3], atom->size_);
310 +URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
311 + plugin_data *p = p_d;
317 + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
319 + mod_h264_streaming_patch_connection(srv, con, p);
321 + s_len = con->physical.path->used - 1;
323 + for (k = 0; k < p->conf.extensions->used; k++) {
324 + data_string *ds = (data_string *)p->conf.extensions->data[k];
325 + int ct_len = ds->value->used - 1;
327 + if (ct_len > s_len) continue;
328 + if (ds->value->used == 0) continue;
330 + if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
331 + data_string *get_param;
332 + stat_cache_entry *sce = NULL;
336 + /* if there is a start=[0-9]+ in the header use it as start,
337 + * otherwise send the full file */
339 + array_reset(p->get_params);
340 + buffer_copy_string_buffer(p->query_str, con->uri.query);
341 + split_get_params(p->get_params, p->query_str);
343 + if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
344 + return HANDLER_GO_ON;
348 +// if (get_param->value->used < 2) return HANDLER_GO_ON;
350 + /* check if it is a number */
351 +// start = strtol(get_param->value->ptr, &err, 10);
352 + start = strtod(get_param->value->ptr, &err);
353 + if (*err != '\0') {
354 + return HANDLER_GO_ON;
357 +// if (start <= 0) return HANDLER_GO_ON;
358 + if (start < 0) return HANDLER_GO_ON;
360 + /* check if start is > filesize */
361 + if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
362 + return HANDLER_GO_ON;
365 +// if (start > sce->st.st_size) {
366 +// return HANDLER_GO_ON;
369 + /* we are safe now, let's build a h264 header */
370 + b = chunkqueue_get_append_buffer(con->write_queue);
372 + BUFFER_COPY_STRING_CONST(b, "FLV\x1\x1\0\0\0\x9\0\0\0\x9");
373 + http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);
377 + struct atom_t ftyp_atom;
378 + struct atom_t moov_atom;
379 + struct atom_t mdat_atom;
380 + unsigned char* moov_data = 0;
381 + unsigned char* ftyp_data = 0;
383 + infile = fopen(con->physical.path->ptr, "rb");
385 + return HANDLER_GO_ON;
389 + unsigned int filesize = sce->st.st_size;
391 + struct atom_t leaf_atom;
392 + while(ftell(infile) < filesize)
394 + if(!atom_read_header(infile, &leaf_atom))
397 + atom_print(&leaf_atom);
399 + if(atom_is(&leaf_atom, "ftyp"))
401 + ftyp_atom = leaf_atom;
402 + ftyp_data = malloc(ftyp_atom.size_);
403 + fseek(infile, ftyp_atom.start_, SEEK_SET);
404 + fread(ftyp_data, ftyp_atom.size_, 1, infile);
407 + if(atom_is(&leaf_atom, "moov"))
409 + moov_atom = leaf_atom;
410 + moov_data = malloc(moov_atom.size_);
411 + fseek(infile, moov_atom.start_, SEEK_SET);
412 + fread(moov_data, moov_atom.size_, 1, infile);
415 + if(atom_is(&leaf_atom, "mdat"))
417 + mdat_atom = leaf_atom;
419 + atom_skip(infile, &leaf_atom);
425 + return HANDLER_GO_ON;
429 + unsigned int mdat_start = (ftyp_data ? ftyp_atom.size_ : 0) + moov_atom.size_;
430 + if(!moov_seek(moov_data + ATOM_PREAMBLE_SIZE,
431 + moov_atom.size_ - ATOM_PREAMBLE_SIZE,
433 + &mdat_atom.start_, &mdat_atom.size_,
434 + mdat_start - mdat_atom.start_))
435 + return HANDLER_GO_ON;
439 + buffer_append_memory(b, ftyp_data, ftyp_atom.size_);
443 + buffer_append_memory(b, moov_data, moov_atom.size_);
447 + unsigned char mdat_bytes[ATOM_PREAMBLE_SIZE];
448 +// mdat_atom.size_ -= bytes_to_skip;
449 + atom_write_header(mdat_bytes, &mdat_atom);
450 + buffer_append_memory(b, mdat_bytes, ATOM_PREAMBLE_SIZE);
451 + b->used++; /* add virtual \0 */
454 + http_chunk_append_file(srv, con, con->physical.path, mdat_atom.start_ + ATOM_PREAMBLE_SIZE,
455 + mdat_atom.size_ - ATOM_PREAMBLE_SIZE);
459 + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4"));
461 + con->file_finished = 1;
463 + return HANDLER_FINISHED;
468 + return HANDLER_GO_ON;
471 +/* this function is called at dlopen() time and inits the callbacks */
473 +int mod_h264_streaming_plugin_init(plugin *p) {
474 + p->version = LIGHTTPD_VERSION_ID;
475 + p->name = buffer_init_string("h264_streaming");
477 + p->init = mod_h264_streaming_init;
478 + p->handle_physical = mod_h264_streaming_path_handler;
479 + p->set_defaults = mod_h264_streaming_set_defaults;
480 + p->cleanup = mod_h264_streaming_free;
487 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
488 --- lighttpd-1.4.18/src/moov.c 1970-01-01 03:00:00.000000000 +0300
489 +++ lighttpd-mod_h264_streaming-1.4.18/src/moov.c 2007-10-23 23:42:37.776980392 +0300
491 +/******************************************************************************
494 + moov - A library for splitting Quicktime/MPEG4 files.
495 + http://h264.code-shop.com
497 + Copyright (C) 2007 CodeShop B.V.
499 + This program is free software: you can redistribute it and/or modify
500 + it under the terms of the GNU General Public License as published by
501 + the Free Software Foundation, either version 3 of the License, or
502 + (at your option) any later version.
504 + This program is distributed in the hope that it will be useful,
505 + but WITHOUT ANY WARRANTY; without even the implied warranty of
506 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
507 + GNU General Public License for more details.
509 + You should have received a copy of the GNU General Public License
510 + along with this program. If not, see <http://www.gnu.org/licenses/>.
511 +******************************************************************************/
514 + Uses code snippets from the libquicktime library:
515 + http://libquicktime.sourceforge.net
517 + The QuickTime File Format PDF from Apple:
518 + http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
525 +#include <inttypes.h>
527 +static int read_char(unsigned char const* buffer)
532 +static int read_int32(void const* buffer)
534 + unsigned char* p = (unsigned char*)buffer;
535 + return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
538 +static void write_int32(void* outbuffer, uint32_t value)
540 + unsigned char* p = (unsigned char*)outbuffer;
541 + p[0] = (unsigned char)((value >> 24) & 0xff);
542 + p[1] = (unsigned char)((value >> 16) & 0xff);
543 + p[2] = (unsigned char)((value >> 8) & 0xff);
544 + p[3] = (unsigned char)((value >> 0) & 0xff);
549 + unsigned char type_[4];
550 + unsigned int size_;
551 + unsigned char* start_;
552 + unsigned char* end_;
555 +#define ATOM_PREAMBLE_SIZE 8
557 +static unsigned int atom_header_size(unsigned char* atom_bytes)
559 + return (atom_bytes[0] << 24) +
560 + (atom_bytes[1] << 16) +
561 + (atom_bytes[2] << 8) +
565 +static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
567 + atom->start_ = buffer;
568 + memcpy(&atom->type_[0], &buffer[4], 4);
569 + atom->size_ = atom_header_size(buffer);
570 + atom->end_ = atom->start_ + atom->size_;
572 + return buffer + ATOM_PREAMBLE_SIZE;
575 +static unsigned char* atom_skip(struct atom_t const* atom)
580 +static int atom_is(struct atom_t const* atom, const char* type)
582 + return (atom->type_[0] == type[0] &&
583 + atom->type_[1] == type[1] &&
584 + atom->type_[2] == type[2] &&
585 + atom->type_[3] == type[3])
589 +static void atom_print(struct atom_t const* atom)
591 + printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
592 + atom->type_[2], atom->type_[3], atom->size_);
595 +#define MAX_TRACKS 8
597 +unsigned int stts_get_entries(unsigned char const* stts)
599 + return read_int32(stts + 4);
602 +void stts_get_sample_count_and_duration(unsigned char const* stts,
603 + unsigned int idx, unsigned int* sample_count, unsigned int* sample_duration)
605 + unsigned char const* table = stts + 8 + idx * 8;
606 + *sample_count = read_int32(table);
607 + *sample_duration = read_int32(table + 4);
612 + uint32_t sample_count_;
613 + uint32_t sample_duration_;
616 +unsigned int ctts_get_entries(unsigned char const* ctts)
618 + return read_int32(ctts + 4);
621 +void ctts_get_sample_count_and_offset(unsigned char const* ctts,
622 + unsigned int idx, unsigned int* sample_count, unsigned int* sample_offset)
624 + unsigned char const* table = ctts + 8 + idx * 8;
625 + *sample_count = read_int32(table);
626 + *sample_offset = read_int32(table + 4);
629 +unsigned int ctts_get_samples(unsigned char const* ctts)
632 + long entries = ctts_get_entries(ctts);
634 + for(i = 0; i != entries; ++i)
636 + unsigned int sample_count;
637 + unsigned int sample_offset;
638 + ctts_get_sample_count_and_offset(ctts, i, &sample_count, &sample_offset);
639 + samples += sample_count;
647 + uint32_t sample_count_;
648 + uint32_t sample_offset_;
658 +unsigned int stsc_get_entries(unsigned char const* stsc)
660 + return read_int32(stsc + 4);
663 +void stsc_get_table(unsigned char const* stsc, unsigned int i, struct stsc_table_t *stsc_table)
665 + struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
666 + stsc_table->chunk_ = read_int32(&table[i].chunk_) - 1;
667 + stsc_table->samples_ = read_int32(&table[i].samples_);
668 + stsc_table->id_ = read_int32(&table[i].id_);
671 +unsigned int stsc_get_chunk(unsigned char* stsc, unsigned int sample)
673 + unsigned int entries = read_int32(stsc + 4);
674 + struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
683 +// unsigned int table_samples = read_int32(&table[0].samples_);
684 +// unsigned int chunk = (sample + 1) / table_samples;
685 +// return chunk - 1;
689 + unsigned int total = 0;
690 + unsigned int chunk1 = 1;
691 + unsigned int chunk1samples = 0;
692 + unsigned int chunk2entry = 0;
693 + unsigned int chunk, chunk_sample;
697 + unsigned int range_samples;
698 + unsigned int chunk2 = read_int32(&table[chunk2entry].chunk_);
699 + chunk = chunk2 - chunk1;
700 + range_samples = chunk * chunk1samples;
702 + if(sample < total + range_samples)
705 + chunk1samples = read_int32(&table[chunk2entry].samples_);
708 + if(chunk2entry < entries)
711 + total += range_samples;
713 + } while(chunk2entry < entries);
717 + unsigned int sample_in_chunk = (sample - total) % chunk1samples;
718 + if(sample_in_chunk != 0)
720 + printf("ERROR: sample must be chunk aligned: %d\n", sample_in_chunk);
722 + chunk = (sample - total) / chunk1samples + chunk1;
727 + chunk_sample = total + (chunk - chunk1) * chunk1samples;
733 +unsigned int stsc_get_samples(unsigned char* stsc)
735 + unsigned int entries = read_int32(stsc + 4);
736 + struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
737 + unsigned int samples = 0;
739 + for(i = 0; i != entries; ++i)
741 + samples += read_int32(&table[i].samples_);
746 +unsigned int stco_get_entries(unsigned char const* stco)
748 + return read_int32(stco + 4);
751 +unsigned int stco_get_offset(unsigned char const* stco, int idx)
753 + uint32_t const* table = (uint32_t const*)(stco + 8);
754 + return read_int32(&table[idx]);
757 +unsigned int stsz_get_sample_size(unsigned char const* stsz)
759 + return read_int32(stsz + 4);
762 +unsigned int stsz_get_entries(unsigned char const* stsz)
764 + return read_int32(stsz + 8);
767 +unsigned int stsz_get_size(unsigned char const* stsz, unsigned int idx)
769 + uint32_t const* table = (uint32_t const*)(stsz + 12);
770 + return read_int32(&table[idx]);
773 +unsigned int stts_get_duration(unsigned char const* stts)
776 + long entries = stts_get_entries(stts);
778 + for(i = 0; i != entries; ++i)
780 + unsigned int sample_count;
781 + unsigned int sample_duration;
782 + stts_get_sample_count_and_duration(stts, i,
783 + &sample_count, &sample_duration);
784 + duration += sample_duration * sample_count;
790 +unsigned int stts_get_samples(unsigned char const* stts)
793 + long entries = stts_get_entries(stts);
795 + for(i = 0; i != entries; ++i)
797 + unsigned int sample_count;
798 + unsigned int sample_duration;
799 + stts_get_sample_count_and_duration(stts, i,
800 + &sample_count, &sample_duration);
801 + samples += sample_count;
807 +unsigned int stts_get_sample(unsigned char const* stts, unsigned int time)
809 + unsigned int stts_index = 0;
810 + unsigned int stts_count;
812 + unsigned int ret = 0;
813 + unsigned int time_count = 0;
815 + unsigned int entries = stts_get_entries(stts);
816 + for(; stts_index != entries; ++stts_index)
818 + unsigned int sample_count;
819 + unsigned int sample_duration;
820 + stts_get_sample_count_and_duration(stts, stts_index,
821 + &sample_count, &sample_duration);
822 + if(time_count + sample_duration * sample_count >= time)
824 + stts_count = (time - time_count) / sample_duration;
825 + time_count += stts_count * sample_duration;
831 + time_count += sample_duration * sample_count;
832 + ret += sample_count;
835 +// if(stts_index >= table_.size())
838 +// *time = time_count;
842 +unsigned int stts_get_time(unsigned char const* stts, unsigned int sample)
844 + unsigned int ret = 0;
845 + unsigned int stts_index = 0;
846 + unsigned int sample_count = 0;
850 + unsigned int table_sample_count;
851 + unsigned int table_sample_duration;
852 + stts_get_sample_count_and_duration(stts, stts_index,
853 + &table_sample_count, &table_sample_duration);
855 + if(sample_count + table_sample_count > sample)
857 + unsigned int stts_count = (sample - sample_count);
858 + ret += stts_count * table_sample_duration;
863 + sample_count += table_sample_count;
864 + ret += table_sample_count * table_sample_duration;
874 + unsigned char* start_;
875 +//stsd stsd_; // sample description
876 + unsigned char* stts_; // decoding time-to-sample
877 + unsigned char* stss_; // sync sample
878 + unsigned char* stsc_; // sample-to-chunk
879 + unsigned char* stsz_; // sample size
880 + unsigned char* stco_; // chunk offset
881 + unsigned char* ctts_; // composition time-to-sample
884 +void stbl_parse(struct stbl_t* stbl, unsigned char* buffer, unsigned int size)
886 + struct atom_t leaf_atom;
887 + unsigned char* buffer_start = buffer;
891 + stbl->start_ = buffer;
893 + while(buffer < buffer_start + size)
895 + buffer = atom_read_header(buffer, &leaf_atom);
897 + atom_print(&leaf_atom);
899 + if(atom_is(&leaf_atom, "stts"))
901 + stbl->stts_ = buffer;
904 + if(atom_is(&leaf_atom, "stss"))
906 + stbl->stss_ = buffer;
909 + if(atom_is(&leaf_atom, "stsc"))
911 + stbl->stsc_ = buffer;
914 + if(atom_is(&leaf_atom, "stsz"))
916 + stbl->stsz_ = buffer;
919 + if(atom_is(&leaf_atom, "stco"))
921 + stbl->stco_ = buffer;
924 + if(atom_is(&leaf_atom, "co64"))
926 + perror("TODO: co64");
929 + if(atom_is(&leaf_atom, "ctts"))
931 + stbl->ctts_ = buffer;
934 + buffer = atom_skip(&leaf_atom);
940 + unsigned char* start_;
941 + struct stbl_t stbl_;
944 +void minf_parse(struct minf_t* minf, unsigned char* buffer, unsigned int size)
946 + struct atom_t leaf_atom;
947 + unsigned char* buffer_start = buffer;
949 + minf->start_ = buffer;
951 + while(buffer < buffer_start + size)
953 + buffer = atom_read_header(buffer, &leaf_atom);
955 + atom_print(&leaf_atom);
957 + if(atom_is(&leaf_atom, "stbl"))
959 + stbl_parse(&minf->stbl_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
962 + buffer = atom_skip(&leaf_atom);
968 + unsigned char* start_;
969 + unsigned char* mdhd_;
970 + struct minf_t minf_;
974 +void mdia_parse(struct mdia_t* mdia, unsigned char* buffer, unsigned int size)
976 + struct atom_t leaf_atom;
977 + unsigned char* buffer_start = buffer;
979 + mdia->start_ = buffer;
981 + while(buffer < buffer_start + size)
983 + buffer = atom_read_header(buffer, &leaf_atom);
985 + atom_print(&leaf_atom);
987 + if(atom_is(&leaf_atom, "mdhd"))
989 + mdia->mdhd_ = buffer;
992 + if(atom_is(&leaf_atom, "minf"))
994 + minf_parse(&mdia->minf_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
997 + buffer = atom_skip(&leaf_atom);
1003 + unsigned int sample_; // number of the first sample in the chunk
1004 + unsigned int size_; // number of samples in the chunk
1005 + int id_; // for multiple codecs mode - not used
1006 + unsigned int pos_; // start byte position of chunk
1011 + unsigned int pts_; // decoding/presentation time
1012 + unsigned int size_; // size in bytes
1013 + unsigned int pos_; // byte offset
1014 + unsigned int cto_; // composition time offset
1019 + unsigned char* start_;
1020 + unsigned char* tkhd_;
1021 + struct mdia_t mdia_;
1023 + /* temporary indices */
1024 + unsigned int chunks_size_;
1025 + struct chunks_t* chunks_;
1027 + unsigned int samples_size_;
1028 + struct samples_t* samples_;
1031 +void trak_init(struct trak_t* trak)
1033 + trak->chunks_ = 0;
1034 + trak->samples_ = 0;
1037 +void trak_exit(struct trak_t* trak)
1040 + free(trak->chunks_);
1041 + if(trak->samples_)
1042 + free(trak->samples_);
1045 +void trak_parse(struct trak_t* trak, unsigned char* buffer, unsigned int size)
1047 + struct atom_t leaf_atom;
1048 + unsigned char* buffer_start = buffer;
1050 + trak->start_ = buffer;
1052 + while(buffer < buffer_start + size)
1054 + buffer = atom_read_header(buffer, &leaf_atom);
1056 + atom_print(&leaf_atom);
1058 + if(atom_is(&leaf_atom, "tkhd"))
1060 + trak->tkhd_ = buffer;
1063 + if(atom_is(&leaf_atom, "mdia"))
1065 + mdia_parse(&trak->mdia_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
1068 + buffer = atom_skip(&leaf_atom);
1074 + unsigned char* start_;
1075 + unsigned int tracks_;
1076 + unsigned char* mvhd_;
1077 + struct trak_t traks_[MAX_TRACKS];
1080 +void moov_init(struct moov_t* moov)
1082 + moov->tracks_ = 0;
1085 +void moov_exit(struct moov_t* moov)
1088 + for(i = 0; i != moov->tracks_; ++i)
1090 + trak_exit(&moov->traks_[i]);
1094 +void trak_build_index(struct trak_t* trak)
1096 + void const* stco = trak->mdia_.minf_.stbl_.stco_;
1098 + trak->chunks_size_ = stco_get_entries(stco);
1099 + trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t));
1103 + for(i = 0; i != trak->chunks_size_; ++i)
1105 + trak->chunks_[i].pos_ = stco_get_offset(stco, i);
1109 + // process chunkmap:
1111 + void const* stsc = trak->mdia_.minf_.stbl_.stsc_;
1112 + unsigned int last = trak->chunks_size_;
1113 + unsigned int i = stsc_get_entries(stsc);
1116 + struct stsc_table_t stsc_table;
1121 + stsc_get_table(stsc, i, &stsc_table);
1122 + for(j = stsc_table.chunk_; j < last; j++)
1124 + trak->chunks_[j].id_ = stsc_table.id_;
1125 + trak->chunks_[j].size_ = stsc_table.samples_;
1127 + last = stsc_table.chunk_;
1131 + // calc pts of chunks:
1133 + void const* stsz = trak->mdia_.minf_.stbl_.stsz_;
1134 + unsigned int sample_size = stsz_get_sample_size(stsz);
1135 + unsigned int s = 0;
1138 + for(j = 0; j < trak->chunks_size_; j++)
1140 + trak->chunks_[j].sample_ = s;
1141 + s += trak->chunks_[j].size_;
1145 + if(sample_size == 0)
1147 + trak->samples_size_ = stsz_get_entries(stsz);
1151 + trak->samples_size_ = s;
1154 + trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t));
1156 + if(sample_size == 0)
1159 + for(i = 0; i != trak->samples_size_ ; ++i)
1160 + trak->samples_[i].size_ = stsz_get_size(stsz, i);
1165 + for(i = 0; i != trak->samples_size_ ; ++i)
1166 + trak->samples_[i].size_ = sample_size;
1171 +// for (j = 0; j < trak->durmap_size; j++)
1172 +// i += trak->durmap[j].num;
1174 +// mp_msg(MSGT_DEMUX, MSGL_WARN,
1175 +// "MOV: durmap and chunkmap sample count differ (%i vs %i)\n", i, s);
1176 +// if (i > s) s = i;
1181 + void const* stts = trak->mdia_.minf_.stbl_.stts_;
1182 + unsigned int s = 0;
1183 + unsigned int entries = stts_get_entries(stts);
1185 + for(j = 0; j < entries; j++)
1188 + unsigned int pts = 0;
1189 + unsigned int sample_count;
1190 + unsigned int sample_duration;
1191 + stts_get_sample_count_and_duration(stts, j,
1192 + &sample_count, &sample_duration);
1193 + for(i = 0; i < sample_count; i++)
1195 + trak->samples_[s].pts_ = pts;
1197 + pts += sample_duration;
1202 + // calc composition times:
1204 + void const* ctts = trak->mdia_.minf_.stbl_.ctts_;
1207 + unsigned int s = 0;
1208 + unsigned int entries = ctts_get_entries(ctts);
1210 + for(j = 0; j < entries; j++)
1213 + unsigned int sample_count;
1214 + unsigned int sample_offset;
1215 + ctts_get_sample_count_and_offset(ctts, j, &sample_count, &sample_offset);
1216 + for(i = 0; i < sample_count; i++)
1218 + trak->samples_[s].cto_ = sample_offset;
1225 + // calc sample offsets
1227 + unsigned int s = 0;
1229 + for(j = 0; j < trak->chunks_size_; j++)
1231 + unsigned int pos = trak->chunks_[j].pos_;
1233 + for(i = 0; i < trak->chunks_[j].size_; i++)
1235 + trak->samples_[s].pos_ = pos;
1236 + pos += trak->samples_[s].size_;
1243 +void trak_write_index(struct trak_t* trak, unsigned int start, unsigned int end)
1245 + // write samples [start,end>
1247 + // stts = [entries * [sample_count, sample_duration]
1249 + unsigned char* stts = trak->mdia_.minf_.stbl_.stts_;
1250 + unsigned int entries = 0;
1251 + struct stts_table_t* table = (struct stts_table_t*)(stts + 8);
1253 + for(s = start; s != end; ++s)
1255 + unsigned int sample_count = 1;
1256 + unsigned int sample_duration =
1257 + trak->samples_[s + 1].pts_ - trak->samples_[s].pts_;
1258 + while(s != end - 1)
1260 + if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration)
1266 + write_int32(&table[entries].sample_count_, sample_count);
1267 + write_int32(&table[entries].sample_duration_, sample_duration);
1270 + write_int32(stts + 4, entries);
1271 + if(stts_get_samples(stts) != end - start)
1273 + printf("ERROR: stts_get_samples=%d, should be %d\n",
1274 + stts_get_samples(stts), end - start);
1278 + // ctts = [entries * [sample_count, sample_offset]
1280 + unsigned char* ctts = trak->mdia_.minf_.stbl_.ctts_;
1283 + unsigned int entries = 0;
1284 + struct ctts_table_t* table = (struct ctts_table_t*)(ctts + 8);
1286 + for(s = start; s != end; ++s)
1288 + unsigned int sample_count = 1;
1289 + unsigned int sample_offset = trak->samples_[s].cto_;
1290 + while(s != end - 1)
1292 + if(trak->samples_[s + 1].cto_ != sample_offset)
1298 + write_int32(&table[entries].sample_count_, sample_count);
1299 + write_int32(&table[entries].sample_offset_, sample_offset);
1302 + write_int32(ctts + 4, entries);
1303 + if(ctts_get_samples(ctts) != end - start)
1305 + printf("ERROR: ctts_get_samples=%d, should be %d\n",
1306 + ctts_get_samples(ctts), end - start);
1311 + // process chunkmap:
1313 + unsigned char* stsc = trak->mdia_.minf_.stbl_.stsc_;
1314 + struct stsc_table_t* stsc_table = (struct stsc_table_t*)(stsc + 8);
1316 + for(i = 0; i != trak->chunks_size_; ++i)
1318 + if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start)
1323 + unsigned int stsc_entries = 0;
1324 + unsigned int chunk_start = i;
1325 + unsigned int samples =
1326 + trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start;
1327 + unsigned int id = trak->chunks_[i].id_;
1329 + // write entry [chunk,samples,id]
1330 + write_int32(&stsc_table[stsc_entries].chunk_, 1);
1331 + write_int32(&stsc_table[stsc_entries].samples_, samples);
1332 + write_int32(&stsc_table[stsc_entries].id_, id);
1334 + if(i != trak->chunks_size_)
1336 + for(i += 1; i != trak->chunks_size_; ++i)
1338 + if(trak->chunks_[i].size_ != samples)
1340 + samples = trak->chunks_[i].size_;
1341 + id = trak->chunks_[i].id_;
1342 + write_int32(&stsc_table[stsc_entries].chunk_, i - chunk_start + 1);
1343 + write_int32(&stsc_table[stsc_entries].samples_, samples);
1344 + write_int32(&stsc_table[stsc_entries].id_, id);
1349 + write_int32(stsc + 4, stsc_entries);
1351 + unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
1352 +// stco_erase(stco, chunk_start);
1353 + unsigned int entries = read_int32(stco + 4);
1354 + uint32_t* stco_table = (uint32_t*)(stco + 8);
1355 + memmove(stco_table, &stco_table[chunk_start],
1356 + (entries - chunk_start) * sizeof(uint32_t));
1357 + write_int32(stco + 4, entries - chunk_start);
1359 + // patch first chunk with correct sample offset
1360 +// uint32_t* stco_table = (uint32_t*)(stco + 8);
1361 + write_int32(stco_table, trak->samples_[start].pos_);
1366 + // process sync samples:
1367 + if(trak->mdia_.minf_.stbl_.stss_)
1369 + unsigned char* stss = trak->mdia_.minf_.stbl_.stss_;
1370 + unsigned int entries = read_int32(stss + 4);
1371 + uint32_t* table = (uint32_t*)(stss + 8);
1372 + unsigned int stss_start;
1374 + for(i = 0; i != entries; ++i)
1376 + if(read_int32(&table[i]) >= start + 1)
1380 + for(; i != entries; ++i)
1382 + unsigned int sync_sample = read_int32(&table[i]);
1383 + if(sync_sample >= end + 1)
1385 + write_int32(&table[i - stss_start], sync_sample - start);
1388 +// memmove(table, table + stss_start, (i - stss_start) * sizeof(uint32_t));
1389 + write_int32(stss + 4, i - stss_start);
1392 + // process sample sizes
1394 + unsigned char* stsz = trak->mdia_.minf_.stbl_.stsz_;
1395 + if(stsz_get_sample_size(stsz) == 0)
1397 + uint32_t* table = (uint32_t*)(stsz + 12);
1398 + memmove(table, &table[start], (end - start) * sizeof(uint32_t));
1399 + write_int32(stsz + 8, end - start);
1404 +int moov_parse(struct moov_t* moov, unsigned char* buffer, unsigned int size)
1406 + struct atom_t leaf_atom;
1407 + unsigned char* buffer_start = buffer;
1409 + moov->start_ = buffer;
1411 + while(buffer < buffer_start + size)
1413 + buffer = atom_read_header(buffer, &leaf_atom);
1415 + atom_print(&leaf_atom);
1417 + if(atom_is(&leaf_atom, "cmov"))
1422 + if(atom_is(&leaf_atom, "mvhd"))
1424 + moov->mvhd_ = buffer;
1427 + if(atom_is(&leaf_atom, "trak"))
1429 + if(moov->tracks_ == MAX_TRACKS)
1433 + struct trak_t* trak = &moov->traks_[moov->tracks_];
1435 + trak_parse(trak, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
1439 + buffer = atom_skip(&leaf_atom);
1442 + // build the indexing tables
1445 + for(i = 0; i != moov->tracks_; ++i)
1447 + trak_build_index(&moov->traks_[i]);
1454 +void stco_shift_offsets(unsigned char* stco, int offset)
1456 + unsigned int entries = read_int32(stco + 4);
1457 + unsigned int* table = (unsigned int*)(stco + 8);
1459 + for(i = 0; i != entries; ++i)
1460 + write_int32(&table[i], (read_int32(&table[i]) + offset));
1463 +void trak_shift_offsets(struct trak_t* trak, int offset)
1465 + unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
1466 + stco_shift_offsets(stco, offset);
1469 +void moov_shift_offsets(struct moov_t* moov, int offset)
1472 + for(i = 0; i != moov->tracks_; ++i)
1474 + trak_shift_offsets(&moov->traks_[i], offset);
1478 +long mvhd_get_time_scale(unsigned char* mvhd)
1480 + int version = read_char(mvhd);
1481 + unsigned char* p = mvhd + (version == 0 ? 12 : 20);
1482 + return read_int32(p);
1485 +void mvhd_set_duration(unsigned char* mvhd, long duration)
1487 + int version = read_char(mvhd);
1490 + write_int32(mvhd + 16, duration);
1494 + perror("mvhd_set_duration");
1495 +// write_int64(mvhd + 24, duration);
1499 +long mdhd_get_time_scale(unsigned char* mdhd)
1501 + return read_int32(mdhd + 12);
1504 +void mdhd_set_duration(unsigned char* mdhd, unsigned int duration)
1506 + write_int32(mdhd + 16, duration);
1509 +void tkhd_set_duration(unsigned char* tkhd, unsigned int duration)
1511 + int version = read_char(tkhd);
1514 + write_int32(tkhd + 20, duration);
1518 + perror("tkhd_set_duration");
1519 +// write_int64(tkhd + 28, duration);
1523 +unsigned int stss_get_entries(unsigned char const* stss)
1525 + return read_int32(stss + 4);
1528 +unsigned int stss_get_sample(unsigned char const* stss, unsigned int idx)
1530 + unsigned char const* p = stss + 8 + idx * 4;
1531 + return read_int32(p);
1534 +unsigned int stss_get_nearest_keyframe(unsigned char const* stss, unsigned int sample)
1536 + // scan the sync samples to find the key frame that precedes the sample number
1538 + unsigned int entries = stss_get_entries(stss);
1539 + unsigned int table_sample = 0;
1540 + for(i = 0; i != entries; ++i)
1542 + table_sample = stss_get_sample(stss, i);
1543 + if(table_sample >= sample)
1546 + if(table_sample == sample)
1547 + return table_sample;
1549 + return stss_get_sample(stss, i - 1);
1552 +unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample)
1554 + // If the sync atom is not present, all samples are implicit sync samples.
1558 + return stss_get_nearest_keyframe(stbl->stss_, sample);
1561 +unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
1563 + unsigned int* mdat_start,
1564 + unsigned int* mdat_size,
1565 + unsigned int offset)
1567 + struct moov_t* moov = malloc(sizeof(struct moov_t));
1569 + if(!moov_parse(moov, moov_data, size))
1577 + long moov_time_scale = mvhd_get_time_scale(moov->mvhd_);
1578 + unsigned int start = (unsigned int)(start_time * moov_time_scale);
1579 +// unsigned int end = (unsigned int)(end_time * moov_time_scale);
1580 + unsigned int bytes_to_skip = UINT_MAX;
1583 + // for every trak, convert seconds to sample (time-to-sample).
1584 + // adjust sample to keyframe
1585 + unsigned int trak_sample_start[MAX_TRACKS];
1586 +// unsigned int trak_sample_end[MAX_TRACKS];
1588 + unsigned int moov_duration = 0;
1590 + // clayton.mp4 has a third track with one sample that lasts the whole clip.
1591 + // Assuming the first two tracks are the audio and video track, we patch
1592 + // the remaining tracks to 'free' atoms.
1593 + if(moov->tracks_ > 2)
1595 + for(i = 2; i != moov->tracks_; ++i)
1597 + // patch 'trak' to 'free'
1598 + unsigned char* p = moov->traks_[i].start_ - 4;
1604 + moov->tracks_ = 2;
1607 + for(i = 0; i != moov->tracks_; ++i)
1609 + struct trak_t* trak = &moov->traks_[i];
1610 + struct stbl_t* stbl = &trak->mdia_.minf_.stbl_;
1611 + long trak_time_scale = mdhd_get_time_scale(trak->mdia_.mdhd_);
1612 + float moov_to_trak_time = (float)trak_time_scale / (float)moov_time_scale;
1613 + float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
1615 + start = stts_get_sample(stbl->stts_, (unsigned int)(start * moov_to_trak_time));
1616 + start = stbl_get_nearest_keyframe(stbl, start + 1) - 1;
1617 + trak_sample_start[i] = start;
1618 + start = (unsigned int)(stts_get_time(stbl->stts_, start) * trak_to_moov_time);
1621 + printf("start=%u\n", start);
1623 + for(i = 0; i != moov->tracks_; ++i)
1625 + struct trak_t* trak = &moov->traks_[i];
1626 + struct stbl_t* stbl = &trak->mdia_.minf_.stbl_;
1628 + unsigned int start_sample = trak_sample_start[i];
1629 + unsigned int end_sample = trak->samples_size_;
1631 + trak_write_index(trak, start_sample, end_sample);
1635 + trak->samples_[start_sample].pos_ - trak->samples_[0].pos_;
1636 + if(skip < bytes_to_skip)
1637 + bytes_to_skip = skip;
1638 + printf("Trak can skip %u bytes\n", skip);
1642 + // fixup trak (duration)
1643 + unsigned int trak_duration = stts_get_duration(stbl->stts_);
1644 + long trak_time_scale = mdhd_get_time_scale(trak->mdia_.mdhd_);
1645 + float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
1646 + unsigned int duration = (long)((float)trak_duration * trak_to_moov_time);
1647 + mdhd_set_duration(trak->mdia_.mdhd_, trak_duration);
1648 + tkhd_set_duration(trak->tkhd_, duration);
1649 + printf("trak: new_duration=%d\n", duration);
1651 + if(duration > moov_duration)
1652 + moov_duration = duration;
1655 + printf("stco.size=%d, ", read_int32(stbl->stco_ + 4));
1656 + printf("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_));
1657 + printf("stsz.size=%d\n", read_int32(stbl->stsz_ + 8));
1658 + printf("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_));
1660 + mvhd_set_duration(moov->mvhd_, moov_duration);
1662 + offset -= bytes_to_skip;
1664 + printf("shifting offsets by %d\n", offset);
1665 + moov_shift_offsets(moov, offset);
1667 + *mdat_start += bytes_to_skip;
1668 + *mdat_size -= bytes_to_skip;