]> git.pld-linux.org Git - packages/lighttpd.git/blame - lighttpd-mod_h264_streaming.patch
- skip configure.in chunk for version bump
[packages/lighttpd.git] / lighttpd-mod_h264_streaming.patch
CommitLineData
cdbbc5bb
ER
1diff -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
4@@ -77,6 +77,11 @@
5 mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
6 mod_flv_streaming_la_LIBADD = $(common_libadd)
7
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)
12+
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
16diff -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
19@@ -0,0 +1,467 @@
20+/*******************************************************************************
21+ mod_h264_streaming.c
22+
23+ mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files.
24+ http://h264.code-shop.com
25+
26+ Copyright (C) 2007 CodeShop B.V.
27+
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.
32+
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.
37+
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+******************************************************************************/
41+
42+#include <ctype.h>
43+#include <stdlib.h>
44+#include <string.h>
45+#include <stdio.h>
46+
47+#include "base.h"
48+#include "log.h"
49+#include "buffer.h"
50+#include "response.h"
51+#include "http_chunk.h"
52+#include "stat_cache.h"
53+
54+#include "plugin.h"
55+
56+#ifdef HAVE_CONFIG_H
57+#include "config.h"
58+#endif
59+
60+/* plugin config for all request/connections */
61+
62+typedef struct {
63+ array *extensions;
64+} plugin_config;
65+
66+typedef struct {
67+ PLUGIN_DATA;
68+
69+ buffer *query_str;
70+ array *get_params;
71+
72+ plugin_config **config_storage;
73+
74+ plugin_config conf;
75+} plugin_data;
76+
77+/* init the plugin data */
78+INIT_FUNC(mod_h264_streaming_init) {
79+ plugin_data *p;
80+
81+ p = calloc(1, sizeof(*p));
82+
83+ p->query_str = buffer_init();
84+ p->get_params = array_init();
85+
86+ return p;
87+}
88+
89+/* detroy the plugin data */
90+FREE_FUNC(mod_h264_streaming_free) {
91+ plugin_data *p = p_d;
92+
93+ UNUSED(srv);
94+
95+ if (!p) return HANDLER_GO_ON;
96+
97+ if (p->config_storage) {
98+ size_t i;
99+
100+ for (i = 0; i < srv->config_context->used; i++) {
101+ plugin_config *s = p->config_storage[i];
102+
103+ if (!s) continue;
104+
105+ array_free(s->extensions);
106+
107+ free(s);
108+ }
109+ free(p->config_storage);
110+ }
111+
112+ buffer_free(p->query_str);
113+ array_free(p->get_params);
114+
115+ free(p);
116+
117+ return HANDLER_GO_ON;
118+}
119+
120+/* handle plugin config and check values */
121+
122+SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) {
123+ plugin_data *p = p_d;
124+ size_t i = 0;
125+
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 }
129+ };
130+
131+ if (!p) return HANDLER_ERROR;
132+
133+ p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
134+
135+ for (i = 0; i < srv->config_context->used; i++) {
136+ plugin_config *s;
137+
138+ s = calloc(1, sizeof(plugin_config));
139+ s->extensions = array_init();
140+
141+ cv[0].destination = s->extensions;
142+
143+ p->config_storage[i] = s;
144+
145+ if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
146+ return HANDLER_ERROR;
147+ }
148+ }
149+
150+ return HANDLER_GO_ON;
151+}
152+
153+#define PATCH(x) \
154+ p->conf.x = s->x;
155+static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) {
156+ size_t i, j;
157+ plugin_config *s = p->config_storage[0];
158+
159+ PATCH(extensions);
160+
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];
165+
166+ /* condition didn't match */
167+ if (!config_check_cond(srv, con, dc)) continue;
168+
169+ /* merge config */
170+ for (j = 0; j < dc->value->used; j++) {
171+ data_unset *du = dc->value->data[j];
172+
173+ if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) {
174+ PATCH(extensions);
175+ }
176+ }
177+ }
178+
179+ return 0;
180+}
181+#undef PATCH
182+
183+static int split_get_params(array *get_params, buffer *qrystr) {
184+ size_t is_key = 1;
185+ size_t i;
186+ char *key = NULL, *val = NULL;
187+
188+ key = qrystr->ptr;
189+
190+ /* we need the \0 */
191+ for (i = 0; i < qrystr->used; i++) {
192+ switch(qrystr->ptr[i]) {
193+ case '=':
194+ if (is_key) {
195+ val = qrystr->ptr + i + 1;
196+
197+ qrystr->ptr[i] = '\0';
198+
199+ is_key = 0;
200+ }
201+
202+ break;
203+ case '&':
204+ case '\0': /* fin symbol */
205+ if (!is_key) {
206+ data_string *ds;
207+ /* we need at least a = since the last & */
208+
209+ /* terminate the value */
210+ qrystr->ptr[i] = '\0';
211+
212+ if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) {
213+ ds = data_string_init();
214+ }
215+ buffer_copy_string_len(ds->key, key, strlen(key));
216+ buffer_copy_string_len(ds->value, val, strlen(val));
217+
218+ array_insert_unique(get_params, (data_unset *)ds);
219+ }
220+
221+ key = qrystr->ptr + i + 1;
222+ val = NULL;
223+ is_key = 1;
224+ break;
225+ }
226+ }
227+
228+ return 0;
229+}
230+
231+extern unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
232+ float start_time,
233+ unsigned int* mdat_start, unsigned int* mdat_size,
234+ unsigned int offset);
235+
236+void write_char(unsigned char* outbuffer, int value)
237+{
238+ outbuffer[0] = (unsigned char)(value);
239+}
240+
241+void write_int32(unsigned char* outbuffer, long value)
242+{
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);
247+}
248+
249+struct atom_t
250+{
251+ unsigned char type_[4];
252+ unsigned int size_;
253+ unsigned int start_;
254+ unsigned int end_;
255+};
256+
257+#define ATOM_PREAMBLE_SIZE 8
258+
259+unsigned int atom_header_size(unsigned char* atom_bytes)
260+{
261+ return (atom_bytes[0] << 24) +
262+ (atom_bytes[1] << 16) +
263+ (atom_bytes[2] << 8) +
264+ (atom_bytes[3]);
265+}
266+
267+int atom_read_header(FILE* infile, struct atom_t* atom)
268+{
269+ unsigned char atom_bytes[ATOM_PREAMBLE_SIZE];
270+
271+ atom->start_ = ftell(infile);
272+
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_;
277+
278+ return 1;
279+}
280+
281+void atom_write_header(unsigned char* outbuffer, struct atom_t* atom)
282+{
283+ int i;
284+ write_int32(outbuffer, atom->size_);
285+ for(i = 0; i != 4; ++i)
286+ write_char(outbuffer + 4 + i, atom->type_[i]);
287+}
288+
289+int atom_is(struct atom_t const* atom, const char* type)
290+{
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])
295+ ;
296+}
297+
298+void atom_skip(FILE* infile, struct atom_t const* atom)
299+{
300+ fseek(infile, atom->end_, SEEK_SET);
301+}
302+
303+void atom_print(struct atom_t const* atom)
304+{
305+ printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
306+ atom->type_[2], atom->type_[3], atom->size_);
307+}
308+
309+
310+URIHANDLER_FUNC(mod_h264_streaming_path_handler) {
311+ plugin_data *p = p_d;
312+ int s_len;
313+ size_t k;
314+
315+ UNUSED(srv);
316+
317+ if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
318+
319+ mod_h264_streaming_patch_connection(srv, con, p);
320+
321+ s_len = con->physical.path->used - 1;
322+
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;
326+
327+ if (ct_len > s_len) continue;
328+ if (ds->value->used == 0) continue;
329+
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;
333+ buffer *b;
334+ double start;
335+ char *err = NULL;
336+ /* if there is a start=[0-9]+ in the header use it as start,
337+ * otherwise send the full file */
338+
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);
342+
343+ if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
344+ return HANDLER_GO_ON;
345+ }
346+
347+ /* too short */
348+// if (get_param->value->used < 2) return HANDLER_GO_ON;
349+
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;
355+ }
356+
357+// if (start <= 0) return HANDLER_GO_ON;
358+ if (start < 0) return HANDLER_GO_ON;
359+
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;
363+ }
364+
365+// if (start > sce->st.st_size) {
366+// return HANDLER_GO_ON;
367+// }
368+
369+ /* we are safe now, let's build a h264 header */
370+ b = chunkqueue_get_append_buffer(con->write_queue);
371+#if 0
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);
374+#else
375+ {
376+ FILE* infile;
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;
382+
383+ infile = fopen(con->physical.path->ptr, "rb");
384+ if(!infile) {
385+ return HANDLER_GO_ON;
386+ }
387+
388+ {
389+ unsigned int filesize = sce->st.st_size;
390+
391+ struct atom_t leaf_atom;
392+ while(ftell(infile) < filesize)
393+ {
394+ if(!atom_read_header(infile, &leaf_atom))
395+ break;
396+
397+ atom_print(&leaf_atom);
398+
399+ if(atom_is(&leaf_atom, "ftyp"))
400+ {
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);
405+ }
406+ else
407+ if(atom_is(&leaf_atom, "moov"))
408+ {
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);
413+ }
414+ else
415+ if(atom_is(&leaf_atom, "mdat"))
416+ {
417+ mdat_atom = leaf_atom;
418+ }
419+ atom_skip(infile, &leaf_atom);
420+ }
421+ }
422+ fclose(infile);
423+
424+ if(!moov_data)
425+ return HANDLER_GO_ON;
426+
427+ {
428+
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,
432+ start,
433+ &mdat_atom.start_, &mdat_atom.size_,
434+ mdat_start - mdat_atom.start_))
435+ return HANDLER_GO_ON;
436+
437+ if(ftyp_data)
438+ {
439+ buffer_append_memory(b, ftyp_data, ftyp_atom.size_);
440+ free(ftyp_data);
441+ }
442+
443+ buffer_append_memory(b, moov_data, moov_atom.size_);
444+ free(moov_data);
445+
446+ {
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 */
452+ }
453+
454+ http_chunk_append_file(srv, con, con->physical.path, mdat_atom.start_ + ATOM_PREAMBLE_SIZE,
455+ mdat_atom.size_ - ATOM_PREAMBLE_SIZE);
456+ }
457+ }
458+#endif
459+ response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4"));
460+
461+ con->file_finished = 1;
462+
463+ return HANDLER_FINISHED;
464+ }
465+ }
466+
467+ /* not found */
468+ return HANDLER_GO_ON;
469+}
470+
471+/* this function is called at dlopen() time and inits the callbacks */
472+
473+int mod_h264_streaming_plugin_init(plugin *p) {
474+ p->version = LIGHTTPD_VERSION_ID;
475+ p->name = buffer_init_string("h264_streaming");
476+
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;
481+
482+ p->data = NULL;
483+
484+ return 0;
485+}
486+
487diff -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
490@@ -0,0 +1,1188 @@
491+/******************************************************************************
492+ moov.c
493+
494+ moov - A library for splitting Quicktime/MPEG4 files.
495+ http://h264.code-shop.com
496+
497+ Copyright (C) 2007 CodeShop B.V.
498+
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.
503+
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.
508+
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+******************************************************************************/
512+
513+/*
514+ Uses code snippets from the libquicktime library:
515+ http://libquicktime.sourceforge.net
516+
517+ The QuickTime File Format PDF from Apple:
518+ http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf
519+*/
520+
521+#include <stdlib.h>
522+#include <stdio.h>
523+#include <string.h>
524+#include <limits.h>
525+#include <inttypes.h>
526+
527+static int read_char(unsigned char const* buffer)
528+{
529+ return buffer[0];
530+}
531+
532+static int read_int32(void const* buffer)
533+{
534+ unsigned char* p = (unsigned char*)buffer;
535+ return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
536+}
537+
538+static void write_int32(void* outbuffer, uint32_t value)
539+{
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);
545+}
546+
547+struct atom_t
548+{
549+ unsigned char type_[4];
550+ unsigned int size_;
551+ unsigned char* start_;
552+ unsigned char* end_;
553+};
554+
555+#define ATOM_PREAMBLE_SIZE 8
556+
557+static unsigned int atom_header_size(unsigned char* atom_bytes)
558+{
559+ return (atom_bytes[0] << 24) +
560+ (atom_bytes[1] << 16) +
561+ (atom_bytes[2] << 8) +
562+ (atom_bytes[3]);
563+}
564+
565+static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom)
566+{
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_;
571+
572+ return buffer + ATOM_PREAMBLE_SIZE;
573+}
574+
575+static unsigned char* atom_skip(struct atom_t const* atom)
576+{
577+ return atom->end_;
578+}
579+
580+static int atom_is(struct atom_t const* atom, const char* type)
581+{
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])
586+ ;
587+}
588+
589+static void atom_print(struct atom_t const* atom)
590+{
591+ printf("Atom(%c%c%c%c,%d)\n", atom->type_[0], atom->type_[1],
592+ atom->type_[2], atom->type_[3], atom->size_);
593+}
594+
595+#define MAX_TRACKS 8
596+
597+unsigned int stts_get_entries(unsigned char const* stts)
598+{
599+ return read_int32(stts + 4);
600+}
601+
602+void stts_get_sample_count_and_duration(unsigned char const* stts,
603+ unsigned int idx, unsigned int* sample_count, unsigned int* sample_duration)
604+{
605+ unsigned char const* table = stts + 8 + idx * 8;
606+ *sample_count = read_int32(table);
607+ *sample_duration = read_int32(table + 4);
608+}
609+
610+struct stts_table_t
611+{
612+ uint32_t sample_count_;
613+ uint32_t sample_duration_;
614+};
615+
616+unsigned int ctts_get_entries(unsigned char const* ctts)
617+{
618+ return read_int32(ctts + 4);
619+}
620+
621+void ctts_get_sample_count_and_offset(unsigned char const* ctts,
622+ unsigned int idx, unsigned int* sample_count, unsigned int* sample_offset)
623+{
624+ unsigned char const* table = ctts + 8 + idx * 8;
625+ *sample_count = read_int32(table);
626+ *sample_offset = read_int32(table + 4);
627+}
628+
629+unsigned int ctts_get_samples(unsigned char const* ctts)
630+{
631+ long samples = 0;
632+ long entries = ctts_get_entries(ctts);
633+ int i;
634+ for(i = 0; i != entries; ++i)
635+ {
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;
640+ }
641+
642+ return samples;
643+}
644+
645+struct ctts_table_t
646+{
647+ uint32_t sample_count_;
648+ uint32_t sample_offset_;
649+};
650+
651+struct stsc_table_t
652+{
653+ uint32_t chunk_;
654+ uint32_t samples_;
655+ uint32_t id_;
656+};
657+
658+unsigned int stsc_get_entries(unsigned char const* stsc)
659+{
660+ return read_int32(stsc + 4);
661+}
662+
663+void stsc_get_table(unsigned char const* stsc, unsigned int i, struct stsc_table_t *stsc_table)
664+{
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_);
669+}
670+
671+unsigned int stsc_get_chunk(unsigned char* stsc, unsigned int sample)
672+{
673+ unsigned int entries = read_int32(stsc + 4);
674+ struct stsc_table_t* table = (struct stsc_table_t*)(stsc + 8);
675+
676+ if(entries == 0)
677+ {
678+ return 0;
679+ }
680+ else
681+// if(entries == 1)
682+// {
683+// unsigned int table_samples = read_int32(&table[0].samples_);
684+// unsigned int chunk = (sample + 1) / table_samples;
685+// return chunk - 1;
686+// }
687+// else
688+ {
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;
694+
695+ do
696+ {
697+ unsigned int range_samples;
698+ unsigned int chunk2 = read_int32(&table[chunk2entry].chunk_);
699+ chunk = chunk2 - chunk1;
700+ range_samples = chunk * chunk1samples;
701+
702+ if(sample < total + range_samples)
703+ break;
704+
705+ chunk1samples = read_int32(&table[chunk2entry].samples_);
706+ chunk1 = chunk2;
707+
708+ if(chunk2entry < entries)
709+ {
710+ chunk2entry++;
711+ total += range_samples;
712+ }
713+ } while(chunk2entry < entries);
714+
715+ if(chunk1samples)
716+ {
717+ unsigned int sample_in_chunk = (sample - total) % chunk1samples;
718+ if(sample_in_chunk != 0)
719+ {
720+ printf("ERROR: sample must be chunk aligned: %d\n", sample_in_chunk);
721+ }
722+ chunk = (sample - total) / chunk1samples + chunk1;
723+ }
724+ else
725+ chunk = 1;
726+
727+ chunk_sample = total + (chunk - chunk1) * chunk1samples;
728+
729+ return chunk;
730+ }
731+}
732+
733+unsigned int stsc_get_samples(unsigned char* stsc)
734+{
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;
738+ unsigned int i;
739+ for(i = 0; i != entries; ++i)
740+ {
741+ samples += read_int32(&table[i].samples_);
742+ }
743+ return samples;
744+}
745+
746+unsigned int stco_get_entries(unsigned char const* stco)
747+{
748+ return read_int32(stco + 4);
749+}
750+
751+unsigned int stco_get_offset(unsigned char const* stco, int idx)
752+{
753+ uint32_t const* table = (uint32_t const*)(stco + 8);
754+ return read_int32(&table[idx]);
755+}
756+
757+unsigned int stsz_get_sample_size(unsigned char const* stsz)
758+{
759+ return read_int32(stsz + 4);
760+}
761+
762+unsigned int stsz_get_entries(unsigned char const* stsz)
763+{
764+ return read_int32(stsz + 8);
765+}
766+
767+unsigned int stsz_get_size(unsigned char const* stsz, unsigned int idx)
768+{
769+ uint32_t const* table = (uint32_t const*)(stsz + 12);
770+ return read_int32(&table[idx]);
771+}
772+
773+unsigned int stts_get_duration(unsigned char const* stts)
774+{
775+ long duration = 0;
776+ long entries = stts_get_entries(stts);
777+ int i;
778+ for(i = 0; i != entries; ++i)
779+ {
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;
785+ }
786+
787+ return duration;
788+}
789+
790+unsigned int stts_get_samples(unsigned char const* stts)
791+{
792+ long samples = 0;
793+ long entries = stts_get_entries(stts);
794+ int i;
795+ for(i = 0; i != entries; ++i)
796+ {
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;
802+ }
803+
804+ return samples;
805+}
806+
807+unsigned int stts_get_sample(unsigned char const* stts, unsigned int time)
808+{
809+ unsigned int stts_index = 0;
810+ unsigned int stts_count;
811+
812+ unsigned int ret = 0;
813+ unsigned int time_count = 0;
814+
815+ unsigned int entries = stts_get_entries(stts);
816+ for(; stts_index != entries; ++stts_index)
817+ {
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)
823+ {
824+ stts_count = (time - time_count) / sample_duration;
825+ time_count += stts_count * sample_duration;
826+ ret += stts_count;
827+ break;
828+ }
829+ else
830+ {
831+ time_count += sample_duration * sample_count;
832+ ret += sample_count;
833+// stts_index++;
834+ }
835+// if(stts_index >= table_.size())
836+// break;
837+ }
838+// *time = time_count;
839+ return ret;
840+}
841+
842+unsigned int stts_get_time(unsigned char const* stts, unsigned int sample)
843+{
844+ unsigned int ret = 0;
845+ unsigned int stts_index = 0;
846+ unsigned int sample_count = 0;
847+
848+ for(;;)
849+ {
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);
854+
855+ if(sample_count + table_sample_count > sample)
856+ {
857+ unsigned int stts_count = (sample - sample_count);
858+ ret += stts_count * table_sample_duration;
859+ break;
860+ }
861+ else
862+ {
863+ sample_count += table_sample_count;
864+ ret += table_sample_count * table_sample_duration;
865+ stts_index++;
866+ }
867+ }
868+ return ret;
869+}
870+
871+
872+struct stbl_t
873+{
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
882+};
883+
884+void stbl_parse(struct stbl_t* stbl, unsigned char* buffer, unsigned int size)
885+{
886+ struct atom_t leaf_atom;
887+ unsigned char* buffer_start = buffer;
888+ stbl->stss_ = 0;
889+ stbl->ctts_ = 0;
890+
891+ stbl->start_ = buffer;
892+
893+ while(buffer < buffer_start + size)
894+ {
895+ buffer = atom_read_header(buffer, &leaf_atom);
896+
897+ atom_print(&leaf_atom);
898+
899+ if(atom_is(&leaf_atom, "stts"))
900+ {
901+ stbl->stts_ = buffer;
902+ }
903+ else
904+ if(atom_is(&leaf_atom, "stss"))
905+ {
906+ stbl->stss_ = buffer;
907+ }
908+ else
909+ if(atom_is(&leaf_atom, "stsc"))
910+ {
911+ stbl->stsc_ = buffer;
912+ }
913+ else
914+ if(atom_is(&leaf_atom, "stsz"))
915+ {
916+ stbl->stsz_ = buffer;
917+ }
918+ else
919+ if(atom_is(&leaf_atom, "stco"))
920+ {
921+ stbl->stco_ = buffer;
922+ }
923+ else
924+ if(atom_is(&leaf_atom, "co64"))
925+ {
926+ perror("TODO: co64");
927+ }
928+ else
929+ if(atom_is(&leaf_atom, "ctts"))
930+ {
931+ stbl->ctts_ = buffer;
932+ }
933+
934+ buffer = atom_skip(&leaf_atom);
935+ }
936+}
937+
938+struct minf_t
939+{
940+ unsigned char* start_;
941+ struct stbl_t stbl_;
942+};
943+
944+void minf_parse(struct minf_t* minf, unsigned char* buffer, unsigned int size)
945+{
946+ struct atom_t leaf_atom;
947+ unsigned char* buffer_start = buffer;
948+
949+ minf->start_ = buffer;
950+
951+ while(buffer < buffer_start + size)
952+ {
953+ buffer = atom_read_header(buffer, &leaf_atom);
954+
955+ atom_print(&leaf_atom);
956+
957+ if(atom_is(&leaf_atom, "stbl"))
958+ {
959+ stbl_parse(&minf->stbl_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
960+ }
961+
962+ buffer = atom_skip(&leaf_atom);
963+ }
964+}
965+
966+struct mdia_t
967+{
968+ unsigned char* start_;
969+ unsigned char* mdhd_;
970+ struct minf_t minf_;
971+// hdlr hdlr_;
972+};
973+
974+void mdia_parse(struct mdia_t* mdia, unsigned char* buffer, unsigned int size)
975+{
976+ struct atom_t leaf_atom;
977+ unsigned char* buffer_start = buffer;
978+
979+ mdia->start_ = buffer;
980+
981+ while(buffer < buffer_start + size)
982+ {
983+ buffer = atom_read_header(buffer, &leaf_atom);
984+
985+ atom_print(&leaf_atom);
986+
987+ if(atom_is(&leaf_atom, "mdhd"))
988+ {
989+ mdia->mdhd_ = buffer;
990+ }
991+ else
992+ if(atom_is(&leaf_atom, "minf"))
993+ {
994+ minf_parse(&mdia->minf_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
995+ }
996+
997+ buffer = atom_skip(&leaf_atom);
998+ }
999+}
1000+
1001+struct chunks_t
1002+{
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
1007+};
1008+
1009+struct samples_t
1010+{
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
1015+};
1016+
1017+struct trak_t
1018+{
1019+ unsigned char* start_;
1020+ unsigned char* tkhd_;
1021+ struct mdia_t mdia_;
1022+
1023+ /* temporary indices */
1024+ unsigned int chunks_size_;
1025+ struct chunks_t* chunks_;
1026+
1027+ unsigned int samples_size_;
1028+ struct samples_t* samples_;
1029+};
1030+
1031+void trak_init(struct trak_t* trak)
1032+{
1033+ trak->chunks_ = 0;
1034+ trak->samples_ = 0;
1035+}
1036+
1037+void trak_exit(struct trak_t* trak)
1038+{
1039+ if(trak->chunks_)
1040+ free(trak->chunks_);
1041+ if(trak->samples_)
1042+ free(trak->samples_);
1043+}
1044+
1045+void trak_parse(struct trak_t* trak, unsigned char* buffer, unsigned int size)
1046+{
1047+ struct atom_t leaf_atom;
1048+ unsigned char* buffer_start = buffer;
1049+
1050+ trak->start_ = buffer;
1051+
1052+ while(buffer < buffer_start + size)
1053+ {
1054+ buffer = atom_read_header(buffer, &leaf_atom);
1055+
1056+ atom_print(&leaf_atom);
1057+
1058+ if(atom_is(&leaf_atom, "tkhd"))
1059+ {
1060+ trak->tkhd_ = buffer;
1061+ }
1062+ else
1063+ if(atom_is(&leaf_atom, "mdia"))
1064+ {
1065+ mdia_parse(&trak->mdia_, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
1066+ }
1067+
1068+ buffer = atom_skip(&leaf_atom);
1069+ }
1070+}
1071+
1072+struct moov_t
1073+{
1074+ unsigned char* start_;
1075+ unsigned int tracks_;
1076+ unsigned char* mvhd_;
1077+ struct trak_t traks_[MAX_TRACKS];
1078+};
1079+
1080+void moov_init(struct moov_t* moov)
1081+{
1082+ moov->tracks_ = 0;
1083+}
1084+
1085+void moov_exit(struct moov_t* moov)
1086+{
1087+ unsigned int i;
1088+ for(i = 0; i != moov->tracks_; ++i)
1089+ {
1090+ trak_exit(&moov->traks_[i]);
1091+ }
1092+}
1093+
1094+void trak_build_index(struct trak_t* trak)
1095+{
1096+ void const* stco = trak->mdia_.minf_.stbl_.stco_;
1097+
1098+ trak->chunks_size_ = stco_get_entries(stco);
1099+ trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t));
1100+
1101+ {
1102+ unsigned int i;
1103+ for(i = 0; i != trak->chunks_size_; ++i)
1104+ {
1105+ trak->chunks_[i].pos_ = stco_get_offset(stco, i);
1106+ }
1107+ }
1108+
1109+ // process chunkmap:
1110+ {
1111+ void const* stsc = trak->mdia_.minf_.stbl_.stsc_;
1112+ unsigned int last = trak->chunks_size_;
1113+ unsigned int i = stsc_get_entries(stsc);
1114+ while(i > 0)
1115+ {
1116+ struct stsc_table_t stsc_table;
1117+ unsigned int j;
1118+
1119+ --i;
1120+
1121+ stsc_get_table(stsc, i, &stsc_table);
1122+ for(j = stsc_table.chunk_; j < last; j++)
1123+ {
1124+ trak->chunks_[j].id_ = stsc_table.id_;
1125+ trak->chunks_[j].size_ = stsc_table.samples_;
1126+ }
1127+ last = stsc_table.chunk_;
1128+ }
1129+ }
1130+
1131+ // calc pts of chunks:
1132+ {
1133+ void const* stsz = trak->mdia_.minf_.stbl_.stsz_;
1134+ unsigned int sample_size = stsz_get_sample_size(stsz);
1135+ unsigned int s = 0;
1136+ {
1137+ unsigned int j;
1138+ for(j = 0; j < trak->chunks_size_; j++)
1139+ {
1140+ trak->chunks_[j].sample_ = s;
1141+ s += trak->chunks_[j].size_;
1142+ }
1143+ }
1144+
1145+ if(sample_size == 0)
1146+ {
1147+ trak->samples_size_ = stsz_get_entries(stsz);
1148+ }
1149+ else
1150+ {
1151+ trak->samples_size_ = s;
1152+ }
1153+
1154+ trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t));
1155+
1156+ if(sample_size == 0)
1157+ {
1158+ unsigned int i;
1159+ for(i = 0; i != trak->samples_size_ ; ++i)
1160+ trak->samples_[i].size_ = stsz_get_size(stsz, i);
1161+ }
1162+ else
1163+ {
1164+ unsigned int i;
1165+ for(i = 0; i != trak->samples_size_ ; ++i)
1166+ trak->samples_[i].size_ = sample_size;
1167+ }
1168+ }
1169+
1170+// i = 0;
1171+// for (j = 0; j < trak->durmap_size; j++)
1172+// i += trak->durmap[j].num;
1173+// if (i != s) {
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;
1177+// }
1178+
1179+ // calc pts:
1180+ {
1181+ void const* stts = trak->mdia_.minf_.stbl_.stts_;
1182+ unsigned int s = 0;
1183+ unsigned int entries = stts_get_entries(stts);
1184+ unsigned int j;
1185+ for(j = 0; j < entries; j++)
1186+ {
1187+ unsigned int i;
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++)
1194+ {
1195+ trak->samples_[s].pts_ = pts;
1196+ ++s;
1197+ pts += sample_duration;
1198+ }
1199+ }
1200+ }
1201+
1202+ // calc composition times:
1203+ {
1204+ void const* ctts = trak->mdia_.minf_.stbl_.ctts_;
1205+ if(ctts)
1206+ {
1207+ unsigned int s = 0;
1208+ unsigned int entries = ctts_get_entries(ctts);
1209+ unsigned int j;
1210+ for(j = 0; j < entries; j++)
1211+ {
1212+ unsigned int i;
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++)
1217+ {
1218+ trak->samples_[s].cto_ = sample_offset;
1219+ ++s;
1220+ }
1221+ }
1222+ }
1223+ }
1224+
1225+ // calc sample offsets
1226+ {
1227+ unsigned int s = 0;
1228+ unsigned int j;
1229+ for(j = 0; j < trak->chunks_size_; j++)
1230+ {
1231+ unsigned int pos = trak->chunks_[j].pos_;
1232+ unsigned int i;
1233+ for(i = 0; i < trak->chunks_[j].size_; i++)
1234+ {
1235+ trak->samples_[s].pos_ = pos;
1236+ pos += trak->samples_[s].size_;
1237+ ++s;
1238+ }
1239+ }
1240+ }
1241+}
1242+
1243+void trak_write_index(struct trak_t* trak, unsigned int start, unsigned int end)
1244+{
1245+ // write samples [start,end>
1246+
1247+ // stts = [entries * [sample_count, sample_duration]
1248+ {
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);
1252+ unsigned int s;
1253+ for(s = start; s != end; ++s)
1254+ {
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)
1259+ {
1260+ if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration)
1261+ break;
1262+ ++sample_count;
1263+ ++s;
1264+ }
1265+ // write entry
1266+ write_int32(&table[entries].sample_count_, sample_count);
1267+ write_int32(&table[entries].sample_duration_, sample_duration);
1268+ ++entries;
1269+ }
1270+ write_int32(stts + 4, entries);
1271+ if(stts_get_samples(stts) != end - start)
1272+ {
1273+ printf("ERROR: stts_get_samples=%d, should be %d\n",
1274+ stts_get_samples(stts), end - start);
1275+ }
1276+ }
1277+
1278+ // ctts = [entries * [sample_count, sample_offset]
1279+ {
1280+ unsigned char* ctts = trak->mdia_.minf_.stbl_.ctts_;
1281+ if(ctts)
1282+ {
1283+ unsigned int entries = 0;
1284+ struct ctts_table_t* table = (struct ctts_table_t*)(ctts + 8);
1285+ unsigned int s;
1286+ for(s = start; s != end; ++s)
1287+ {
1288+ unsigned int sample_count = 1;
1289+ unsigned int sample_offset = trak->samples_[s].cto_;
1290+ while(s != end - 1)
1291+ {
1292+ if(trak->samples_[s + 1].cto_ != sample_offset)
1293+ break;
1294+ ++sample_count;
1295+ ++s;
1296+ }
1297+ // write entry
1298+ write_int32(&table[entries].sample_count_, sample_count);
1299+ write_int32(&table[entries].sample_offset_, sample_offset);
1300+ ++entries;
1301+ }
1302+ write_int32(ctts + 4, entries);
1303+ if(ctts_get_samples(ctts) != end - start)
1304+ {
1305+ printf("ERROR: ctts_get_samples=%d, should be %d\n",
1306+ ctts_get_samples(ctts), end - start);
1307+ }
1308+ }
1309+ }
1310+
1311+ // process chunkmap:
1312+ {
1313+ unsigned char* stsc = trak->mdia_.minf_.stbl_.stsc_;
1314+ struct stsc_table_t* stsc_table = (struct stsc_table_t*)(stsc + 8);
1315+ unsigned int i;
1316+ for(i = 0; i != trak->chunks_size_; ++i)
1317+ {
1318+ if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start)
1319+ break;
1320+ }
1321+
1322+ {
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_;
1328+
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);
1333+ ++stsc_entries;
1334+ if(i != trak->chunks_size_)
1335+ {
1336+ for(i += 1; i != trak->chunks_size_; ++i)
1337+ {
1338+ if(trak->chunks_[i].size_ != samples)
1339+ {
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);
1345+ ++stsc_entries;
1346+ }
1347+ }
1348+ }
1349+ write_int32(stsc + 4, stsc_entries);
1350+ {
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);
1358+
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_);
1362+ }
1363+ }
1364+ }
1365+
1366+ // process sync samples:
1367+ if(trak->mdia_.minf_.stbl_.stss_)
1368+ {
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;
1373+ unsigned int i;
1374+ for(i = 0; i != entries; ++i)
1375+ {
1376+ if(read_int32(&table[i]) >= start + 1)
1377+ break;
1378+ }
1379+ stss_start = i;
1380+ for(; i != entries; ++i)
1381+ {
1382+ unsigned int sync_sample = read_int32(&table[i]);
1383+ if(sync_sample >= end + 1)
1384+ break;
1385+ write_int32(&table[i - stss_start], sync_sample - start);
1386+
1387+ }
1388+// memmove(table, table + stss_start, (i - stss_start) * sizeof(uint32_t));
1389+ write_int32(stss + 4, i - stss_start);
1390+ }
1391+
1392+ // process sample sizes
1393+ {
1394+ unsigned char* stsz = trak->mdia_.minf_.stbl_.stsz_;
1395+ if(stsz_get_sample_size(stsz) == 0)
1396+ {
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);
1400+ }
1401+ }
1402+}
1403+
1404+int moov_parse(struct moov_t* moov, unsigned char* buffer, unsigned int size)
1405+{
1406+ struct atom_t leaf_atom;
1407+ unsigned char* buffer_start = buffer;
1408+
1409+ moov->start_ = buffer;
1410+
1411+ while(buffer < buffer_start + size)
1412+ {
1413+ buffer = atom_read_header(buffer, &leaf_atom);
1414+
1415+ atom_print(&leaf_atom);
1416+
1417+ if(atom_is(&leaf_atom, "cmov"))
1418+ {
1419+ return 0;
1420+ }
1421+ else
1422+ if(atom_is(&leaf_atom, "mvhd"))
1423+ {
1424+ moov->mvhd_ = buffer;
1425+ }
1426+ else
1427+ if(atom_is(&leaf_atom, "trak"))
1428+ {
1429+ if(moov->tracks_ == MAX_TRACKS)
1430+ return 0;
1431+ else
1432+ {
1433+ struct trak_t* trak = &moov->traks_[moov->tracks_];
1434+ trak_init(trak);
1435+ trak_parse(trak, buffer, leaf_atom.size_ - ATOM_PREAMBLE_SIZE);
1436+ ++moov->tracks_;
1437+ }
1438+ }
1439+ buffer = atom_skip(&leaf_atom);
1440+ }
1441+
1442+ // build the indexing tables
1443+ {
1444+ unsigned int i;
1445+ for(i = 0; i != moov->tracks_; ++i)
1446+ {
1447+ trak_build_index(&moov->traks_[i]);
1448+ }
1449+ }
1450+
1451+ return 1;
1452+}
1453+
1454+void stco_shift_offsets(unsigned char* stco, int offset)
1455+{
1456+ unsigned int entries = read_int32(stco + 4);
1457+ unsigned int* table = (unsigned int*)(stco + 8);
1458+ unsigned int i;
1459+ for(i = 0; i != entries; ++i)
1460+ write_int32(&table[i], (read_int32(&table[i]) + offset));
1461+}
1462+
1463+void trak_shift_offsets(struct trak_t* trak, int offset)
1464+{
1465+ unsigned char* stco = trak->mdia_.minf_.stbl_.stco_;
1466+ stco_shift_offsets(stco, offset);
1467+}
1468+
1469+void moov_shift_offsets(struct moov_t* moov, int offset)
1470+{
1471+ unsigned int i;
1472+ for(i = 0; i != moov->tracks_; ++i)
1473+ {
1474+ trak_shift_offsets(&moov->traks_[i], offset);
1475+ }
1476+}
1477+
1478+long mvhd_get_time_scale(unsigned char* mvhd)
1479+{
1480+ int version = read_char(mvhd);
1481+ unsigned char* p = mvhd + (version == 0 ? 12 : 20);
1482+ return read_int32(p);
1483+}
1484+
1485+void mvhd_set_duration(unsigned char* mvhd, long duration)
1486+{
1487+ int version = read_char(mvhd);
1488+ if(version == 0)
1489+ {
1490+ write_int32(mvhd + 16, duration);
1491+ }
1492+ else
1493+ {
1494+ perror("mvhd_set_duration");
1495+// write_int64(mvhd + 24, duration);
1496+ }
1497+}
1498+
1499+long mdhd_get_time_scale(unsigned char* mdhd)
1500+{
1501+ return read_int32(mdhd + 12);
1502+}
1503+
1504+void mdhd_set_duration(unsigned char* mdhd, unsigned int duration)
1505+{
1506+ write_int32(mdhd + 16, duration);
1507+}
1508+
1509+void tkhd_set_duration(unsigned char* tkhd, unsigned int duration)
1510+{
1511+ int version = read_char(tkhd);
1512+ if(version == 0)
1513+ {
1514+ write_int32(tkhd + 20, duration);
1515+ }
1516+ else
1517+ {
1518+ perror("tkhd_set_duration");
1519+// write_int64(tkhd + 28, duration);
1520+ }
1521+}
1522+
1523+unsigned int stss_get_entries(unsigned char const* stss)
1524+{
1525+ return read_int32(stss + 4);
1526+}
1527+
1528+unsigned int stss_get_sample(unsigned char const* stss, unsigned int idx)
1529+{
1530+ unsigned char const* p = stss + 8 + idx * 4;
1531+ return read_int32(p);
1532+}
1533+
1534+unsigned int stss_get_nearest_keyframe(unsigned char const* stss, unsigned int sample)
1535+{
1536+ // scan the sync samples to find the key frame that precedes the sample number
1537+ unsigned int i;
1538+ unsigned int entries = stss_get_entries(stss);
1539+ unsigned int table_sample = 0;
1540+ for(i = 0; i != entries; ++i)
1541+ {
1542+ table_sample = stss_get_sample(stss, i);
1543+ if(table_sample >= sample)
1544+ break;
1545+ }
1546+ if(table_sample == sample)
1547+ return table_sample;
1548+ else
1549+ return stss_get_sample(stss, i - 1);
1550+}
1551+
1552+unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample)
1553+{
1554+ // If the sync atom is not present, all samples are implicit sync samples.
1555+ if(!stbl->stss_)
1556+ return sample;
1557+
1558+ return stss_get_nearest_keyframe(stbl->stss_, sample);
1559+}
1560+
1561+unsigned int moov_seek(unsigned char* moov_data, unsigned int size,
1562+ float start_time,
1563+ unsigned int* mdat_start,
1564+ unsigned int* mdat_size,
1565+ unsigned int offset)
1566+{
1567+ struct moov_t* moov = malloc(sizeof(struct moov_t));
1568+ moov_init(moov);
1569+ if(!moov_parse(moov, moov_data, size))
1570+ {
1571+ moov_exit(moov);
1572+ free(moov);
1573+ return 0;
1574+ }
1575+
1576+ {
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;
1581+ unsigned int i;
1582+
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];
1587+
1588+ unsigned int moov_duration = 0;
1589+
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)
1594+ {
1595+ for(i = 2; i != moov->tracks_; ++i)
1596+ {
1597+ // patch 'trak' to 'free'
1598+ unsigned char* p = moov->traks_[i].start_ - 4;
1599+ p[0] = 'f';
1600+ p[1] = 'r';
1601+ p[2] = 'e';
1602+ p[3] = 'e';
1603+ }
1604+ moov->tracks_ = 2;
1605+ }
1606+
1607+ for(i = 0; i != moov->tracks_; ++i)
1608+ {
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;
1614+
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);
1619+ }
1620+
1621+ printf("start=%u\n", start);
1622+
1623+ for(i = 0; i != moov->tracks_; ++i)
1624+ {
1625+ struct trak_t* trak = &moov->traks_[i];
1626+ struct stbl_t* stbl = &trak->mdia_.minf_.stbl_;
1627+
1628+ unsigned int start_sample = trak_sample_start[i];
1629+ unsigned int end_sample = trak->samples_size_;
1630+
1631+ trak_write_index(trak, start_sample, end_sample);
1632+
1633+ {
1634+ unsigned skip =
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);
1639+ }
1640+
1641+ {
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);
1650+
1651+ if(duration > moov_duration)
1652+ moov_duration = duration;
1653+ }
1654+
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_));
1659+ }
1660+ mvhd_set_duration(moov->mvhd_, moov_duration);
1661+
1662+ offset -= bytes_to_skip;
1663+
1664+ printf("shifting offsets by %d\n", offset);
1665+ moov_shift_offsets(moov, offset);
1666+
1667+ *mdat_start += bytes_to_skip;
1668+ *mdat_size -= bytes_to_skip;
1669+ }
1670+
1671+ moov_exit(moov);
1672+ free(moov);
1673+
1674+ return 1;
1675+}
1676+
1677+// End Of File
1678+
This page took 0.284192 seconds and 4 git commands to generate.