1 --- lighttpd-1.4.18/src/Makefile.am 2007-09-03 01:23:53.000000000 +0300
2 +++ lighttpd-mod_h264_streaming-1.4.18/src/Makefile.am 2007-10-23 23:42:37.736979478 +0300
4 mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
5 mod_flv_streaming_la_LIBADD = $(common_libadd)
7 +lib_LTLIBRARIES += mod_h264_streaming.la
8 +mod_h264_streaming_la_SOURCES = mod_h264_streaming.c moov.c
9 +mod_h264_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
10 +mod_h264_streaming_la_LIBADD = $(common_libadd)
12 lib_LTLIBRARIES += mod_evasive.la
13 mod_evasive_la_SOURCES = mod_evasive.c
14 mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
15 --- /dev/null 2008-11-04 20:33:38.146691408 +0200
16 +++ lighttpd-1.4.18/src/mod_h264_streaming.c 2009-01-26 20:59:51.385271731 +0200
18 +/*******************************************************************************
19 + mod_h264_streaming.c
21 + mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files.
22 + http://h264.code-shop.com
24 + Copyright (C) 2007-2009 CodeShop B.V.
25 +******************************************************************************/
35 +#include "response.h"
36 +#include "http_chunk.h"
37 +#include "stat_cache.h"
47 +/* plugin config for all request/connections */
59 + plugin_config **config_storage;
64 +/* init the plugin data */
65 +INIT_FUNC(mod_h264_streaming_init) {
68 + p = calloc(1, sizeof(*p));
70 + p->query_str = buffer_init();
71 + p->get_params = array_init();
76 +/* detroy the plugin data */
77 +FREE_FUNC(mod_h264_streaming_free) {
78 + plugin_data *p = p_d;
82 + if (!p) return HANDLER_GO_ON;
84 + if (p->config_storage) {
87 + for (i = 0; i < srv->config_context->used; i++) {
88 + plugin_config *s = p->config_storage[i];
92 + array_free(s->extensions);
96 + free(p->config_storage);
99 + buffer_free(p->query_str);
100 + array_free(p->get_params);
104 + return HANDLER_GO_ON;
107 +/* handle plugin config and check values */
109 +SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) {
110 + plugin_data *p = p_d;
113 + config_values_t cv[] = {
114 + { "h264-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
115 + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
118 + if (!p) return HANDLER_ERROR;
120 + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
122 + for (i = 0; i < srv->config_context->used; i++) {
125 + s = calloc(1, sizeof(plugin_config));
126 + s->extensions = array_init();
128 + cv[0].destination = s->extensions;
130 + p->config_storage[i] = s;
132 + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
133 + return HANDLER_ERROR;
137 + return HANDLER_GO_ON;
142 +static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
144 + plugin_config *s = p->config_storage[0];
148 + /* skip the first, the global context */
149 + for (i = 1; i < srv->config_context->used; i++) {
150 + data_config *dc = (data_config *)srv->config_context->data[i];
151 + s = p->config_storage[i];
153 + /* condition didn't match */
154 + if (!config_check_cond(srv, con, dc)) continue;
157 + for (j = 0; j < dc->value->used; j++) {
158 + data_unset *du = dc->value->data[j];
160 + if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
170 +static int split_get_params(array *get_params, buffer *qrystr) {
173 + char *key = NULL, *val = NULL;
177 + /* we need the \0 */
178 + for (i = 0; i < qrystr->used; i++) {
179 + switch(qrystr->ptr[i]) {
182 + val = qrystr->ptr + i + 1;
184 + qrystr->ptr[i] = '\0';
191 + case '\0': /* fin symbol */
194 + /* we need at least a = since the last & */
196 + /* terminate the value */
197 + qrystr->ptr[i] = '\0';
199 + if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
200 + ds = data_string_init();
202 + buffer_copy_string_len(ds->key, key, strlen(key));
203 + buffer_copy_string_len(ds->value, val, strlen(val));
205 + array_insert_unique(get_params, (data_unset *)ds);
208 + key = qrystr->ptr + i + 1;
218 +URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
219 + plugin_data *p = p_d;
225 + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
227 + mod_h264_streaming_patch_connection(srv, con, p);
229 + s_len = con->physical.path->used - 1;
231 + for (k = 0; k < p->conf.extensions->used; k++) {
232 + data_string *ds = (data_string *)p->conf.extensions->data[k];
233 + int ct_len = ds->value->used - 1;
235 + if (ct_len > s_len) continue;
236 + if (ds->value->used == 0) continue;
238 + if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
239 + data_string *get_param;
240 + stat_cache_entry *sce = NULL;
241 + double start = 0.0;
244 + int client_is_flash = 0;
246 + array_reset(p->get_params);
247 + buffer_copy_string_buffer(p->query_str, con->uri.query);
248 + split_get_params(p->get_params, p->query_str);
250 + /* if there is a start=[0-9]+ in the header use it as start,
251 + * otherwise send the full file */
252 + if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start")))
254 + /* check if it is a number */
255 + start = strtod(get_param->value->ptr, &err);
256 + if (*err != '\0') {
257 + return HANDLER_GO_ON;
259 + if (start < 0) return HANDLER_GO_ON;
262 + /* if there is an end=[0-9]+ in the header use it as end */
263 + if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end")))
265 + /* check if it is a number */
266 + end = strtod(get_param->value->ptr, &err);
267 + if (*err != '\0') {
268 + return HANDLER_GO_ON;
270 + if (end < 0 || start >= end) return HANDLER_GO_ON;
273 + if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "client")))
275 + client_is_flash = starts_with(get_param->value->ptr, "FLASH");
278 + /* get file info */
279 + if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
280 + return HANDLER_GO_ON;
283 + /* we are safe now, let's build a h264 header */
286 + unsigned int filesize = sce->st.st_size;
289 + uint32_t mp4_header_size;
290 + uint64_t mdat_offset;
291 + uint64_t mdat_size;
293 + int result = mp4_split(con->physical.path->ptr, filesize, start, end,
294 + &mp4_header, &mp4_header_size,
295 + &mdat_offset, &mdat_size, client_is_flash);
299 + buffer* b = chunkqueue_get_append_buffer(con->write_queue);
300 + buffer_append_memory(b, mp4_header, mp4_header_size);
301 + b->used++; /* add virtual \0 */
303 + http_chunk_append_file(srv, con, con->physical.path,
304 + mdat_offset, mdat_size);
314 + return HANDLER_GO_ON;
319 + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4"));
320 + if(!client_is_flash)
322 + response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0"));
326 + response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0,client=flash"));
329 + con->file_finished = 1;
331 + return HANDLER_FINISHED;
336 + return HANDLER_GO_ON;
339 +/* this function is called at dlopen() time and inits the callbacks */
341 +int mod_h264_streaming_plugin_init(plugin *p) {
342 + p->version = LIGHTTPD_VERSION_ID;
343 + p->name = buffer_init_string("h264_streaming");
345 + p->init = mod_h264_streaming_init;
346 + p->handle_physical = mod_h264_streaming_path_handler;
347 + p->set_defaults = mod_h264_streaming_set_defaults;
348 + p->cleanup = mod_h264_streaming_free;
355 --- /dev/null 2008-11-04 20:33:38.146691408 +0200
356 +++ lighttpd-1.4.18/src/moov.c 2009-01-26 21:00:05.071936866 +0200
358 +/*******************************************************************************
361 + moov - A library for splitting Quicktime/MPEG4 files.
362 + http://h264.code-shop.com
364 + Copyright (C) 2007-2009 CodeShop B.V.
367 + The H264 Streaming Module is licened under a Creative Common License. It allows
368 + you to use, modify and redistribute the module, but only for *noncommercial*
369 + purposes. For corporate use, please apply for a commercial license.
371 + Creative Commons License:
372 + http://creativecommons.org/licenses/by-nc-sa/3.0/
374 + Commercial License:
375 + http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2
376 +******************************************************************************/
381 +#define _CRTDBG_MAP_ALLOC
387 +#elif defined(__GNUC__)
388 +# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
389 +#elif defined(__LCLINT__)
390 +# define UNUSED(x) /*@unused@*/ x
392 +# define UNUSED(x) x
396 + The QuickTime File Format PDF from Apple:
397 + http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
406 +#ifdef HAVE_CONFIG_H
410 +#ifdef HAVE_STDINT_H
411 +# include <stdint.h>
413 +#ifdef HAVE_INTTYPES_H
414 +# include <inttypes.h>
416 +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ
417 +// Compress the MOOV atom. Turn this off for Flash as it doesn't support it.
418 +// # define COMPRESS_MOOV_ATOM
423 +#define ftello _ftelli64
424 +#define fseeko _fseeki64
427 +#define MAX_TRACKS 8
429 +#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24) + \
430 + ((uint32_t)(b) << 16) + \
431 + ((uint32_t)(c) << 8) + \
434 +/* Returns true when the test string is a prefix of the input */
435 +int starts_with(const char* input, const char* test)
437 + while(*input && *test)
439 + if(*input != *test)
445 + return *test == '\0';
448 +static unsigned int read_8(unsigned char const* buffer)
453 +static unsigned char* write_8(unsigned char* buffer, unsigned char v)
460 +static uint16_t read_16(unsigned char const* buffer)
462 + return (buffer[0] << 8) |
466 +static unsigned char* write_16(unsigned char* buffer, unsigned int v)
468 + buffer[0] = (unsigned char)(v >> 8);
469 + buffer[1] = (unsigned char)(v >> 0);
474 +static unsigned int read_24(unsigned char const* buffer)
476 + return (buffer[0] << 16) |
481 +static unsigned char* write_24(unsigned char* buffer, unsigned int v)
483 + buffer[0] = (unsigned char)(v >> 16);
484 + buffer[1] = (unsigned char)(v >> 8);
485 + buffer[2] = (unsigned char)(v >> 0);
490 +static uint32_t read_32(unsigned char const* buffer)
492 + return (buffer[0] << 24) |
493 + (buffer[1] << 16) |
498 +static unsigned char* write_32(unsigned char* buffer, uint32_t v)
500 + buffer[0] = (unsigned char)(v >> 24);
501 + buffer[1] = (unsigned char)(v >> 16);
502 + buffer[2] = (unsigned char)(v >> 8);
503 + buffer[3] = (unsigned char)(v >> 0);
508 +static uint64_t read_64(unsigned char const* buffer)
510 + return ((uint64_t)(read_32(buffer)) << 32) + read_32(buffer + 4);
513 +static unsigned char* write_64(unsigned char* buffer, uint64_t v)
515 + write_32(buffer + 0, (uint32_t)(v >> 32));
516 + write_32(buffer + 4, (uint32_t)(v >> 0));
521 +#define ATOM_PREAMBLE_SIZE 8
526 + uint32_t short_size_;
528 + unsigned char* start_;
529 + unsigned char* end_;
532 +static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
534 + atom->start_ = buffer;
535 + atom->short_size_ = read_32(buffer);
536 + atom->type_ = read_32(buffer + 4);
538 + if(atom->short_size_ == 1)
539 + atom->size_ = read_64(buffer + 8);
541 + atom->size_ = atom->short_size_;
543 + atom->end_ = atom->start_ + atom->size_;
545 + return buffer + ATOM_PREAMBLE_SIZE + (atom->short_size_ == 1 ? 8 : 0);
548 +static void atom_print(struct atom_t const* atom)
550 + printf("Atom(%c%c%c%c,%lld)\n",
558 +struct unknown_atom_t
561 + struct unknown_atom_t* next_;
564 +static struct unknown_atom_t* unknown_atom_init()
566 + struct unknown_atom_t* atom = (struct unknown_atom_t*)malloc(sizeof(struct unknown_atom_t));
573 +static void unknown_atom_exit(struct unknown_atom_t* atom)
577 + struct unknown_atom_t* next = atom->next_;
584 +static struct unknown_atom_t* unknown_atom_add_atom(struct unknown_atom_t* parent, void* atom)
586 + size_t size = read_32(atom);
587 + struct unknown_atom_t* unknown = unknown_atom_init();
588 + unknown->atom_ = malloc(size);
589 + memcpy(unknown->atom_, atom, size);
590 + unknown->next_ = parent;
594 +struct atom_read_list_t
598 + int (*destination_)(void* parent, void* child);
599 + void* (*reader_)(void* parent, unsigned char* buffer, uint64_t size);
602 +static int atom_reader(struct atom_read_list_t* atom_read_list,
603 + unsigned int atom_read_list_size,
605 + unsigned char* buffer, uint64_t size)
607 + struct atom_t leaf_atom;
608 + unsigned char* buffer_start = buffer;
610 + while(buffer < buffer_start + size)
613 + buffer = atom_read_header(buffer, &leaf_atom);
615 + atom_print(&leaf_atom);
617 + for(i = 0; i != atom_read_list_size; ++i)
619 + if(leaf_atom.type_ == atom_read_list[i].type_)
625 + if(i == atom_read_list_size)
627 + // add to unkown chunks
628 + (*(struct unknown_atom_t**)parent) =
629 + unknown_atom_add_atom(*(struct unknown_atom_t**)(parent), buffer - ATOM_PREAMBLE_SIZE);
634 + atom_read_list[i].reader_(parent, buffer,
635 + leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
638 + if(!atom_read_list[i].destination_(parent, child))
641 + buffer = leaf_atom.end_;
644 + if(buffer < buffer_start + size)
652 +struct atom_write_list_t
657 + unsigned char* (*writer_)(void* parent, void* atom, unsigned char* buffer);
660 +static unsigned char* atom_writer_unknown(struct unknown_atom_t* atoms,
661 + unsigned char* buffer)
665 + size_t size = read_32(atoms->atom_);
666 + memcpy(buffer, atoms->atom_, size);
668 + atoms = atoms->next_;
674 +static unsigned char* atom_writer(struct unknown_atom_t* unknown_atoms,
675 + struct atom_write_list_t* atom_write_list,
676 + unsigned int atom_write_list_size,
677 + unsigned char* buffer)
680 + const int write_box64 = 0;
684 + buffer = atom_writer_unknown(unknown_atoms, buffer);
687 + for(i = 0; i != atom_write_list_size; ++i)
689 + if(atom_write_list[i].source_ != 0)
691 + unsigned char* atom_start = buffer;
695 + write_32(buffer, 1); // box64
700 + buffer = write_32(buffer, atom_write_list[i].type_);
703 + buffer += 8; // box64
707 + buffer = atom_write_list[i].writer_(atom_write_list[i].parent_,
708 + atom_write_list[i].source_, buffer);
711 + write_64(atom_start + 8, buffer - atom_start);
713 + write_32(atom_start, buffer - atom_start);
722 + unsigned int version_;
723 + unsigned int flags_;
724 + uint64_t creation_time_;
725 + uint64_t modification_time_;
726 + uint32_t track_id_;
727 + uint32_t reserved_;
728 + uint64_t duration_;
729 + uint32_t reserved2_[2];
731 + uint16_t predefined_;
733 + uint16_t reserved3_;
734 + uint32_t matrix_[9];
741 + unsigned int version_;
742 + unsigned int flags_;
743 + uint64_t creation_time_;
744 + uint64_t modification_time_;
745 + uint32_t timescale_;
746 + uint64_t duration_;
747 + unsigned int language_[3];
748 + uint16_t predefined_;
753 + unsigned int version_;
754 + unsigned int flags_;
755 + uint16_t graphics_mode_;
756 + uint16_t opcolor_[3];
761 + unsigned int version_;
762 + unsigned int flags_;
763 + uint32_t predefined_;
764 + uint32_t handler_type_;
765 + uint32_t reserved1_;
766 + uint32_t reserved2_;
767 + uint32_t reserved3_;
773 + struct unknown_atom_t* unknown_atoms_;
774 +//struct stsd_t* stsd_; // sample description
775 + struct stts_t* stts_; // decoding time-to-sample
776 + struct stss_t* stss_; // sync sample
777 + struct stsc_t* stsc_; // sample-to-chunk
778 + struct stsz_t* stsz_; // sample size
779 + struct stco_t* stco_; // chunk offset
780 + struct ctts_t* ctts_; // composition time-to-sample
782 + void* stco_inplace_; // newly generated stco (patched inplace)
787 + uint32_t sample_count_;
788 + uint32_t sample_duration_;
793 + unsigned int version_;
794 + unsigned int flags_;
796 + struct stts_table_t* table_;
801 + unsigned int version_;
802 + unsigned int flags_;
804 + uint32_t* sample_numbers_;
816 + unsigned int version_;
817 + unsigned int flags_;
819 + struct stsc_table_t* table_;
824 + unsigned int version_;
825 + unsigned int flags_;
826 + uint32_t sample_size_;
828 + uint32_t* sample_sizes_;
833 + unsigned int version_;
834 + unsigned int flags_;
836 + uint64_t* chunk_offsets_;
841 + uint32_t sample_count_;
842 + uint32_t sample_offset_;
847 + unsigned int version_;
848 + unsigned int flags_;
850 + struct ctts_table_t* table_;
855 + struct unknown_atom_t* unknown_atoms_;
856 + struct vmhd_t* vmhd_;
857 +// struct dinf_t* dinf_;
858 + struct stbl_t* stbl_;
863 + struct unknown_atom_t* unknown_atoms_;
864 + struct mdhd_t* mdhd_;
865 + struct hdlr_t* hdlr_;
866 + struct minf_t* minf_;
871 + unsigned int sample_; // number of the first sample in the chunk
872 + unsigned int size_; // number of samples in the chunk
873 + int id_; // for multiple codecs mode - not used
874 + uint64_t pos_; // start byte position of chunk
879 + unsigned int pts_; // decoding/presentation time
880 + unsigned int size_; // size in bytes
881 + uint64_t pos_; // byte offset
882 + unsigned int cto_; // composition time offset
887 + struct unknown_atom_t* unknown_atoms_;
888 + struct tkhd_t* tkhd_;
889 + struct mdia_t* mdia_;
891 + /* temporary indices */
892 + unsigned int chunks_size_;
893 + struct chunks_t* chunks_;
895 + unsigned int samples_size_;
896 + struct samples_t* samples_;
901 + unsigned int version_;
902 + unsigned int flags_;
903 + uint64_t creation_time_;
904 + uint64_t modification_time_;
905 + uint32_t timescale_;
906 + uint64_t duration_;
909 + uint16_t reserved1_;
910 + uint32_t reserved2_[2];
911 + uint32_t matrix_[9];
912 + uint32_t predefined_[6];
913 + uint32_t next_track_id_;
918 + struct unknown_atom_t* unknown_atoms_;
919 + struct mvhd_t* mvhd_;
920 + unsigned int tracks_;
921 + struct trak_t* traks_[MAX_TRACKS];
925 +static struct tkhd_t* tkhd_init()
927 + struct tkhd_t* tkhd = (struct tkhd_t*)malloc(sizeof(struct tkhd_t));
932 +static void tkhd_exit(struct tkhd_t* tkhd)
937 +static void* tkhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
941 + struct tkhd_t* tkhd = tkhd_init();
943 + tkhd->version_ = read_8(buffer + 0);
944 + tkhd->flags_ = read_24(buffer + 1);
945 + if(tkhd->version_ == 0)
950 + tkhd->creation_time_ = read_32(buffer + 4);
951 + tkhd->modification_time_ = read_32(buffer + 8);
952 + tkhd->track_id_ = read_32(buffer + 12);
953 + tkhd->reserved_ = read_32(buffer + 16);
954 + tkhd->duration_ = read_32(buffer + 20);
962 + tkhd->creation_time_ = read_64(buffer + 4);
963 + tkhd->modification_time_ = read_64(buffer + 12);
964 + tkhd->track_id_ = read_32(buffer + 20);
965 + tkhd->reserved_ = read_32(buffer + 24);
966 + tkhd->duration_ = read_64(buffer + 28);
970 + tkhd->reserved2_[0] = read_32(buffer + 0);
971 + tkhd->reserved2_[1] = read_32(buffer + 4);
972 + tkhd->layer_ = read_16(buffer + 8);
973 + tkhd->predefined_ = read_16(buffer + 10);
974 + tkhd->volume_ = read_16(buffer + 12);
975 + tkhd->reserved3_ = read_16(buffer + 14);
978 + for(i = 0; i != 9; ++i)
980 + tkhd->matrix_[i] = read_32(buffer);
984 + tkhd->width_ = read_32(buffer + 0);
985 + tkhd->height_ = read_32(buffer + 4);
990 +static unsigned char* tkhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
992 + struct tkhd_t const* tkhd = atom;
995 + buffer = write_8(buffer, tkhd->version_);
996 + buffer = write_24(buffer, tkhd->flags_);
998 + if(tkhd->version_ == 0)
1000 + buffer = write_32(buffer, (uint32_t)tkhd->creation_time_);
1001 + buffer = write_32(buffer, (uint32_t)tkhd->modification_time_);
1002 + buffer = write_32(buffer, tkhd->track_id_);
1003 + buffer = write_32(buffer, tkhd->reserved_);
1004 + buffer = write_32(buffer, (uint32_t)tkhd->duration_);
1008 + buffer = write_64(buffer, tkhd->creation_time_);
1009 + buffer = write_64(buffer, tkhd->modification_time_);
1010 + buffer = write_32(buffer, tkhd->track_id_);
1011 + buffer = write_32(buffer, tkhd->reserved_);
1012 + buffer = write_64(buffer, tkhd->duration_);
1015 + buffer = write_32(buffer, tkhd->reserved2_[0]);
1016 + buffer = write_32(buffer, tkhd->reserved2_[1]);
1017 + buffer = write_16(buffer, tkhd->layer_);
1018 + buffer = write_16(buffer, tkhd->predefined_);
1019 + buffer = write_16(buffer, tkhd->volume_);
1020 + buffer = write_16(buffer, tkhd->reserved3_);
1022 + for(i = 0; i != 9; ++i)
1024 + buffer = write_32(buffer, tkhd->matrix_[i]);
1027 + buffer = write_32(buffer, tkhd->width_);
1028 + buffer = write_32(buffer, tkhd->height_);
1033 +static struct mdhd_t* mdhd_init()
1035 + struct mdhd_t* mdhd = (struct mdhd_t*)malloc(sizeof(struct mdhd_t));
1040 +static void mdhd_exit(struct mdhd_t* mdhd)
1045 +static void* mdhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t UNUSED(size))
1047 + uint16_t language;
1050 + struct mdhd_t* mdhd = mdhd_init();
1051 + mdhd->version_ = read_8(buffer + 0);
1052 + mdhd->flags_ = read_24(buffer + 1);
1053 + if(mdhd->version_ == 0)
1055 + mdhd->creation_time_ = read_32(buffer + 4);
1056 + mdhd->modification_time_ = read_32(buffer + 8);
1057 + mdhd->timescale_ = read_32(buffer + 12);
1058 + mdhd->duration_ = read_32(buffer + 16);
1063 + mdhd->creation_time_ = read_64(buffer + 4);
1064 + mdhd->modification_time_ = read_64(buffer + 12);
1065 + mdhd->timescale_ = read_32(buffer + 20);
1066 + mdhd->duration_ = read_64(buffer + 24);
1070 + language = read_16(buffer + 0);
1071 + for(i = 0; i != 3; ++i)
1073 + mdhd->language_[i] = ((language >> ((2 - i) * 5)) & 0x1f) + 0x60;
1076 + mdhd->predefined_ = read_16(buffer + 2);
1081 +static unsigned char* mdhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1083 + struct mdhd_t const* mdhd = atom;
1085 + buffer = write_8(buffer, mdhd->version_);
1086 + buffer = write_24(buffer, mdhd->flags_);
1088 + if(mdhd->version_ == 0)
1090 + buffer = write_32(buffer, (uint32_t)mdhd->creation_time_);
1091 + buffer = write_32(buffer, (uint32_t)mdhd->modification_time_);
1092 + buffer = write_32(buffer, mdhd->timescale_);
1093 + buffer = write_32(buffer, (uint32_t)mdhd->duration_);
1097 + buffer = write_64(buffer, mdhd->creation_time_);
1098 + buffer = write_64(buffer, mdhd->modification_time_);
1099 + buffer = write_32(buffer, mdhd->timescale_);
1100 + buffer = write_64(buffer, mdhd->duration_);
1103 + buffer = write_16(buffer,
1104 + ((mdhd->language_[0] - 0x60) << 10) +
1105 + ((mdhd->language_[1] - 0x60) << 5) +
1106 + ((mdhd->language_[2] - 0x60) << 0));
1108 + buffer = write_16(buffer, mdhd->predefined_);
1113 +static struct vmhd_t* vmhd_init()
1115 + struct vmhd_t* atom = (struct vmhd_t*)malloc(sizeof(struct vmhd_t));
1120 +void vmhd_exit(struct vmhd_t* atom)
1125 +static void* vmhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1129 + struct vmhd_t* atom;
1134 + atom = vmhd_init();
1135 + atom->version_ = read_8(buffer + 0);
1136 + atom->flags_ = read_24(buffer + 1);
1138 + atom->graphics_mode_ = read_16(buffer + 4);
1140 + for(i = 0; i != 3; ++i)
1142 + atom->opcolor_[i] = read_16(buffer);
1149 +static unsigned char* vmhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1151 + struct vmhd_t const* vmhd = atom;
1154 + buffer = write_8(buffer, vmhd->version_);
1155 + buffer = write_24(buffer, vmhd->flags_);
1156 + buffer = write_16(buffer, vmhd->graphics_mode_);
1157 + for(i = 0; i != 3; ++i)
1159 + buffer = write_16(buffer, vmhd->opcolor_[i]);
1165 +static struct hdlr_t* hdlr_init()
1167 + struct hdlr_t* atom = (struct hdlr_t*)malloc(sizeof(struct hdlr_t));
1173 +static void hdlr_exit(struct hdlr_t* atom)
1177 + free(atom->name_);
1182 +static void* hdlr_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1184 + struct hdlr_t* atom;
1189 + atom = hdlr_init();
1190 + atom->version_ = read_8(buffer + 0);
1191 + atom->flags_ = read_24(buffer + 1);
1192 + atom->predefined_ = read_32(buffer + 4);
1193 + atom->handler_type_ = read_32(buffer + 8);
1194 + atom->reserved1_ = read_32(buffer + 12);
1195 + atom->reserved2_ = read_32(buffer + 16);
1196 + atom->reserved3_ = read_32(buffer + 20);
1201 + size_t length = (size_t)size;
1202 + atom->name_ = malloc(length + 1);
1203 + if(atom->predefined_ == FOURCC('m', 'h', 'l', 'r'))
1205 + length = read_8(buffer);
1208 + length = (size_t)size;
1210 + memcpy(atom->name_, buffer, length);
1211 + atom->name_[length] = '\0';
1217 +static unsigned char* hdlr_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1219 + struct hdlr_t* hdlr = atom;
1220 + buffer = write_8(buffer, hdlr->version_);
1221 + buffer = write_24(buffer, hdlr->flags_);
1223 + buffer = write_32(buffer, hdlr->predefined_);
1224 + buffer = write_32(buffer, hdlr->handler_type_);
1225 + buffer = write_32(buffer, hdlr->reserved1_);
1226 + buffer = write_32(buffer, hdlr->reserved2_);
1227 + buffer = write_32(buffer, hdlr->reserved3_);
1231 + if(hdlr->predefined_ == FOURCC('m', 'h', 'l', 'r'))
1233 + buffer = write_8(buffer, strlen(hdlr->name_));
1236 + for(p = hdlr->name_; *p; ++p)
1237 + buffer = write_8(buffer, *p);
1243 +static struct stts_t* stts_init()
1245 + struct stts_t* atom = (struct stts_t*)malloc(sizeof(struct stts_t));
1251 +void stts_exit(struct stts_t* atom)
1255 + free(atom->table_);
1260 +static void* stts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1264 + struct stts_t* atom;
1269 + atom = stts_init();
1270 + atom->version_ = read_8(buffer + 0);
1271 + atom->flags_ = read_24(buffer + 1);
1272 + atom->entries_ = read_32(buffer + 4);
1274 + if(size < 8 + atom->entries_ * sizeof(struct stts_table_t))
1279 + atom->table_ = (struct stts_table_t*)(malloc(atom->entries_ * sizeof(struct stts_table_t)));
1281 + for(i = 0; i != atom->entries_; ++i)
1283 + atom->table_[i].sample_count_ = read_32(buffer + 0);
1284 + atom->table_[i].sample_duration_ = read_32(buffer + 4);
1291 +static unsigned char* stts_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1293 + struct stts_t* stts = atom;
1296 + buffer = write_8(buffer, stts->version_);
1297 + buffer = write_24(buffer, stts->flags_);
1298 + buffer = write_32(buffer, stts->entries_);
1299 + for(i = 0; i != stts->entries_; ++i)
1301 + buffer = write_32(buffer, stts->table_[i].sample_count_);
1302 + buffer = write_32(buffer, stts->table_[i].sample_duration_);
1308 +static unsigned int stts_get_sample(struct stts_t const* stts, uint64_t time)
1310 + unsigned int stts_index = 0;
1311 + unsigned int stts_count;
1313 + unsigned int ret = 0;
1314 + uint64_t time_count = 0;
1316 + for(; stts_index != stts->entries_; ++stts_index)
1318 + unsigned int sample_count = stts->table_[stts_index].sample_count_;
1319 + unsigned int sample_duration = stts->table_[stts_index].sample_duration_;
1320 + if(time_count + (uint64_t)sample_duration * (uint64_t)sample_count >= time)
1322 + stts_count = (unsigned int)((time - time_count) / sample_duration);
1323 + time_count += (uint64_t)stts_count * (uint64_t)sample_duration;
1324 + ret += stts_count;
1329 + time_count += (uint64_t)sample_duration * (uint64_t)sample_count;
1330 + ret += sample_count;
1336 +static uint64_t stts_get_time(struct stts_t const* stts, unsigned int sample)
1339 + unsigned int stts_index = 0;
1340 + unsigned int sample_count = 0;
1344 + unsigned int table_sample_count = stts->table_[stts_index].sample_count_;
1345 + unsigned int table_sample_duration = stts->table_[stts_index].sample_duration_;
1346 + if(sample_count + table_sample_count > sample)
1348 + unsigned int stts_count = (sample - sample_count);
1349 + ret += (uint64_t)stts_count * (uint64_t)table_sample_duration;
1354 + sample_count += table_sample_count;
1355 + ret += (uint64_t)table_sample_count * (uint64_t)table_sample_duration;
1362 +static uint64_t stts_get_duration(struct stts_t const* stts)
1364 + uint64_t duration = 0;
1366 + for(i = 0; i != stts->entries_; ++i)
1368 + unsigned int sample_count = stts->table_[i].sample_count_;
1369 + unsigned int sample_duration = stts->table_[i].sample_duration_;
1370 + duration += (uint64_t)sample_duration * (uint64_t)sample_count;
1376 +static unsigned int stts_get_samples(struct stts_t const* stts)
1378 + unsigned int samples = 0;
1379 + unsigned int entries = stts->entries_;
1381 + for(i = 0; i != entries; ++i)
1383 + unsigned int sample_count = stts->table_[i].sample_count_;
1384 +// unsigned int sample_duration = stts->table_[i].sample_duration_;
1385 + samples += sample_count;
1391 +static struct stss_t* stss_init()
1393 + struct stss_t* atom = (struct stss_t*)malloc(sizeof(struct stss_t));
1394 + atom->sample_numbers_ = 0;
1399 +void stss_exit(struct stss_t* atom)
1401 + if(atom->sample_numbers_)
1403 + free(atom->sample_numbers_);
1408 +static void* stss_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1412 + struct stss_t* atom;
1417 + atom = stss_init();
1418 + atom->version_ = read_8(buffer + 0);
1419 + atom->flags_ = read_24(buffer + 1);
1420 + atom->entries_ = read_32(buffer + 4);
1422 + if(size < 8 + atom->entries_ * sizeof(uint32_t))
1427 + atom->sample_numbers_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
1429 + for(i = 0; i != atom->entries_; ++i)
1431 + atom->sample_numbers_[i] = read_32(buffer);
1438 +static unsigned char* stss_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1440 + struct stss_t const* stss = atom;
1443 + buffer = write_8(buffer, stss->version_);
1444 + buffer = write_24(buffer, stss->flags_);
1445 + buffer = write_32(buffer, stss->entries_);
1446 + for(i = 0; i != stss->entries_; ++i)
1448 + buffer = write_32(buffer, stss->sample_numbers_[i]);
1454 +static unsigned int stss_get_nearest_keyframe(struct stss_t const* stss, unsigned int sample)
1456 + // scan the sync samples to find the key frame that precedes the sample number
1458 + unsigned int table_sample = 0;
1459 + for(i = 0; i != stss->entries_; ++i)
1461 + table_sample = stss->sample_numbers_[i];
1462 + if(table_sample >= sample)
1465 + if(table_sample == sample)
1466 + return table_sample;
1468 + return stss->sample_numbers_[i - 1];
1471 +static struct stsc_t* stsc_init()
1473 + struct stsc_t* atom = (struct stsc_t*)malloc(sizeof(struct stsc_t));
1479 +static void stsc_exit(struct stsc_t* atom)
1483 + free(atom->table_);
1488 +static void* stsc_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1492 + struct stsc_t* atom;
1497 + atom = stsc_init();
1498 + atom->version_ = read_8(buffer + 0);
1499 + atom->flags_ = read_24(buffer + 1);
1500 + atom->entries_ = read_32(buffer + 4);
1502 + if(size < 8 + atom->entries_ * sizeof(struct stsc_table_t))
1507 + // reserve space for one extra entry as when splitting the video we may have to
1508 + // split the first entry
1509 + atom->table_ = (struct stsc_table_t*)(malloc((atom->entries_ + 1) * sizeof(struct stsc_table_t)));
1511 + for(i = 0; i != atom->entries_; ++i)
1513 + atom->table_[i].chunk_ = read_32(buffer + 0) - 1; // Note: we use zero based
1514 + atom->table_[i].samples_ = read_32(buffer + 4);
1515 + atom->table_[i].id_ = read_32(buffer + 8);
1522 +static unsigned char* stsc_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1524 + struct stsc_t* stsc = atom;
1527 + buffer = write_8(buffer, stsc->version_);
1528 + buffer = write_24(buffer, stsc->flags_);
1529 + buffer = write_32(buffer, stsc->entries_);
1530 + for(i = 0; i != stsc->entries_; ++i)
1532 + buffer = write_32(buffer, stsc->table_[i].chunk_ + 1);
1533 + buffer = write_32(buffer, stsc->table_[i].samples_);
1534 + buffer = write_32(buffer, stsc->table_[i].id_);
1540 +static struct stsz_t* stsz_init()
1542 + struct stsz_t* atom = (struct stsz_t*)malloc(sizeof(struct stsz_t));
1543 + atom->sample_sizes_ = 0;
1548 +static void stsz_exit(struct stsz_t* atom)
1550 + if(atom->sample_sizes_)
1552 + free(atom->sample_sizes_);
1557 +static void* stsz_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1561 + struct stsz_t* atom;
1565 + printf("Error: not enough bytes for stsz atom\n");
1569 + atom = stsz_init();
1570 + atom->version_ = read_8(buffer + 0);
1571 + atom->flags_ = read_24(buffer + 1);
1572 + atom->sample_size_ = read_32(buffer + 4);
1573 + atom->entries_ = read_32(buffer + 8);
1576 + // fix for clayton.mp4, it mistakenly says there is 1 entry
1577 + if(atom->sample_size_ && atom->entries_)
1578 + atom->entries_ = 0;
1580 + if(size < 12 + atom->entries_ * sizeof(uint32_t))
1582 + printf("Error: stsz.entries don't match with size\n");
1587 + if(!atom->sample_size_)
1589 + atom->sample_sizes_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t));
1590 + for(i = 0; i != atom->entries_; ++i)
1592 + atom->sample_sizes_[i] = read_32(buffer);
1600 +static unsigned char* stsz_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1602 + struct stsz_t* stsz = atom;
1604 + unsigned int entries = stsz->sample_size_ ? 0 : stsz->entries_;
1606 + buffer = write_8(buffer, stsz->version_);
1607 + buffer = write_24(buffer, stsz->flags_);
1608 + buffer = write_32(buffer, stsz->sample_size_);
1609 + buffer = write_32(buffer, entries);
1610 + for(i = 0; i != entries; ++i)
1612 + buffer = write_32(buffer, stsz->sample_sizes_[i]);
1618 +static struct stco_t* stco_init()
1620 + struct stco_t* atom = (struct stco_t*)malloc(sizeof(struct stco_t));
1621 + atom->chunk_offsets_ = 0;
1626 +static void stco_exit(struct stco_t* atom)
1628 + if(atom->chunk_offsets_)
1630 + free(atom->chunk_offsets_);
1635 +static void* stco_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1639 + struct stco_t* atom;
1644 + atom = stco_init();
1645 + atom->version_ = read_8(buffer + 0);
1646 + atom->flags_ = read_24(buffer + 1);
1647 + atom->entries_ = read_32(buffer + 4);
1650 + if(size < 8 + atom->entries_ * sizeof(uint32_t))
1653 + atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
1654 + for(i = 0; i != atom->entries_; ++i)
1656 + atom->chunk_offsets_[i] = read_32(buffer);
1663 +static void* co64_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1667 + struct stco_t* atom;
1672 + atom = stco_init();
1673 + atom->version_ = read_8(buffer + 0);
1674 + atom->flags_ = read_24(buffer + 1);
1675 + atom->entries_ = read_32(buffer + 4);
1678 + if(size < 8 + atom->entries_ * sizeof(uint64_t))
1681 + atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t));
1682 + for(i = 0; i != atom->entries_; ++i)
1684 + atom->chunk_offsets_[i] = read_64(buffer);
1691 +static unsigned char* stco_write(void* parent, void* atom, unsigned char* buffer)
1693 + struct stbl_t* stbl = parent;
1694 + struct stco_t* stco = atom;
1697 + stbl->stco_inplace_ = buffer; // newly generated stco (patched inplace)
1699 + buffer = write_8(buffer, stco->version_);
1700 + buffer = write_24(buffer, stco->flags_);
1701 + buffer = write_32(buffer, stco->entries_);
1702 + for(i = 0; i != stco->entries_; ++i)
1704 + buffer = write_32(buffer, (uint32_t)(stco->chunk_offsets_[i]));
1710 +static void stco_shift_offsets(struct stco_t* stco, int offset)
1713 + for(i = 0; i != stco->entries_; ++i)
1714 + stco->chunk_offsets_[i] += offset;
1717 +static void stco_shift_offsets_inplace(unsigned char* stco, int offset)
1719 + unsigned int entries = read_32(stco + 4);
1720 + unsigned int* table = (unsigned int*)(stco + 8);
1722 + for(i = 0; i != entries; ++i)
1723 + write_32((unsigned char*)&table[i], (read_32((unsigned char*)&table[i]) + offset));
1726 +static struct ctts_t* ctts_init()
1728 + struct ctts_t* atom = (struct ctts_t*)malloc(sizeof(struct ctts_t));
1734 +static void ctts_exit(struct ctts_t* atom)
1738 + free(atom->table_);
1743 +static void* ctts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1747 + struct ctts_t* atom;
1752 + atom = ctts_init();
1753 + atom->version_ = read_8(buffer + 0);
1754 + atom->flags_ = read_24(buffer + 1);
1755 + atom->entries_ = read_32(buffer + 4);
1757 + if(size < 8 + atom->entries_ * sizeof(struct ctts_table_t))
1762 + atom->table_ = (struct ctts_table_t*)(malloc(atom->entries_ * sizeof(struct ctts_table_t)));
1764 + for(i = 0; i != atom->entries_; ++i)
1766 + atom->table_[i].sample_count_ = read_32(buffer + 0);
1767 + atom->table_[i].sample_offset_ = read_32(buffer + 4);
1774 +static unsigned char* ctts_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1776 + struct ctts_t const* ctts = atom;
1779 + buffer = write_8(buffer, ctts->version_);
1780 + buffer = write_24(buffer, ctts->flags_);
1781 + buffer = write_32(buffer, ctts->entries_);
1782 + for(i = 0; i != ctts->entries_; ++i)
1784 + buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_count_));
1785 + buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_offset_));
1791 +static unsigned int ctts_get_samples(struct ctts_t const* ctts)
1793 + unsigned int samples = 0;
1794 + unsigned int entries = ctts->entries_;
1796 + for(i = 0; i != entries; ++i)
1798 + unsigned int sample_count = ctts->table_[i].sample_count_;
1799 +// unsigned int sample_offset = ctts->table_[i].sample_offset_;
1800 + samples += sample_count;
1806 +static struct stbl_t* stbl_init()
1808 + struct stbl_t* atom = (struct stbl_t*)malloc(sizeof(struct stbl_t));
1809 + atom->unknown_atoms_ = 0;
1820 +static void stbl_exit(struct stbl_t* atom)
1822 + if(atom->unknown_atoms_)
1824 + unknown_atom_exit(atom->unknown_atoms_);
1828 + stts_exit(atom->stts_);
1832 + stss_exit(atom->stss_);
1836 + stsc_exit(atom->stsc_);
1840 + stsz_exit(atom->stsz_);
1844 + stco_exit(atom->stco_);
1848 + ctts_exit(atom->ctts_);
1854 +static int stbl_add_stts(void* parent, void* child)
1856 + struct stbl_t* stbl = parent;
1857 + stbl->stts_ = child;
1862 +static int stbl_add_stss(void* parent, void* child)
1864 + struct stbl_t* stbl = parent;
1865 + stbl->stss_ = child;
1870 +static int stbl_add_stsc(void* parent, void* child)
1872 + struct stbl_t* stbl = parent;
1873 + stbl->stsc_ = child;
1878 +static int stbl_add_stsz(void* parent, void* child)
1880 + struct stbl_t* stbl = parent;
1881 + stbl->stsz_ = child;
1886 +static int stbl_add_stco(void* parent, void* child)
1888 + struct stbl_t* stbl = parent;
1889 + stbl->stco_ = child;
1894 +static int stbl_add_ctts(void* parent, void* child)
1896 + struct stbl_t* stbl = parent;
1897 + stbl->ctts_ = child;
1902 +static void* stbl_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
1904 + struct stbl_t* atom = stbl_init();
1906 + struct atom_read_list_t atom_read_list[] = {
1907 + { FOURCC('s', 't', 't', 's'), atom, &stbl_add_stts, &stts_read },
1908 + { FOURCC('s', 't', 's', 's'), atom, &stbl_add_stss, &stss_read },
1909 + { FOURCC('s', 't', 's', 'c'), atom, &stbl_add_stsc, &stsc_read },
1910 + { FOURCC('s', 't', 's', 'z'), atom, &stbl_add_stsz, &stsz_read },
1911 + { FOURCC('s', 't', 'c', 'o'), atom, &stbl_add_stco, &stco_read },
1912 + { FOURCC('c', 'o', '6', '4'), atom, &stbl_add_stco, &co64_read },
1913 + { FOURCC('c', 't', 't', 's'), atom, &stbl_add_ctts, &ctts_read },
1916 + int result = atom_reader(atom_read_list,
1917 + sizeof(atom_read_list) / sizeof(atom_read_list[0]),
1921 + // check for mandatory atoms
1924 + printf("stbl: missing stts\n");
1930 + printf("stbl: missing stsc\n");
1936 + printf("stbl: missing stsz\n");
1942 + printf("stbl: missing stco\n");
1955 +static unsigned char* stbl_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
1957 + struct stbl_t* stbl = atom;
1958 + struct atom_write_list_t atom_write_list[] = {
1959 + { FOURCC('s', 't', 't', 's'), stbl, stbl->stts_, &stts_write },
1960 + { FOURCC('s', 't', 's', 's'), stbl, stbl->stss_, &stss_write },
1961 + { FOURCC('s', 't', 's', 'c'), stbl, stbl->stsc_, &stsc_write },
1962 + { FOURCC('s', 't', 's', 'z'), stbl, stbl->stsz_, &stsz_write },
1963 + { FOURCC('s', 't', 'c', 'o'), stbl, stbl->stco_, &stco_write },
1964 + { FOURCC('c', 't', 't', 's'), stbl, stbl->ctts_, &ctts_write },
1967 + buffer = atom_writer(stbl->unknown_atoms_,
1969 + sizeof(atom_write_list) / sizeof(atom_write_list[0]),
1975 +static unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample)
1977 + // If the sync atom is not present, all samples are implicit sync samples.
1981 + return stss_get_nearest_keyframe(stbl->stss_, sample);
1984 +static struct minf_t* minf_init()
1986 + struct minf_t* atom = (struct minf_t*)malloc(sizeof(struct minf_t));
1987 + atom->unknown_atoms_ = 0;
1994 +static void minf_exit(struct minf_t* atom)
1996 + if(atom->unknown_atoms_)
1998 + unknown_atom_exit(atom->unknown_atoms_);
2002 + vmhd_exit(atom->vmhd_);
2006 + stbl_exit(atom->stbl_);
2011 +static int minf_add_vmhd(void* parent, void* child)
2013 + struct minf_t* minf = parent;
2014 + minf->vmhd_ = child;
2019 +static int minf_add_stbl(void* parent, void* child)
2021 + struct minf_t* minf = parent;
2022 + minf->stbl_ = child;
2027 +static void* minf_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
2029 + struct minf_t* atom = minf_init();
2031 + struct atom_read_list_t atom_read_list[] = {
2032 + { FOURCC('v', 'm', 'h', 'd'), atom, &minf_add_vmhd, &vmhd_read },
2033 + { FOURCC('s', 't', 'b', 'l'), atom, &minf_add_stbl, &stbl_read }
2036 + int result = atom_reader(atom_read_list,
2037 + sizeof(atom_read_list) / sizeof(atom_read_list[0]),
2041 + // check for mandatory atoms
2044 + printf("minf: missing stbl\n");
2057 +static unsigned char* minf_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
2059 + struct minf_t const* minf = atom;
2060 + struct atom_write_list_t atom_write_list[] = {
2061 + { FOURCC('v', 'm', 'h', 'd'), atom, minf->vmhd_, &vmhd_write },
2062 + { FOURCC('s', 't', 'b', 'l'), atom, minf->stbl_, &stbl_write }
2065 + buffer = atom_writer(minf->unknown_atoms_,
2067 + sizeof(atom_write_list) / sizeof(atom_write_list[0]),
2073 +static struct mdia_t* mdia_init()
2075 + struct mdia_t* atom = (struct mdia_t*)malloc(sizeof(struct mdia_t));
2076 + atom->unknown_atoms_ = 0;
2084 +static void mdia_exit(struct mdia_t* atom)
2086 + if(atom->unknown_atoms_)
2088 + unknown_atom_exit(atom->unknown_atoms_);
2092 + mdhd_exit(atom->mdhd_);
2096 + hdlr_exit(atom->hdlr_);
2100 + minf_exit(atom->minf_);
2105 +static int mdia_add_mdhd(void* parent, void* child)
2107 + struct mdia_t* mdia = parent;
2108 + mdia->mdhd_ = child;
2113 +static int mdia_add_hdlr(void* parent, void* child)
2115 + struct mdia_t* mdia = parent;
2116 + mdia->hdlr_ = child;
2121 +static int mdia_add_minf(void* parent, void* child)
2123 + struct mdia_t* mdia = parent;
2124 + mdia->minf_ = child;
2129 +static void* mdia_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
2131 + struct mdia_t* atom = mdia_init();
2133 + struct atom_read_list_t atom_read_list[] = {
2134 + { FOURCC('m', 'd', 'h', 'd'), atom, &mdia_add_mdhd, &mdhd_read },
2135 + { FOURCC('h', 'd', 'l', 'r'), atom, &mdia_add_hdlr, &hdlr_read },
2136 + { FOURCC('m', 'i', 'n', 'f'), atom, &mdia_add_minf, &minf_read }
2139 + int result = atom_reader(atom_read_list,
2140 + sizeof(atom_read_list) / sizeof(atom_read_list[0]),
2144 + // check for mandatory atoms
2147 + printf("mdia: missing mdhd\n");
2153 + printf("mdia: missing hdlr\n");
2159 + printf("mdia: missing minf\n");
2172 +static unsigned char* mdia_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
2174 + struct mdia_t const* mdia = atom;
2175 + struct atom_write_list_t atom_write_list[] = {
2176 + { FOURCC('m', 'd', 'h', 'd'), atom, mdia->mdhd_, &mdhd_write },
2177 + { FOURCC('h', 'd', 'l', 'r'), atom, mdia->hdlr_, &hdlr_write },
2178 + { FOURCC('m', 'i', 'n', 'f'), atom, mdia->minf_, &minf_write }
2181 + buffer = atom_writer(mdia->unknown_atoms_,
2183 + sizeof(atom_write_list) / sizeof(atom_write_list[0]),
2189 +void trak_build_index(struct trak_t* trak)
2191 + struct stco_t const* stco = trak->mdia_->minf_->stbl_->stco_;
2193 + trak->chunks_size_ = stco->entries_;
2194 + trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t));
2198 + for(i = 0; i != trak->chunks_size_; ++i)
2200 + trak->chunks_[i].pos_ = stco->chunk_offsets_[i];
2204 + // process chunkmap:
2206 + struct stsc_t const* stsc = trak->mdia_->minf_->stbl_->stsc_;
2207 + unsigned int last = trak->chunks_size_;
2208 + unsigned int i = stsc->entries_;
2215 + for(j = stsc->table_[i].chunk_; j < last; j++)
2217 + trak->chunks_[j].id_ = stsc->table_[i].id_;
2218 + trak->chunks_[j].size_ = stsc->table_[i].samples_;
2220 + last = stsc->table_[i].chunk_;
2224 + // calc pts of chunks:
2226 + struct stsz_t const* stsz = trak->mdia_->minf_->stbl_->stsz_;
2227 + unsigned int sample_size = stsz->sample_size_;
2228 + unsigned int s = 0;
2231 + for(j = 0; j < trak->chunks_size_; j++)
2233 + trak->chunks_[j].sample_ = s;
2234 + s += trak->chunks_[j].size_;
2238 + if(sample_size == 0)
2240 + trak->samples_size_ = stsz->entries_;
2244 + trak->samples_size_ = s;
2247 + trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t));
2249 + if(sample_size == 0)
2252 + for(i = 0; i != trak->samples_size_ ; ++i)
2253 + trak->samples_[i].size_ = stsz->sample_sizes_[i];
2258 + for(i = 0; i != trak->samples_size_ ; ++i)
2259 + trak->samples_[i].size_ = sample_size;
2264 +// for (j = 0; j < trak->durmap_size; j++)
2265 +// i += trak->durmap[j].num;
2267 +// mp_msg(MSGT_DEMUX, MSGL_WARN,
2268 +// "MOV: durmap and chunkmap sample count differ (%i vs %i)\n", i, s);
2269 +// if (i > s) s = i;
2274 + struct stts_t const* stts = trak->mdia_->minf_->stbl_->stts_;
2275 + unsigned int s = 0;
2276 + unsigned int pts = 0;
2277 + unsigned int entries = stts->entries_;
2279 + for(j = 0; j < entries; j++)
2282 + unsigned int sample_count = stts->table_[j].sample_count_;
2283 + unsigned int sample_duration = stts->table_[j].sample_duration_;
2284 + for(i = 0; i < sample_count; i++)
2286 + trak->samples_[s].pts_ = pts;
2288 + pts += sample_duration;
2293 + // calc composition times:
2295 + struct ctts_t const* ctts = trak->mdia_->minf_->stbl_->ctts_;
2298 + unsigned int s = 0;
2299 + unsigned int entries = ctts->entries_;
2301 + for(j = 0; j != entries; j++)
2304 + unsigned int sample_count = ctts->table_[j].sample_count_;
2305 + unsigned int sample_offset = ctts->table_[j].sample_offset_;
2306 + for(i = 0; i < sample_count; i++)
2308 + trak->samples_[s].cto_ = sample_offset;
2315 + // calc sample offsets
2317 + unsigned int s = 0;
2319 + for(j = 0; j != trak->chunks_size_; j++)
2321 + uint64_t pos = trak->chunks_[j].pos_;
2323 + for(i = 0; i != trak->chunks_[j].size_; i++)
2325 + trak->samples_[s].pos_ = pos;
2326 + pos += trak->samples_[s].size_;
2333 +void trak_update_index(struct trak_t* trak, unsigned int start, unsigned int end)
2335 + // write samples [start,end>
2337 + // stts = [entries * [sample_count, sample_duration]
2339 + struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_;
2341 + unsigned int entries = 0;
2344 + for(s = start; s != end; ++s)
2346 + unsigned int sample_count = 1;
2347 + unsigned int sample_duration =
2348 + trak->samples_[s + 1].pts_ - trak->samples_[s].pts_;
2349 + while(s != end - 1)
2351 + if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration)
2356 + stts->table_[entries].sample_count_ = sample_count;
2357 + stts->table_[entries].sample_duration_ = sample_duration;
2360 + stts->entries_ = entries;
2362 + if(stts_get_samples(stts) != end - start)
2364 + printf("ERROR: stts_get_samples=%d, should be %d\n",
2365 + stts_get_samples(stts), end - start);
2369 + // ctts = [entries * [sample_count, sample_offset]
2371 + struct ctts_t* ctts = trak->mdia_->minf_->stbl_->ctts_;
2374 + unsigned int entries = 0;
2377 + for(s = start; s != end; ++s)
2379 + unsigned int sample_count = 1;
2380 + unsigned int sample_offset = trak->samples_[s].cto_;
2381 + while(s != end - 1)
2383 + if(trak->samples_[s + 1].cto_ != sample_offset)
2389 + ctts->table_[entries].sample_count_ = sample_count;
2390 + ctts->table_[entries].sample_offset_ = sample_offset;
2393 + ctts->entries_ = entries;
2394 + if(ctts_get_samples(ctts) != end - start)
2396 + printf("ERROR: ctts_get_samples=%d, should be %d\n",
2397 + ctts_get_samples(ctts), end - start);
2402 + // process chunkmap:
2404 + struct stsc_t* stsc = trak->mdia_->minf_->stbl_->stsc_;
2407 + for(i = 0; i != trak->chunks_size_; ++i)
2409 + if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start)
2414 + unsigned int stsc_entries = 0;
2415 + unsigned int chunk_start = i;
2416 + unsigned int chunk_end;
2417 + // problem.mp4: reported by Jin-seok Lee. Second track contains no samples
2418 + if(trak->chunks_size_ != 0)
2420 + unsigned int samples =
2421 + trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start;
2422 + unsigned int id = trak->chunks_[i].id_;
2424 + // write entry [chunk,samples,id]
2425 + stsc->table_[stsc_entries].chunk_ = 0;
2426 + stsc->table_[stsc_entries].samples_ = samples;
2427 + stsc->table_[stsc_entries].id_ = id;
2430 + if(i != trak->chunks_size_)
2432 + for(i += 1; i != trak->chunks_size_; ++i)
2434 + if(trak->chunks_[i].sample_ >= end)
2437 + if(trak->chunks_[i].size_ != samples)
2439 + samples = trak->chunks_[i].size_;
2440 + id = trak->chunks_[i].id_;
2442 + stsc->table_[stsc_entries].chunk_ = i - chunk_start;
2443 + stsc->table_[stsc_entries].samples_ = samples;
2444 + stsc->table_[stsc_entries].id_ = id;
2451 + stsc->entries_ = stsc_entries;
2454 + struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_;
2455 + unsigned int entries = 0;
2456 + for(i = chunk_start; i != chunk_end; ++i)
2458 + stco->chunk_offsets_[entries] = stco->chunk_offsets_[i];
2461 + stco->entries_ = entries;
2463 + // patch first chunk with correct sample offset
2464 + stco->chunk_offsets_[0] = (uint32_t)trak->samples_[start].pos_;
2469 + // process sync samples:
2470 + if(trak->mdia_->minf_->stbl_->stss_)
2472 + struct stss_t* stss = trak->mdia_->minf_->stbl_->stss_;
2473 + unsigned int entries = 0;
2474 + unsigned int stss_start;
2477 + for(i = 0; i != stss->entries_; ++i)
2479 + if(stss->sample_numbers_[i] >= start + 1)
2483 + for(; i != stss->entries_; ++i)
2485 + unsigned int sync_sample = stss->sample_numbers_[i];
2486 + if(sync_sample >= end + 1)
2488 + stss->sample_numbers_[entries] = sync_sample - start;
2491 + stss->entries_ = entries;
2494 + // process sample sizes
2496 + struct stsz_t* stsz = trak->mdia_->minf_->stbl_->stsz_;
2498 + if(stsz->sample_size_ == 0)
2500 + unsigned int entries = 0;
2502 + for(i = start; i != end; ++i)
2504 + stsz->sample_sizes_[entries] = stsz->sample_sizes_[i];
2507 + stsz->entries_ = entries;
2512 +static struct trak_t* trak_init()
2514 + struct trak_t* trak = (struct trak_t*)malloc(sizeof(struct trak_t));
2515 + trak->unknown_atoms_ = 0;
2518 + trak->chunks_size_ = 0;
2519 + trak->chunks_ = 0;
2520 + trak->samples_size_ = 0;
2521 + trak->samples_ = 0;
2526 +static void trak_exit(struct trak_t* trak)
2528 + if(trak->unknown_atoms_)
2530 + unknown_atom_exit(trak->unknown_atoms_);
2534 + tkhd_exit(trak->tkhd_);
2538 + mdia_exit(trak->mdia_);
2542 + free(trak->chunks_);
2544 + if(trak->samples_)
2546 + free(trak->samples_);
2551 +static int trak_add_tkhd(void* parent, void* tkhd)
2553 + struct trak_t* trak = parent;
2554 + trak->tkhd_ = tkhd;
2559 +static int trak_add_mdia(void* parent, void* mdia)
2561 + struct trak_t* trak = parent;
2562 + trak->mdia_ = mdia;
2567 +static void* trak_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
2569 + struct trak_t* atom = trak_init();
2571 + struct atom_read_list_t atom_read_list[] = {
2572 + { FOURCC('t', 'k', 'h', 'd'), atom, &trak_add_tkhd, &tkhd_read },
2573 + { FOURCC('m', 'd', 'i', 'a'), atom, &trak_add_mdia, &mdia_read }
2576 + int result = atom_reader(atom_read_list,
2577 + sizeof(atom_read_list) / sizeof(atom_read_list[0]),
2581 + // check for mandatory atoms
2584 + printf("trak: missing tkhd\n");
2590 + printf("trak: missing mdia\n");
2600 + trak_build_index(atom);
2605 +static unsigned char* trak_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
2607 + struct trak_t* trak = atom;
2608 + struct atom_write_list_t atom_write_list[] = {
2609 + { FOURCC('t', 'k', 'h', 'd'), atom, trak->tkhd_, &tkhd_write },
2610 + { FOURCC('m', 'd', 'i', 'a'), atom, trak->mdia_, &mdia_write }
2613 + buffer = atom_writer(trak->unknown_atoms_,
2615 + sizeof(atom_write_list) / sizeof(atom_write_list[0]),
2621 +void trak_shift_offsets(struct trak_t* trak, int64_t offset)
2623 + struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_;
2624 + stco_shift_offsets(stco, (int32_t)offset);
2627 +void trak_shift_offsets_inplace(struct trak_t* trak, int64_t offset)
2629 + void* stco = trak->mdia_->minf_->stbl_->stco_inplace_;
2630 + stco_shift_offsets_inplace(stco, (int32_t)offset);
2633 +static struct mvhd_t* mvhd_init()
2635 + struct mvhd_t* atom = (struct mvhd_t*)malloc(sizeof(struct mvhd_t));
2640 +void mvhd_exit(struct mvhd_t* atom)
2645 +static void* mvhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
2649 + struct mvhd_t* atom = mvhd_init();
2650 + atom->version_ = read_8(buffer + 0);
2651 + atom->flags_ = read_24(buffer + 1);
2652 + if(atom->version_ == 0)
2657 + atom->creation_time_ = read_32(buffer + 4);
2658 + atom->modification_time_ = read_32(buffer + 8);
2659 + atom->timescale_ = read_32(buffer + 12);
2660 + atom->duration_ = read_32(buffer + 16);
2668 + atom->creation_time_ = read_64(buffer + 4);
2669 + atom->modification_time_ = read_64(buffer + 12);
2670 + atom->timescale_ = read_32(buffer + 20);
2671 + atom->duration_ = read_64(buffer + 24);
2674 + atom->rate_ = read_32(buffer + 0);
2675 + atom->volume_ = read_16(buffer + 4);
2676 + atom->reserved1_ = read_16(buffer + 6);
2677 + atom->reserved2_[0] = read_32(buffer + 8);
2678 + atom->reserved2_[1] = read_32(buffer + 12);
2681 + for(i = 0; i != 9; ++i)
2683 + atom->matrix_[i] = read_32(buffer);
2687 + for(i = 0; i != 6; ++i)
2689 + atom->predefined_[i] = read_32(buffer);
2693 + atom->next_track_id_ = read_32(buffer + 0);
2698 +static unsigned char* mvhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer)
2700 + struct mvhd_t const* mvhd = atom;
2703 + buffer = write_8(buffer, mvhd->version_);
2704 + buffer = write_24(buffer, mvhd->flags_);
2706 + if(mvhd->version_ == 0)
2708 + buffer = write_32(buffer, (uint32_t)mvhd->creation_time_);
2709 + buffer = write_32(buffer, (uint32_t)mvhd->modification_time_);
2710 + buffer = write_32(buffer, mvhd->timescale_);
2711 + buffer = write_32(buffer, (uint32_t)mvhd->duration_);
2715 + buffer = write_64(buffer, mvhd->creation_time_);
2716 + buffer = write_64(buffer, mvhd->modification_time_);
2717 + buffer = write_32(buffer, mvhd->timescale_);
2718 + buffer = write_64(buffer, mvhd->duration_);
2721 + buffer = write_32(buffer, mvhd->rate_);
2722 + buffer = write_16(buffer, mvhd->volume_);
2723 + buffer = write_16(buffer, mvhd->reserved1_);
2724 + buffer = write_32(buffer, mvhd->reserved2_[0]);
2725 + buffer = write_32(buffer, mvhd->reserved2_[1]);
2727 + for(i = 0; i != 9; ++i)
2729 + buffer = write_32(buffer, mvhd->matrix_[i]);
2732 + for(i = 0; i != 6; ++i)
2734 + buffer = write_32(buffer, mvhd->predefined_[i]);
2737 + buffer = write_32(buffer, mvhd->next_track_id_);
2742 +static struct moov_t* moov_init()
2744 + struct moov_t* moov = malloc(sizeof(struct moov_t));
2745 + moov->unknown_atoms_ = 0;
2747 + moov->tracks_ = 0;
2752 +static void moov_exit(struct moov_t* atom)
2755 + if(atom->unknown_atoms_)
2757 + unknown_atom_exit(atom->unknown_atoms_);
2761 + mvhd_exit(atom->mvhd_);
2763 + for(i = 0; i != atom->tracks_; ++i)
2765 + trak_exit(atom->traks_[i]);
2770 +static int moov_add_mvhd(void* parent, void* mvhd)
2772 + struct moov_t* moov = parent;
2773 + moov->mvhd_ = mvhd;
2778 +static int moov_add_trak(void* parent, void* child)
2780 + struct moov_t* moov = parent;
2781 + struct trak_t* trak = child;
2782 + if(moov->tracks_ == MAX_TRACKS)
2788 + if(trak->mdia_->hdlr_->handler_type_ != FOURCC('v', 'i', 'd', 'e') &&
2789 + trak->mdia_->hdlr_->handler_type_ != FOURCC('s', 'o', 'u', 'n'))
2791 + printf("Trak ignored (handler_type=%c%c%c%c, name=%s)\n",
2792 + trak->mdia_->hdlr_->handler_type_ >> 24,
2793 + trak->mdia_->hdlr_->handler_type_ >> 16,
2794 + trak->mdia_->hdlr_->handler_type_ >> 8,
2795 + trak->mdia_->hdlr_->handler_type_,
2796 + trak->mdia_->hdlr_->name_);
2798 + return 1; // continue
2801 + moov->traks_[moov->tracks_] = trak;
2807 +static void* moov_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size)
2809 + struct moov_t* atom = moov_init();
2811 + struct atom_read_list_t atom_read_list[] = {
2812 + { FOURCC('m', 'v', 'h', 'd'), atom, &moov_add_mvhd, &mvhd_read },
2813 + { FOURCC('t', 'r', 'a', 'k'), atom, &moov_add_trak, &trak_read }
2816 + int result = atom_reader(atom_read_list,
2817 + sizeof(atom_read_list) / sizeof(atom_read_list[0]),
2821 + // check for mandatory atoms
2824 + printf("moov: missing mvhd\n");
2828 + if(!atom->tracks_)
2830 + printf("moov: missing trak\n");
2843 +static void moov_write(struct moov_t* atom, unsigned char* buffer)
2847 + unsigned char* atom_start = buffer;
2849 + struct atom_write_list_t atom_write_list[] = {
2850 + { FOURCC('m', 'v', 'h', 'd'), atom, atom->mvhd_, &mvhd_write },
2857 + buffer = write_32(buffer, FOURCC('m', 'o', 'o', 'v'));
2859 + buffer = atom_writer(atom->unknown_atoms_,
2861 + sizeof(atom_write_list) / sizeof(atom_write_list[0]),
2864 + for(i = 0; i != atom->tracks_; ++i)
2866 + struct atom_write_list_t trak_atom_write_list[] = {
2867 + { FOURCC('t', 'r', 'a', 'k'), atom, atom->traks_[i], &trak_write },
2869 + buffer = atom_writer(0,
2870 + trak_atom_write_list,
2871 + sizeof(trak_atom_write_list) / sizeof(trak_atom_write_list[0]),
2874 + write_32(atom_start, buffer - atom_start);
2877 +void moov_shift_offsets(struct moov_t* moov, int64_t offset)
2880 + for(i = 0; i != moov->tracks_; ++i)
2882 + trak_shift_offsets(moov->traks_[i], offset);
2886 +void moov_shift_offsets_inplace(struct moov_t* moov, int64_t offset)
2889 + for(i = 0; i != moov->tracks_; ++i)
2891 + trak_shift_offsets_inplace(moov->traks_[i], offset);
2895 +unsigned int moov_seek(unsigned char* moov_data,
2896 + uint64_t* moov_size,
2899 + uint64_t* mdat_start,
2900 + uint64_t* mdat_size,
2902 + int client_is_flash)
2904 + struct moov_t* moov = moov_read(NULL, moov_data + ATOM_PREAMBLE_SIZE,
2905 + *moov_size - ATOM_PREAMBLE_SIZE);
2907 + if(moov == 0 || moov->mvhd_ == 0)
2909 + printf("Error parsing moov header\n");
2914 + long moov_time_scale = moov->mvhd_->timescale_;
2915 + unsigned int start = (unsigned int)(start_time * moov_time_scale);
2916 + unsigned int end = (unsigned int)(end_time * moov_time_scale);
2917 + uint64_t skip_from_start = UINT64_MAX;
2918 + uint64_t end_offset = 0;
2920 + unsigned int pass;
2922 + // for every trak, convert seconds to sample (time-to-sample).
2923 + // adjust sample to keyframe
2924 + unsigned int trak_sample_start[MAX_TRACKS];
2925 + unsigned int trak_sample_end[MAX_TRACKS];
2927 + uint64_t moov_duration = 0;
2929 + // clayton.mp4 has a third track with one sample that lasts the whole clip.
2930 + // Assuming the first two tracks are the audio and video track, we patch
2931 + // the remaining tracks to 'free' atoms.
2932 +// if(moov->tracks_ > 2)
2934 +// for(i = 2; i != moov->tracks_; ++i)
2936 +// // patch 'trak' to 'free'
2937 +// unsigned char* p = moov->traks_[i].start_ - 4;
2943 +// moov->tracks_ = 2;
2946 + // reported by everwanna:
2947 + // av out of sync because:
2948 + // audio track 0 without stss, seek to the exact time.
2949 + // video track 1 with stss, seek to the nearest key frame time.
2952 + // first pass we get the new aligned times for traks with an stss present
2953 + // second pass is for traks without an stss
2954 + for(pass = 0; pass != 2; ++pass)
2956 + for(i = 0; i != moov->tracks_; ++i)
2958 + struct trak_t* trak = moov->traks_[i];
2959 + struct stbl_t* stbl = trak->mdia_->minf_->stbl_;
2960 + long trak_time_scale = trak->mdia_->mdhd_->timescale_;
2961 + float moov_to_trak_time = (float)trak_time_scale / (float)moov_time_scale;
2962 + float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
2964 + // 1st pass: stss present, 2nd pass: no stss present
2965 + if(pass == 0 && !stbl->stss_)
2967 + if(pass == 1 && stbl->stss_)
2970 + // ignore empty track
2971 + if(trak->mdia_->mdhd_->duration_ == 0)
2977 + trak_sample_start[i] = start;
2981 + start = stts_get_sample(stbl->stts_, (uint64_t)(start * moov_to_trak_time));
2982 + printf("start=%u (trac time)=%.2f (seconds)", start,
2983 + stts_get_time(stbl->stts_, start) / (float)trak_time_scale);
2984 + start = stbl_get_nearest_keyframe(stbl, start + 1) - 1;
2985 + printf("=%u (zero based keyframe)", start);
2986 + trak_sample_start[i] = start;
2987 + start = (unsigned int)(stts_get_time(stbl->stts_, start) * trak_to_moov_time);
2988 + printf("=%u (moov time)\n", start);
2994 + trak_sample_end[i] = trak->samples_size_;
2998 + end = stts_get_sample(stbl->stts_, (uint64_t)(end * moov_to_trak_time));
2999 + if(end >= trak->samples_size_)
3001 + end = trak->samples_size_;
3005 + end = stbl_get_nearest_keyframe(stbl, end + 1) - 1;
3007 + trak_sample_end[i] = end;
3008 + printf("endframe=%u, samples_size_=%u\n", end, trak->samples_size_);
3009 + end = (unsigned int)(stts_get_time(stbl->stts_, end) * trak_to_moov_time);
3014 + printf("start=%u\n", start);
3015 + printf("end=%u\n", end);
3017 + if(end && start >= end)
3023 + for(i = 0; i != moov->tracks_; ++i)
3025 + struct trak_t* trak = moov->traks_[i];
3026 + struct stbl_t* stbl = trak->mdia_->minf_->stbl_;
3028 + unsigned int start_sample = trak_sample_start[i];
3029 + unsigned int end_sample = trak_sample_end[i];
3031 + // ignore empty track
3032 + if(trak->mdia_->mdhd_->duration_ == 0)
3035 + trak_update_index(trak, start_sample, end_sample);
3039 + trak->samples_[start_sample].pos_ - trak->samples_[0].pos_;
3040 + if(skip < skip_from_start)
3041 + skip_from_start = skip;
3042 + printf("Trak can skip %llu bytes\n", skip);
3044 + if(end_sample != trak->samples_size_)
3046 + uint64_t end_pos = trak->samples_[end_sample].pos_;
3047 + if(end_pos > end_offset)
3048 + end_offset = end_pos;
3049 + printf("New endpos=%llu\n", end_pos);
3050 + printf("Trak can skip %llu bytes at end\n",
3051 + *mdat_start + *mdat_size - end_offset);
3056 + // fixup trak (duration)
3057 + uint64_t trak_duration = stts_get_duration(stbl->stts_);
3058 + long trak_time_scale = trak->mdia_->mdhd_->timescale_;
3059 + float trak_to_moov_time = (float)moov_time_scale / (float)trak_time_scale;
3061 + uint64_t duration = (long)((float)trak_duration * trak_to_moov_time);
3062 + trak->mdia_->mdhd_->duration_= trak_duration;
3063 + trak->tkhd_->duration_ = duration;
3064 + printf("trak: new_duration=%lld\n", duration);
3066 + if(duration > moov_duration)
3067 + moov_duration = duration;
3071 +// printf("stco.size=%d, ", read_int32(stbl->stco_ + 4));
3072 +// printf("stts.size=%d samples=%d\n", read_int32(stbl->stts_ + 4), stts_get_samples(stbl->stts_));
3073 +// printf("stsz.size=%d\n", read_int32(stbl->stsz_ + 8));
3074 +// printf("stsc.samples=%d\n", stsc_get_samples(stbl->stsc_));
3076 + moov->mvhd_->duration_ = moov_duration;
3078 + // subtract bytes we skip at the front of the mdat atom
3079 + offset -= skip_from_start;
3081 + // subtract old moov size
3082 + offset -= *moov_size;
3084 + printf("moov: writing header\n");
3086 + moov_write(moov, moov_data);
3087 + *moov_size = read_32(moov_data);
3089 + // add new moov size
3090 + offset += *moov_size;
3092 + printf("shifting offsets by %lld\n", offset);
3093 + moov_shift_offsets_inplace(moov, offset);
3095 +// moov_write(moov, moov_data);
3097 +#ifdef COMPRESS_MOOV_ATOM
3098 + if(!client_is_flash)
3100 + uLong sourceLen = *moov_size - ATOM_PREAMBLE_SIZE;
3101 + uLong destLen = compressBound(sourceLen);
3102 + unsigned char* cmov = malloc(destLen);
3103 + int zstatus = compress(cmov, &destLen, moov_data, sourceLen);
3104 + if(zstatus == Z_OK)
3106 + printf("cmov size = %lu (%ld%%)\n", destLen, 100 * destLen / sourceLen);
3110 + const int extra_space = 4096;
3111 + if(destLen + extra_space < sourceLen)
3113 + const int bytes_saved = sourceLen - destLen;
3116 + printf("shifting offsets by %d\n", -bytes_saved);
3117 + moov_shift_offsets_inplace(moov, -bytes_saved);
3119 + extra += ATOM_PREAMBLE_SIZE + 4; // dcom
3120 + extra += ATOM_PREAMBLE_SIZE + 4; // cmvd
3121 + extra += ATOM_PREAMBLE_SIZE; // cmov
3122 + extra += ATOM_PREAMBLE_SIZE + extra_space; // free
3124 + printf("shifting offsets by %d\n", extra);
3125 + moov_shift_offsets_inplace(moov, extra);
3128 + destLen2 = compressBound(sourceLen);
3129 + zstatus = compress(cmov, &destLen2, moov_data, sourceLen);
3130 + if(zstatus == Z_OK)
3132 + printf("cmov size = %lu (%ld%%)\n", destLen2, 100 * destLen2 / sourceLen);
3134 + if(destLen2 < destLen + extra_space)
3136 + // copy compressed movie atom
3137 + unsigned char* outbuffer = moov_data;
3139 + uint32_t dcom_size = ATOM_PREAMBLE_SIZE + 4;
3140 + uint32_t cmvd_size = ATOM_PREAMBLE_SIZE + 4 + destLen2;
3141 + uint32_t cmov_size = ATOM_PREAMBLE_SIZE + dcom_size + cmvd_size;
3142 + uint32_t free_size = ATOM_PREAMBLE_SIZE + extra_space + destLen - destLen2;
3143 + *moov_size = ATOM_PREAMBLE_SIZE + cmov_size + free_size;
3145 + outbuffer = write_32(outbuffer, (uint32_t)*moov_size);
3150 + outbuffer = write_32(outbuffer, cmov_size);
3152 + outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'o', 'v'));
3153 + outbuffer = write_32(outbuffer, dcom_size);
3154 + outbuffer = write_32(outbuffer, FOURCC('d', 'c', 'o', 'm'));
3155 + outbuffer = write_32(outbuffer, FOURCC('z', 'l', 'i', 'b'));
3157 + outbuffer = write_32(outbuffer, cmvd_size);
3159 + outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'v', 'd'));
3160 + outbuffer = write_32(outbuffer, sourceLen);
3161 + memcpy(outbuffer, cmov, destLen2);
3162 + outbuffer += destLen2;
3166 + // add final padding
3167 + outbuffer = write_32(outbuffer, free_size);
3168 + outbuffer = write_32(outbuffer, FOURCC('f', 'r', 'e', 'e'));
3170 + const char free_bytes[8] =
3172 + 'C', 'o', 'd', 'e','S','h', 'o', 'p'
3174 + uint32_t padding_index;
3175 + for(padding_index = ATOM_PREAMBLE_SIZE; padding_index != free_size; ++padding_index)
3177 + outbuffer[padding_index] = free_bytes[padding_index % 8];
3183 + printf("2nd pass compress overflow\n");
3192 + *mdat_start += skip_from_start;
3193 + if(end_offset != 0)
3195 + *mdat_size = end_offset;
3197 + *mdat_size -= skip_from_start;
3205 +////////////////////////////////////////////////////////////////////////////////
3210 + uint32_t short_size_;
3216 +static int mp4_atom_read_header(FILE* infile, struct mp4_atom_t* atom)
3218 + unsigned char atom_header[8];
3220 + atom->start_ = ftell(infile);
3221 + fread(atom_header, 8, 1, infile);
3222 + atom->short_size_ = read_32(&atom_header[0]);
3223 + atom->type_ = read_32(&atom_header[4]);
3225 + if(atom->short_size_ == 1)
3227 + fread(atom_header, 8, 1, infile);
3228 + atom->size_ = read_64(&atom_header[0]);
3232 + atom->size_ = atom->short_size_;
3235 + atom->end_ = atom->start_ + atom->size_;
3240 +static int mp4_atom_write_header(unsigned char* outbuffer, struct mp4_atom_t* atom)
3242 + int write_box64 = atom->short_size_ == 1 ? 1 : 0;
3245 + write_32(outbuffer, 1);
3247 + write_32(outbuffer, (uint32_t)atom->size_);
3249 + write_32(outbuffer + 4, atom->type_);
3253 + write_64(outbuffer + 8, atom->size_);
3262 +int mp4_split(const char* filename, int64_t filesize,
3263 + float start_time, float end_time,
3264 + void** mp4_header, uint32_t* mp4_header_size,
3265 + uint64_t* mdat_offset, uint64_t* mdat_size,
3266 + int client_is_flash)
3269 + struct mp4_atom_t ftyp_atom;
3270 + struct mp4_atom_t moov_atom;
3271 + struct mp4_atom_t mdat_atom;
3272 + unsigned char* moov_data = 0;
3273 + unsigned char* buffer;
3274 + uint64_t new_mdat_start;
3277 + memset(&ftyp_atom, 0, sizeof(ftyp_atom));
3278 + memset(&moov_atom, 0, sizeof(moov_atom));
3279 + memset(&mdat_atom, 0, sizeof(mdat_atom));
3281 + infile = fopen(filename, "rb");
3282 + if(infile == NULL)
3287 + while(ftello(infile) < filesize)
3289 + struct mp4_atom_t leaf_atom;
3291 + if(!mp4_atom_read_header(infile, &leaf_atom))
3294 + printf("Atom(%c%c%c%c,%lld)\n",
3295 + leaf_atom.type_ >> 24, leaf_atom.type_ >> 16,
3296 + leaf_atom.type_ >> 8, leaf_atom.type_,
3299 + switch(leaf_atom.type_)
3301 + case FOURCC('f', 't', 'y', 'p'):
3302 + ftyp_atom = leaf_atom;
3304 + case FOURCC('m', 'o', 'o', 'v'):
3305 + moov_atom = leaf_atom;
3306 + moov_data = (unsigned char*)malloc((size_t)moov_atom.size_);
3307 + fseeko(infile, moov_atom.start_, SEEK_SET);
3308 + fread(moov_data, (off_t)moov_atom.size_, 1, infile);
3310 + case FOURCC('m', 'd', 'a', 't'):
3311 + mdat_atom = leaf_atom;
3314 + fseeko(infile, leaf_atom.end_, SEEK_SET);
3317 + if(moov_atom.size_ == 0)
3319 + printf("Error: moov atom not found\n");
3324 + if(mdat_atom.size_ == 0)
3326 + printf("Error: mdat atom not found\n");
3331 + buffer = (unsigned char*)malloc((uint32_t)moov_atom.size_ + 4 * 1024);
3332 + *mp4_header = buffer;
3334 + if(ftyp_atom.size_)
3336 + fseeko(infile, ftyp_atom.start_, SEEK_SET);
3337 + fread(buffer, (off_t)ftyp_atom.size_, 1, infile);
3338 + buffer += ftyp_atom.size_;
3342 + static char const free_data[] = {
3343 + 0x0, 0x0, 0x0, 42, 'f', 'r', 'e', 'e',
3344 + 'v', 'i', 'd', 'e', 'o', ' ', 's', 'e',
3345 + 'r', 'v', 'e', 'd', ' ', 'b', 'y', ' ',
3346 + 'm', 'o', 'd', '_', 'h', '2', '6', '4',
3347 + '_', 's', 't', 'r', 'e', 'a', 'm', 'i',
3350 + memcpy(buffer, free_data, sizeof(free_data));
3351 + buffer += sizeof(free_data);
3354 + new_mdat_start = buffer - (unsigned char*)(*mp4_header) + moov_atom.size_;
3355 + if(!moov_seek(moov_data,
3359 + &mdat_atom.start_,
3361 + new_mdat_start - mdat_atom.start_,
3369 + memcpy(buffer, moov_data, (uint32_t)moov_atom.size_);
3370 + buffer += moov_atom.size_;
3374 + int mdat_header_size = mp4_atom_write_header(buffer, &mdat_atom);
3375 + buffer += mdat_header_size;
3376 + *mdat_offset = mdat_atom.start_ + mdat_header_size;
3377 + *mdat_size = mdat_atom.size_ - mdat_header_size;
3380 + *mp4_header_size = (uint32_t)(buffer - (unsigned char*)(*mp4_header));
3389 --- /dev/null 2008-11-04 20:33:38.146691408 +0200
3390 +++ lighttpd-1.4.18/src/moov.h 2009-01-26 21:00:05.071936866 +0200
3392 +/*******************************************************************************
3393 + moov.h (version 2)
3395 + moov - A library for splitting Quicktime/MPEG4 files.
3396 + http://h264.code-shop.com
3398 + Copyright (C) 2007-2009 CodeShop B.V.
3401 + The H264 Streaming Module is licened under a Creative Common License. It allows
3402 + you to use, modify and redistribute the module, but only for *noncommercial*
3403 + purposes. For corporate use, please apply for a commercial license.
3405 + Creative Commons License:
3406 + http://creativecommons.org/licenses/by-nc-sa/3.0/
3408 + Commercial License:
3409 + http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2
3410 +******************************************************************************/
3412 +// NOTE: don't include stdio.h (for FILE) or sys/types.h (for off_t).
3413 +// nginx redefines _FILE_OFFSET_BITS and off_t will have different sizes
3414 +// depending on include order
3417 +#include <inttypes.h>
3419 +#include "inttypes.h"
3426 +extern int mp4_split(const char* infile, int64_t filesize,
3427 + float start_time, float end_time,
3428 + void** mp4_header, uint32_t* mp4_header_size,
3429 + uint64_t* mdat_offset, uint64_t* mdat_size,
3430 + int client_is_flash);
3432 +/* Returns true when the test string is a prefix of the input */
3433 +extern int starts_with(const char* input, const char* test);
3436 +} /* extern C definitions */