]>
Commit | Line | Data |
---|---|---|
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 | |
3 | @@ -77,6 +77,11 @@ | |
4 | mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined | |
5 | mod_flv_streaming_la_LIBADD = $(common_libadd) | |
6 | ||
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) | |
11 | + | |
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 | |
17 | @@ -0,0 +1,337 @@ | |
18 | +/******************************************************************************* | |
19 | + mod_h264_streaming.c | |
20 | + | |
21 | + mod_h264_streaming - A lighttpd plugin for pseudo-streaming Quicktime/MPEG4 files. | |
22 | + http://h264.code-shop.com | |
23 | + | |
24 | + Copyright (C) 2007-2009 CodeShop B.V. | |
25 | +******************************************************************************/ | |
26 | + | |
27 | +#include <ctype.h> | |
28 | +#include <stdlib.h> | |
29 | +#include <string.h> | |
30 | +#include <stdio.h> | |
31 | + | |
32 | +#include "base.h" | |
33 | +#include "log.h" | |
34 | +#include "buffer.h" | |
35 | +#include "response.h" | |
36 | +#include "http_chunk.h" | |
37 | +#include "stat_cache.h" | |
38 | + | |
39 | +#include "plugin.h" | |
40 | + | |
41 | +#ifdef HAVE_CONFIG_H | |
42 | +#include "config.h" | |
43 | +#endif | |
44 | + | |
45 | +#include "moov.h" | |
46 | + | |
47 | +/* plugin config for all request/connections */ | |
48 | + | |
49 | +typedef struct { | |
50 | + array *extensions; | |
51 | +} plugin_config; | |
52 | + | |
53 | +typedef struct { | |
54 | + PLUGIN_DATA; | |
55 | + | |
56 | + buffer *query_str; | |
57 | + array *get_params; | |
58 | + | |
59 | + plugin_config **config_storage; | |
60 | + | |
61 | + plugin_config conf; | |
62 | +} plugin_data; | |
63 | + | |
64 | +/* init the plugin data */ | |
65 | +INIT_FUNC(mod_h264_streaming_init) { | |
66 | + plugin_data *p; | |
67 | + | |
68 | + p = calloc(1, sizeof(*p)); | |
69 | + | |
70 | + p->query_str = buffer_init(); | |
71 | + p->get_params = array_init(); | |
72 | + | |
73 | + return p; | |
74 | +} | |
75 | + | |
76 | +/* detroy the plugin data */ | |
77 | +FREE_FUNC(mod_h264_streaming_free) { | |
78 | + plugin_data *p = p_d; | |
79 | + | |
80 | + UNUSED(srv); | |
81 | + | |
82 | + if (!p) return HANDLER_GO_ON; | |
83 | + | |
84 | + if (p->config_storage) { | |
85 | + size_t i; | |
86 | + | |
87 | + for (i = 0; i < srv->config_context->used; i++) { | |
88 | + plugin_config *s = p->config_storage[i]; | |
89 | + | |
90 | + if (!s) continue; | |
91 | + | |
92 | + array_free(s->extensions); | |
93 | + | |
94 | + free(s); | |
95 | + } | |
96 | + free(p->config_storage); | |
97 | + } | |
98 | + | |
99 | + buffer_free(p->query_str); | |
100 | + array_free(p->get_params); | |
101 | + | |
102 | + free(p); | |
103 | + | |
104 | + return HANDLER_GO_ON; | |
105 | +} | |
106 | + | |
107 | +/* handle plugin config and check values */ | |
108 | + | |
109 | +SETDEFAULTS_FUNC(mod_h264_streaming_set_defaults) { | |
110 | + plugin_data *p = p_d; | |
111 | + size_t i = 0; | |
112 | + | |
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 } | |
116 | + }; | |
117 | + | |
118 | + if (!p) return HANDLER_ERROR; | |
119 | + | |
120 | + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); | |
121 | + | |
122 | + for (i = 0; i < srv->config_context->used; i++) { | |
123 | + plugin_config *s; | |
124 | + | |
125 | + s = calloc(1, sizeof(plugin_config)); | |
126 | + s->extensions = array_init(); | |
127 | + | |
128 | + cv[0].destination = s->extensions; | |
129 | + | |
130 | + p->config_storage[i] = s; | |
131 | + | |
132 | + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { | |
133 | + return HANDLER_ERROR; | |
134 | + } | |
135 | + } | |
136 | + | |
137 | + return HANDLER_GO_ON; | |
138 | +} | |
139 | + | |
140 | +#define PATCH(x) \ | |
141 | + p->conf.x = s->x; | |
142 | +static int mod_h264_streaming_patch_connection(server *srv, connection *con, plugin_data *p) { | |
143 | + size_t i, j; | |
144 | + plugin_config *s = p->config_storage[0]; | |
145 | + | |
146 | + PATCH(extensions); | |
147 | + | |
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]; | |
152 | + | |
153 | + /* condition didn't match */ | |
154 | + if (!config_check_cond(srv, con, dc)) continue; | |
155 | + | |
156 | + /* merge config */ | |
157 | + for (j = 0; j < dc->value->used; j++) { | |
158 | + data_unset *du = dc->value->data[j]; | |
159 | + | |
160 | + if (buffer_is_equal_string(du->key, CONST_STR_LEN("h264-streaming.extensions"))) { | |
161 | + PATCH(extensions); | |
162 | + } | |
163 | + } | |
164 | + } | |
165 | + | |
166 | + return 0; | |
167 | +} | |
168 | +#undef PATCH | |
169 | + | |
170 | +static int split_get_params(array *get_params, buffer *qrystr) { | |
171 | + size_t is_key = 1; | |
172 | + size_t i; | |
173 | + char *key = NULL, *val = NULL; | |
174 | + | |
175 | + key = qrystr->ptr; | |
176 | + | |
177 | + /* we need the \0 */ | |
178 | + for (i = 0; i < qrystr->used; i++) { | |
179 | + switch(qrystr->ptr[i]) { | |
180 | + case '=': | |
181 | + if (is_key) { | |
182 | + val = qrystr->ptr + i + 1; | |
183 | + | |
184 | + qrystr->ptr[i] = '\0'; | |
185 | + | |
186 | + is_key = 0; | |
187 | + } | |
188 | + | |
189 | + break; | |
190 | + case '&': | |
191 | + case '\0': /* fin symbol */ | |
192 | + if (!is_key) { | |
193 | + data_string *ds; | |
194 | + /* we need at least a = since the last & */ | |
195 | + | |
196 | + /* terminate the value */ | |
197 | + qrystr->ptr[i] = '\0'; | |
198 | + | |
199 | + if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) { | |
200 | + ds = data_string_init(); | |
201 | + } | |
202 | + buffer_copy_string_len(ds->key, key, strlen(key)); | |
203 | + buffer_copy_string_len(ds->value, val, strlen(val)); | |
204 | + | |
205 | + array_insert_unique(get_params, (data_unset *)ds); | |
206 | + } | |
207 | + | |
208 | + key = qrystr->ptr + i + 1; | |
209 | + val = NULL; | |
210 | + is_key = 1; | |
211 | + break; | |
212 | + } | |
213 | + } | |
214 | + | |
215 | + return 0; | |
216 | +} | |
217 | + | |
218 | +URIHANDLER_FUNC(mod_h264_streaming_path_handler) { | |
219 | + plugin_data *p = p_d; | |
220 | + int s_len; | |
221 | + size_t k; | |
222 | + | |
223 | + UNUSED(srv); | |
224 | + | |
225 | + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; | |
226 | + | |
227 | + mod_h264_streaming_patch_connection(srv, con, p); | |
228 | + | |
229 | + s_len = con->physical.path->used - 1; | |
230 | + | |
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; | |
234 | + | |
235 | + if (ct_len > s_len) continue; | |
236 | + if (ds->value->used == 0) continue; | |
237 | + | |
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; | |
242 | + double end = 0.0; | |
243 | + char *err = NULL; | |
244 | + int client_is_flash = 0; | |
245 | + | |
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); | |
249 | + | |
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"))) | |
253 | + { | |
254 | + /* check if it is a number */ | |
255 | + start = strtod(get_param->value->ptr, &err); | |
256 | + if (*err != '\0') { | |
257 | + return HANDLER_GO_ON; | |
258 | + } | |
259 | + if (start < 0) return HANDLER_GO_ON; | |
260 | + } | |
261 | + | |
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"))) | |
264 | + { | |
265 | + /* check if it is a number */ | |
266 | + end = strtod(get_param->value->ptr, &err); | |
267 | + if (*err != '\0') { | |
268 | + return HANDLER_GO_ON; | |
269 | + } | |
270 | + if (end < 0 || start >= end) return HANDLER_GO_ON; | |
271 | + } | |
272 | + | |
273 | + if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "client"))) | |
274 | + { | |
275 | + client_is_flash = starts_with(get_param->value->ptr, "FLASH"); | |
276 | + } | |
277 | + | |
278 | + /* get file info */ | |
279 | + if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { | |
280 | + return HANDLER_GO_ON; | |
281 | + } | |
282 | + | |
283 | + /* we are safe now, let's build a h264 header */ | |
284 | + { | |
285 | + { | |
286 | + unsigned int filesize = sce->st.st_size; | |
287 | + | |
288 | + void* mp4_header; | |
289 | + uint32_t mp4_header_size; | |
290 | + uint64_t mdat_offset; | |
291 | + uint64_t mdat_size; | |
292 | + | |
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); | |
296 | + | |
297 | + if(result) | |
298 | + { | |
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 */ | |
302 | + | |
303 | + http_chunk_append_file(srv, con, con->physical.path, | |
304 | + mdat_offset, mdat_size); | |
305 | + } | |
306 | + | |
307 | + if(mp4_header) | |
308 | + { | |
309 | + free(mp4_header); | |
310 | + } | |
311 | + | |
312 | + if(!result) | |
313 | + { | |
314 | + return HANDLER_GO_ON; | |
315 | + } | |
316 | + } | |
317 | + } | |
318 | + | |
319 | + response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/mp4")); | |
320 | + if(!client_is_flash) | |
321 | + { | |
322 | + response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0")); | |
323 | + } | |
324 | + else | |
325 | + { | |
326 | + response_header_overwrite(srv, con, CONST_STR_LEN("X-Mod-H264-Streaming"), CONST_STR_LEN("version=2.0,client=flash")); | |
327 | + } | |
328 | + | |
329 | + con->file_finished = 1; | |
330 | + | |
331 | + return HANDLER_FINISHED; | |
332 | + } | |
333 | + } | |
334 | + | |
335 | + /* not found */ | |
336 | + return HANDLER_GO_ON; | |
337 | +} | |
338 | + | |
339 | +/* this function is called at dlopen() time and inits the callbacks */ | |
340 | + | |
341 | +int mod_h264_streaming_plugin_init(plugin *p) { | |
342 | + p->version = LIGHTTPD_VERSION_ID; | |
343 | + p->name = buffer_init_string("h264_streaming"); | |
344 | + | |
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; | |
349 | + | |
350 | + p->data = NULL; | |
351 | + | |
352 | + return 0; | |
353 | +} | |
354 | + | |
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 | |
357 | @@ -0,0 +1,3031 @@ | |
358 | +/******************************************************************************* | |
359 | + moov.c (version 2) | |
360 | + | |
361 | + moov - A library for splitting Quicktime/MPEG4 files. | |
362 | + http://h264.code-shop.com | |
363 | + | |
364 | + Copyright (C) 2007-2009 CodeShop B.V. | |
365 | + | |
366 | + Licensing | |
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. | |
370 | + | |
371 | + Creative Commons License: | |
372 | + http://creativecommons.org/licenses/by-nc-sa/3.0/ | |
373 | + | |
374 | + Commercial License: | |
375 | + http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2 | |
376 | +******************************************************************************/ | |
377 | + | |
378 | +#include "moov.h" | |
379 | + | |
380 | +#ifdef _MSVC_VER | |
381 | +#define _CRTDBG_MAP_ALLOC | |
382 | +#include <stdlib.h> | |
383 | +#include <crtdbg.h> | |
384 | +#endif | |
385 | + | |
386 | +#ifdef UNUSED | |
387 | +#elif defined(__GNUC__) | |
388 | +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) | |
389 | +#elif defined(__LCLINT__) | |
390 | +# define UNUSED(x) /*@unused@*/ x | |
391 | +#else | |
392 | +# define UNUSED(x) x | |
393 | +#endif | |
394 | + | |
395 | +/* | |
396 | + The QuickTime File Format PDF from Apple: | |
397 | + http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf | |
398 | +*/ | |
399 | + | |
400 | +#include <stdlib.h> | |
401 | +#include <stdio.h> | |
402 | +#include <string.h> | |
403 | +#include <limits.h> | |
404 | +#include <stdint.h> | |
405 | + | |
406 | +#ifdef HAVE_CONFIG_H | |
407 | +#include "config.h" | |
408 | +#endif | |
409 | + | |
410 | +#ifdef HAVE_STDINT_H | |
411 | +# include <stdint.h> | |
412 | +#endif | |
413 | +#ifdef HAVE_INTTYPES_H | |
414 | +# include <inttypes.h> | |
415 | +#endif | |
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 | |
419 | +# include <zlib.h> | |
420 | +#endif | |
421 | + | |
422 | +#ifdef WIN32 | |
423 | +#define ftello _ftelli64 | |
424 | +#define fseeko _fseeki64 | |
425 | +#endif | |
426 | + | |
427 | +#define MAX_TRACKS 8 | |
428 | + | |
429 | +#define FOURCC(a, b, c, d) ((uint32_t)(a) << 24) + \ | |
430 | + ((uint32_t)(b) << 16) + \ | |
431 | + ((uint32_t)(c) << 8) + \ | |
432 | + ((uint32_t)(d)) | |
433 | + | |
434 | +/* Returns true when the test string is a prefix of the input */ | |
435 | +int starts_with(const char* input, const char* test) | |
436 | +{ | |
437 | + while(*input && *test) | |
438 | + { | |
439 | + if(*input != *test) | |
440 | + return 0; | |
441 | + ++input; | |
442 | + ++test; | |
443 | + } | |
444 | + | |
445 | + return *test == '\0'; | |
446 | +} | |
447 | + | |
448 | +static unsigned int read_8(unsigned char const* buffer) | |
449 | +{ | |
450 | + return buffer[0]; | |
451 | +} | |
452 | + | |
453 | +static unsigned char* write_8(unsigned char* buffer, unsigned char v) | |
454 | +{ | |
455 | + buffer[0] = v; | |
456 | + | |
457 | + return buffer + 1; | |
458 | +} | |
459 | + | |
460 | +static uint16_t read_16(unsigned char const* buffer) | |
461 | +{ | |
462 | + return (buffer[0] << 8) | | |
463 | + (buffer[1] << 0); | |
464 | +} | |
465 | + | |
466 | +static unsigned char* write_16(unsigned char* buffer, unsigned int v) | |
467 | +{ | |
468 | + buffer[0] = (unsigned char)(v >> 8); | |
469 | + buffer[1] = (unsigned char)(v >> 0); | |
470 | + | |
471 | + return buffer + 2; | |
472 | +} | |
473 | + | |
474 | +static unsigned int read_24(unsigned char const* buffer) | |
475 | +{ | |
476 | + return (buffer[0] << 16) | | |
477 | + (buffer[1] << 8) | | |
478 | + (buffer[2] << 0); | |
479 | +} | |
480 | + | |
481 | +static unsigned char* write_24(unsigned char* buffer, unsigned int v) | |
482 | +{ | |
483 | + buffer[0] = (unsigned char)(v >> 16); | |
484 | + buffer[1] = (unsigned char)(v >> 8); | |
485 | + buffer[2] = (unsigned char)(v >> 0); | |
486 | + | |
487 | + return buffer + 3; | |
488 | +} | |
489 | + | |
490 | +static uint32_t read_32(unsigned char const* buffer) | |
491 | +{ | |
492 | + return (buffer[0] << 24) | | |
493 | + (buffer[1] << 16) | | |
494 | + (buffer[2] << 8) | | |
495 | + (buffer[3] << 0); | |
496 | +} | |
497 | + | |
498 | +static unsigned char* write_32(unsigned char* buffer, uint32_t v) | |
499 | +{ | |
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); | |
504 | + | |
505 | + return buffer + 4; | |
506 | +} | |
507 | + | |
508 | +static uint64_t read_64(unsigned char const* buffer) | |
509 | +{ | |
510 | + return ((uint64_t)(read_32(buffer)) << 32) + read_32(buffer + 4); | |
511 | +} | |
512 | + | |
513 | +static unsigned char* write_64(unsigned char* buffer, uint64_t v) | |
514 | +{ | |
515 | + write_32(buffer + 0, (uint32_t)(v >> 32)); | |
516 | + write_32(buffer + 4, (uint32_t)(v >> 0)); | |
517 | + | |
518 | + return buffer + 8; | |
519 | +} | |
520 | + | |
521 | +#define ATOM_PREAMBLE_SIZE 8 | |
522 | + | |
523 | +struct atom_t | |
524 | +{ | |
525 | + uint32_t type_; | |
526 | + uint32_t short_size_; | |
527 | + uint64_t size_; | |
528 | + unsigned char* start_; | |
529 | + unsigned char* end_; | |
530 | +}; | |
531 | + | |
532 | +static unsigned char* atom_read_header(unsigned char* buffer, struct atom_t* atom) | |
533 | +{ | |
534 | + atom->start_ = buffer; | |
535 | + atom->short_size_ = read_32(buffer); | |
536 | + atom->type_ = read_32(buffer + 4); | |
537 | + | |
538 | + if(atom->short_size_ == 1) | |
539 | + atom->size_ = read_64(buffer + 8); | |
540 | + else | |
541 | + atom->size_ = atom->short_size_; | |
542 | + | |
543 | + atom->end_ = atom->start_ + atom->size_; | |
544 | + | |
545 | + return buffer + ATOM_PREAMBLE_SIZE + (atom->short_size_ == 1 ? 8 : 0); | |
546 | +} | |
547 | + | |
548 | +static void atom_print(struct atom_t const* atom) | |
549 | +{ | |
550 | + printf("Atom(%c%c%c%c,%lld)\n", | |
551 | + atom->type_ >> 24, | |
552 | + atom->type_ >> 16, | |
553 | + atom->type_ >> 8, | |
554 | + atom->type_, | |
555 | + atom->size_); | |
556 | +} | |
557 | + | |
558 | +struct unknown_atom_t | |
559 | +{ | |
560 | + void* atom_; | |
561 | + struct unknown_atom_t* next_; | |
562 | +}; | |
563 | + | |
564 | +static struct unknown_atom_t* unknown_atom_init() | |
565 | +{ | |
566 | + struct unknown_atom_t* atom = (struct unknown_atom_t*)malloc(sizeof(struct unknown_atom_t)); | |
567 | + atom->atom_ = 0; | |
568 | + atom->next_ = 0; | |
569 | + | |
570 | + return atom; | |
571 | +} | |
572 | + | |
573 | +static void unknown_atom_exit(struct unknown_atom_t* atom) | |
574 | +{ | |
575 | + while(atom) | |
576 | + { | |
577 | + struct unknown_atom_t* next = atom->next_; | |
578 | + free(atom->atom_); | |
579 | + free(atom); | |
580 | + atom = next; | |
581 | + } | |
582 | +} | |
583 | + | |
584 | +static struct unknown_atom_t* unknown_atom_add_atom(struct unknown_atom_t* parent, void* atom) | |
585 | +{ | |
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; | |
591 | + return unknown; | |
592 | +} | |
593 | + | |
594 | +struct atom_read_list_t | |
595 | +{ | |
596 | + uint32_t type_; | |
597 | + void* parent_; | |
598 | + int (*destination_)(void* parent, void* child); | |
599 | + void* (*reader_)(void* parent, unsigned char* buffer, uint64_t size); | |
600 | +}; | |
601 | + | |
602 | +static int atom_reader(struct atom_read_list_t* atom_read_list, | |
603 | + unsigned int atom_read_list_size, | |
604 | + void* parent, | |
605 | + unsigned char* buffer, uint64_t size) | |
606 | +{ | |
607 | + struct atom_t leaf_atom; | |
608 | + unsigned char* buffer_start = buffer; | |
609 | + | |
610 | + while(buffer < buffer_start + size) | |
611 | + { | |
612 | + unsigned int i; | |
613 | + buffer = atom_read_header(buffer, &leaf_atom); | |
614 | + | |
615 | + atom_print(&leaf_atom); | |
616 | + | |
617 | + for(i = 0; i != atom_read_list_size; ++i) | |
618 | + { | |
619 | + if(leaf_atom.type_ == atom_read_list[i].type_) | |
620 | + { | |
621 | + break; | |
622 | + } | |
623 | + } | |
624 | + | |
625 | + if(i == atom_read_list_size) | |
626 | + { | |
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); | |
630 | + } | |
631 | + else | |
632 | + { | |
633 | + void* child = | |
634 | + atom_read_list[i].reader_(parent, buffer, | |
635 | + leaf_atom.size_ - ATOM_PREAMBLE_SIZE); | |
636 | + if(!child) | |
637 | + break; | |
638 | + if(!atom_read_list[i].destination_(parent, child)) | |
639 | + break; | |
640 | + } | |
641 | + buffer = leaf_atom.end_; | |
642 | + } | |
643 | + | |
644 | + if(buffer < buffer_start + size) | |
645 | + { | |
646 | + return 0; | |
647 | + } | |
648 | + | |
649 | + return 1; | |
650 | +} | |
651 | + | |
652 | +struct atom_write_list_t | |
653 | +{ | |
654 | + uint32_t type_; | |
655 | + void* parent_; | |
656 | + void* source_; | |
657 | + unsigned char* (*writer_)(void* parent, void* atom, unsigned char* buffer); | |
658 | +}; | |
659 | + | |
660 | +static unsigned char* atom_writer_unknown(struct unknown_atom_t* atoms, | |
661 | + unsigned char* buffer) | |
662 | +{ | |
663 | + while(atoms) | |
664 | + { | |
665 | + size_t size = read_32(atoms->atom_); | |
666 | + memcpy(buffer, atoms->atom_, size); | |
667 | + buffer += size; | |
668 | + atoms = atoms->next_; | |
669 | + } | |
670 | + | |
671 | + return buffer; | |
672 | +} | |
673 | + | |
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) | |
678 | +{ | |
679 | + unsigned i; | |
680 | + const int write_box64 = 0; | |
681 | + | |
682 | + if(unknown_atoms) | |
683 | + { | |
684 | + buffer = atom_writer_unknown(unknown_atoms, buffer); | |
685 | + } | |
686 | + | |
687 | + for(i = 0; i != atom_write_list_size; ++i) | |
688 | + { | |
689 | + if(atom_write_list[i].source_ != 0) | |
690 | + { | |
691 | + unsigned char* atom_start = buffer; | |
692 | + // atom size | |
693 | + if(write_box64) | |
694 | + { | |
695 | + write_32(buffer, 1); // box64 | |
696 | + } | |
697 | + buffer += 4; | |
698 | + | |
699 | + // atom type | |
700 | + buffer = write_32(buffer, atom_write_list[i].type_); | |
701 | + if(write_box64) | |
702 | + { | |
703 | + buffer += 8; // box64 | |
704 | + } | |
705 | + | |
706 | + // atom payload | |
707 | + buffer = atom_write_list[i].writer_(atom_write_list[i].parent_, | |
708 | + atom_write_list[i].source_, buffer); | |
709 | + | |
710 | + if(write_box64) | |
711 | + write_64(atom_start + 8, buffer - atom_start); | |
712 | + else | |
713 | + write_32(atom_start, buffer - atom_start); | |
714 | + } | |
715 | + } | |
716 | + | |
717 | + return buffer; | |
718 | +} | |
719 | + | |
720 | +struct tkhd_t | |
721 | +{ | |
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]; | |
730 | + uint16_t layer_; | |
731 | + uint16_t predefined_; | |
732 | + uint16_t volume_; | |
733 | + uint16_t reserved3_; | |
734 | + uint32_t matrix_[9]; | |
735 | + uint32_t width_; | |
736 | + uint32_t height_; | |
737 | +}; | |
738 | + | |
739 | +struct mdhd_t | |
740 | +{ | |
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_; | |
749 | +}; | |
750 | + | |
751 | +struct vmhd_t | |
752 | +{ | |
753 | + unsigned int version_; | |
754 | + unsigned int flags_; | |
755 | + uint16_t graphics_mode_; | |
756 | + uint16_t opcolor_[3]; | |
757 | +}; | |
758 | + | |
759 | +struct hdlr_t | |
760 | +{ | |
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_; | |
768 | + char* name_; | |
769 | +}; | |
770 | + | |
771 | +struct stbl_t | |
772 | +{ | |
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 | |
781 | + | |
782 | + void* stco_inplace_; // newly generated stco (patched inplace) | |
783 | +}; | |
784 | + | |
785 | +struct stts_table_t | |
786 | +{ | |
787 | + uint32_t sample_count_; | |
788 | + uint32_t sample_duration_; | |
789 | +}; | |
790 | + | |
791 | +struct stts_t | |
792 | +{ | |
793 | + unsigned int version_; | |
794 | + unsigned int flags_; | |
795 | + uint32_t entries_; | |
796 | + struct stts_table_t* table_; | |
797 | +}; | |
798 | + | |
799 | +struct stss_t | |
800 | +{ | |
801 | + unsigned int version_; | |
802 | + unsigned int flags_; | |
803 | + uint32_t entries_; | |
804 | + uint32_t* sample_numbers_; | |
805 | +}; | |
806 | + | |
807 | +struct stsc_table_t | |
808 | +{ | |
809 | + uint32_t chunk_; | |
810 | + uint32_t samples_; | |
811 | + uint32_t id_; | |
812 | +}; | |
813 | + | |
814 | +struct stsc_t | |
815 | +{ | |
816 | + unsigned int version_; | |
817 | + unsigned int flags_; | |
818 | + uint32_t entries_; | |
819 | + struct stsc_table_t* table_; | |
820 | +}; | |
821 | + | |
822 | +struct stsz_t | |
823 | +{ | |
824 | + unsigned int version_; | |
825 | + unsigned int flags_; | |
826 | + uint32_t sample_size_; | |
827 | + uint32_t entries_; | |
828 | + uint32_t* sample_sizes_; | |
829 | +}; | |
830 | + | |
831 | +struct stco_t | |
832 | +{ | |
833 | + unsigned int version_; | |
834 | + unsigned int flags_; | |
835 | + uint32_t entries_; | |
836 | + uint64_t* chunk_offsets_; | |
837 | +}; | |
838 | + | |
839 | +struct ctts_table_t | |
840 | +{ | |
841 | + uint32_t sample_count_; | |
842 | + uint32_t sample_offset_; | |
843 | +}; | |
844 | + | |
845 | +struct ctts_t | |
846 | +{ | |
847 | + unsigned int version_; | |
848 | + unsigned int flags_; | |
849 | + uint32_t entries_; | |
850 | + struct ctts_table_t* table_; | |
851 | +}; | |
852 | + | |
853 | +struct minf_t | |
854 | +{ | |
855 | + struct unknown_atom_t* unknown_atoms_; | |
856 | + struct vmhd_t* vmhd_; | |
857 | +// struct dinf_t* dinf_; | |
858 | + struct stbl_t* stbl_; | |
859 | +}; | |
860 | + | |
861 | +struct mdia_t | |
862 | +{ | |
863 | + struct unknown_atom_t* unknown_atoms_; | |
864 | + struct mdhd_t* mdhd_; | |
865 | + struct hdlr_t* hdlr_; | |
866 | + struct minf_t* minf_; | |
867 | +}; | |
868 | + | |
869 | +struct chunks_t | |
870 | +{ | |
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 | |
875 | +}; | |
876 | + | |
877 | +struct samples_t | |
878 | +{ | |
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 | |
883 | +}; | |
884 | + | |
885 | +struct trak_t | |
886 | +{ | |
887 | + struct unknown_atom_t* unknown_atoms_; | |
888 | + struct tkhd_t* tkhd_; | |
889 | + struct mdia_t* mdia_; | |
890 | + | |
891 | + /* temporary indices */ | |
892 | + unsigned int chunks_size_; | |
893 | + struct chunks_t* chunks_; | |
894 | + | |
895 | + unsigned int samples_size_; | |
896 | + struct samples_t* samples_; | |
897 | +}; | |
898 | + | |
899 | +struct mvhd_t | |
900 | +{ | |
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_; | |
907 | + uint32_t rate_; | |
908 | + uint16_t volume_; | |
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_; | |
914 | +}; | |
915 | + | |
916 | +struct moov_t | |
917 | +{ | |
918 | + struct unknown_atom_t* unknown_atoms_; | |
919 | + struct mvhd_t* mvhd_; | |
920 | + unsigned int tracks_; | |
921 | + struct trak_t* traks_[MAX_TRACKS]; | |
922 | +}; | |
923 | + | |
924 | + | |
925 | +static struct tkhd_t* tkhd_init() | |
926 | +{ | |
927 | + struct tkhd_t* tkhd = (struct tkhd_t*)malloc(sizeof(struct tkhd_t)); | |
928 | + | |
929 | + return tkhd; | |
930 | +} | |
931 | + | |
932 | +static void tkhd_exit(struct tkhd_t* tkhd) | |
933 | +{ | |
934 | + free(tkhd); | |
935 | +} | |
936 | + | |
937 | +static void* tkhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
938 | +{ | |
939 | + unsigned int i; | |
940 | + | |
941 | + struct tkhd_t* tkhd = tkhd_init(); | |
942 | + | |
943 | + tkhd->version_ = read_8(buffer + 0); | |
944 | + tkhd->flags_ = read_24(buffer + 1); | |
945 | + if(tkhd->version_ == 0) | |
946 | + { | |
947 | + if(size < 92-8) | |
948 | + return 0; | |
949 | + | |
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); | |
955 | + buffer += 24; | |
956 | + } | |
957 | + else | |
958 | + { | |
959 | + if(size < 104-8) | |
960 | + return 0; | |
961 | + | |
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); | |
967 | + buffer += 36; | |
968 | + } | |
969 | + | |
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); | |
976 | + buffer += 16; | |
977 | + | |
978 | + for(i = 0; i != 9; ++i) | |
979 | + { | |
980 | + tkhd->matrix_[i] = read_32(buffer); | |
981 | + buffer += 4; | |
982 | + } | |
983 | + | |
984 | + tkhd->width_ = read_32(buffer + 0); | |
985 | + tkhd->height_ = read_32(buffer + 4); | |
986 | + | |
987 | + return tkhd; | |
988 | +} | |
989 | + | |
990 | +static unsigned char* tkhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
991 | +{ | |
992 | + struct tkhd_t const* tkhd = atom; | |
993 | + unsigned int i; | |
994 | + | |
995 | + buffer = write_8(buffer, tkhd->version_); | |
996 | + buffer = write_24(buffer, tkhd->flags_); | |
997 | + | |
998 | + if(tkhd->version_ == 0) | |
999 | + { | |
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_); | |
1005 | + } | |
1006 | + else | |
1007 | + { | |
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_); | |
1013 | + } | |
1014 | + | |
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_); | |
1021 | + | |
1022 | + for(i = 0; i != 9; ++i) | |
1023 | + { | |
1024 | + buffer = write_32(buffer, tkhd->matrix_[i]); | |
1025 | + } | |
1026 | + | |
1027 | + buffer = write_32(buffer, tkhd->width_); | |
1028 | + buffer = write_32(buffer, tkhd->height_); | |
1029 | + | |
1030 | + return buffer; | |
1031 | +} | |
1032 | + | |
1033 | +static struct mdhd_t* mdhd_init() | |
1034 | +{ | |
1035 | + struct mdhd_t* mdhd = (struct mdhd_t*)malloc(sizeof(struct mdhd_t)); | |
1036 | + | |
1037 | + return mdhd; | |
1038 | +} | |
1039 | + | |
1040 | +static void mdhd_exit(struct mdhd_t* mdhd) | |
1041 | +{ | |
1042 | + free(mdhd); | |
1043 | +} | |
1044 | + | |
1045 | +static void* mdhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t UNUSED(size)) | |
1046 | +{ | |
1047 | + uint16_t language; | |
1048 | + unsigned int i; | |
1049 | + | |
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) | |
1054 | + { | |
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); | |
1059 | + buffer += 20; | |
1060 | + } | |
1061 | + else | |
1062 | + { | |
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); | |
1067 | + buffer += 32; | |
1068 | + } | |
1069 | + | |
1070 | + language = read_16(buffer + 0); | |
1071 | + for(i = 0; i != 3; ++i) | |
1072 | + { | |
1073 | + mdhd->language_[i] = ((language >> ((2 - i) * 5)) & 0x1f) + 0x60; | |
1074 | + } | |
1075 | + | |
1076 | + mdhd->predefined_ = read_16(buffer + 2); | |
1077 | + | |
1078 | + return mdhd; | |
1079 | +} | |
1080 | + | |
1081 | +static unsigned char* mdhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1082 | +{ | |
1083 | + struct mdhd_t const* mdhd = atom; | |
1084 | + | |
1085 | + buffer = write_8(buffer, mdhd->version_); | |
1086 | + buffer = write_24(buffer, mdhd->flags_); | |
1087 | + | |
1088 | + if(mdhd->version_ == 0) | |
1089 | + { | |
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_); | |
1094 | + } | |
1095 | + else | |
1096 | + { | |
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_); | |
1101 | + } | |
1102 | + | |
1103 | + buffer = write_16(buffer, | |
1104 | + ((mdhd->language_[0] - 0x60) << 10) + | |
1105 | + ((mdhd->language_[1] - 0x60) << 5) + | |
1106 | + ((mdhd->language_[2] - 0x60) << 0)); | |
1107 | + | |
1108 | + buffer = write_16(buffer, mdhd->predefined_); | |
1109 | + | |
1110 | + return buffer; | |
1111 | +} | |
1112 | + | |
1113 | +static struct vmhd_t* vmhd_init() | |
1114 | +{ | |
1115 | + struct vmhd_t* atom = (struct vmhd_t*)malloc(sizeof(struct vmhd_t)); | |
1116 | + | |
1117 | + return atom; | |
1118 | +} | |
1119 | + | |
1120 | +void vmhd_exit(struct vmhd_t* atom) | |
1121 | +{ | |
1122 | + free(atom); | |
1123 | +} | |
1124 | + | |
1125 | +static void* vmhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1126 | +{ | |
1127 | + unsigned int i; | |
1128 | + | |
1129 | + struct vmhd_t* atom; | |
1130 | + | |
1131 | + if(size < 20-8) | |
1132 | + return 0; | |
1133 | + | |
1134 | + atom = vmhd_init(); | |
1135 | + atom->version_ = read_8(buffer + 0); | |
1136 | + atom->flags_ = read_24(buffer + 1); | |
1137 | + | |
1138 | + atom->graphics_mode_ = read_16(buffer + 4); | |
1139 | + buffer += 6; | |
1140 | + for(i = 0; i != 3; ++i) | |
1141 | + { | |
1142 | + atom->opcolor_[i] = read_16(buffer); | |
1143 | + buffer += 2; | |
1144 | + } | |
1145 | + | |
1146 | + return atom; | |
1147 | +} | |
1148 | + | |
1149 | +static unsigned char* vmhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1150 | +{ | |
1151 | + struct vmhd_t const* vmhd = atom; | |
1152 | + unsigned int i; | |
1153 | + | |
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) | |
1158 | + { | |
1159 | + buffer = write_16(buffer, vmhd->opcolor_[i]); | |
1160 | + } | |
1161 | + | |
1162 | + return buffer; | |
1163 | +} | |
1164 | + | |
1165 | +static struct hdlr_t* hdlr_init() | |
1166 | +{ | |
1167 | + struct hdlr_t* atom = (struct hdlr_t*)malloc(sizeof(struct hdlr_t)); | |
1168 | + atom->name_ = 0; | |
1169 | + | |
1170 | + return atom; | |
1171 | +} | |
1172 | + | |
1173 | +static void hdlr_exit(struct hdlr_t* atom) | |
1174 | +{ | |
1175 | + if(atom->name_) | |
1176 | + { | |
1177 | + free(atom->name_); | |
1178 | + } | |
1179 | + free(atom); | |
1180 | +} | |
1181 | + | |
1182 | +static void* hdlr_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1183 | +{ | |
1184 | + struct hdlr_t* atom; | |
1185 | + | |
1186 | + if(size < 8) | |
1187 | + return 0; | |
1188 | + | |
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); | |
1197 | + buffer += 24; | |
1198 | + size -= 24; | |
1199 | + if(size > 0) | |
1200 | + { | |
1201 | + size_t length = (size_t)size; | |
1202 | + atom->name_ = malloc(length + 1); | |
1203 | + if(atom->predefined_ == FOURCC('m', 'h', 'l', 'r')) | |
1204 | + { | |
1205 | + length = read_8(buffer); | |
1206 | + buffer += 1; | |
1207 | + if(size < length) | |
1208 | + length = (size_t)size; | |
1209 | + } | |
1210 | + memcpy(atom->name_, buffer, length); | |
1211 | + atom->name_[length] = '\0'; | |
1212 | + } | |
1213 | + | |
1214 | + return atom; | |
1215 | +} | |
1216 | + | |
1217 | +static unsigned char* hdlr_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1218 | +{ | |
1219 | + struct hdlr_t* hdlr = atom; | |
1220 | + buffer = write_8(buffer, hdlr->version_); | |
1221 | + buffer = write_24(buffer, hdlr->flags_); | |
1222 | + | |
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_); | |
1228 | + if(hdlr->name_) | |
1229 | + { | |
1230 | + char const* p; | |
1231 | + if(hdlr->predefined_ == FOURCC('m', 'h', 'l', 'r')) | |
1232 | + { | |
1233 | + buffer = write_8(buffer, strlen(hdlr->name_)); | |
1234 | + } | |
1235 | + | |
1236 | + for(p = hdlr->name_; *p; ++p) | |
1237 | + buffer = write_8(buffer, *p); | |
1238 | + } | |
1239 | + | |
1240 | + return buffer; | |
1241 | +} | |
1242 | + | |
1243 | +static struct stts_t* stts_init() | |
1244 | +{ | |
1245 | + struct stts_t* atom = (struct stts_t*)malloc(sizeof(struct stts_t)); | |
1246 | + atom->table_ = 0; | |
1247 | + | |
1248 | + return atom; | |
1249 | +} | |
1250 | + | |
1251 | +void stts_exit(struct stts_t* atom) | |
1252 | +{ | |
1253 | + if(atom->table_) | |
1254 | + { | |
1255 | + free(atom->table_); | |
1256 | + } | |
1257 | + free(atom); | |
1258 | +} | |
1259 | + | |
1260 | +static void* stts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1261 | +{ | |
1262 | + unsigned int i; | |
1263 | + | |
1264 | + struct stts_t* atom; | |
1265 | + | |
1266 | + if(size < 8) | |
1267 | + return 0; | |
1268 | + | |
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); | |
1273 | + | |
1274 | + if(size < 8 + atom->entries_ * sizeof(struct stts_table_t)) | |
1275 | + return 0; | |
1276 | + | |
1277 | + buffer += 8; | |
1278 | + | |
1279 | + atom->table_ = (struct stts_table_t*)(malloc(atom->entries_ * sizeof(struct stts_table_t))); | |
1280 | + | |
1281 | + for(i = 0; i != atom->entries_; ++i) | |
1282 | + { | |
1283 | + atom->table_[i].sample_count_ = read_32(buffer + 0); | |
1284 | + atom->table_[i].sample_duration_ = read_32(buffer + 4); | |
1285 | + buffer += 8; | |
1286 | + } | |
1287 | + | |
1288 | + return atom; | |
1289 | +} | |
1290 | + | |
1291 | +static unsigned char* stts_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1292 | +{ | |
1293 | + struct stts_t* stts = atom; | |
1294 | + unsigned int i; | |
1295 | + | |
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) | |
1300 | + { | |
1301 | + buffer = write_32(buffer, stts->table_[i].sample_count_); | |
1302 | + buffer = write_32(buffer, stts->table_[i].sample_duration_); | |
1303 | + } | |
1304 | + | |
1305 | + return buffer; | |
1306 | +} | |
1307 | + | |
1308 | +static unsigned int stts_get_sample(struct stts_t const* stts, uint64_t time) | |
1309 | +{ | |
1310 | + unsigned int stts_index = 0; | |
1311 | + unsigned int stts_count; | |
1312 | + | |
1313 | + unsigned int ret = 0; | |
1314 | + uint64_t time_count = 0; | |
1315 | + | |
1316 | + for(; stts_index != stts->entries_; ++stts_index) | |
1317 | + { | |
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) | |
1321 | + { | |
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; | |
1325 | + break; | |
1326 | + } | |
1327 | + else | |
1328 | + { | |
1329 | + time_count += (uint64_t)sample_duration * (uint64_t)sample_count; | |
1330 | + ret += sample_count; | |
1331 | + } | |
1332 | + } | |
1333 | + return ret; | |
1334 | +} | |
1335 | + | |
1336 | +static uint64_t stts_get_time(struct stts_t const* stts, unsigned int sample) | |
1337 | +{ | |
1338 | + uint64_t ret = 0; | |
1339 | + unsigned int stts_index = 0; | |
1340 | + unsigned int sample_count = 0; | |
1341 | + | |
1342 | + for(;;) | |
1343 | + { | |
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) | |
1347 | + { | |
1348 | + unsigned int stts_count = (sample - sample_count); | |
1349 | + ret += (uint64_t)stts_count * (uint64_t)table_sample_duration; | |
1350 | + break; | |
1351 | + } | |
1352 | + else | |
1353 | + { | |
1354 | + sample_count += table_sample_count; | |
1355 | + ret += (uint64_t)table_sample_count * (uint64_t)table_sample_duration; | |
1356 | + stts_index++; | |
1357 | + } | |
1358 | + } | |
1359 | + return ret; | |
1360 | +} | |
1361 | + | |
1362 | +static uint64_t stts_get_duration(struct stts_t const* stts) | |
1363 | +{ | |
1364 | + uint64_t duration = 0; | |
1365 | + unsigned int i; | |
1366 | + for(i = 0; i != stts->entries_; ++i) | |
1367 | + { | |
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; | |
1371 | + } | |
1372 | + | |
1373 | + return duration; | |
1374 | +} | |
1375 | + | |
1376 | +static unsigned int stts_get_samples(struct stts_t const* stts) | |
1377 | +{ | |
1378 | + unsigned int samples = 0; | |
1379 | + unsigned int entries = stts->entries_; | |
1380 | + unsigned int i; | |
1381 | + for(i = 0; i != entries; ++i) | |
1382 | + { | |
1383 | + unsigned int sample_count = stts->table_[i].sample_count_; | |
1384 | +// unsigned int sample_duration = stts->table_[i].sample_duration_; | |
1385 | + samples += sample_count; | |
1386 | + } | |
1387 | + | |
1388 | + return samples; | |
1389 | +} | |
1390 | + | |
1391 | +static struct stss_t* stss_init() | |
1392 | +{ | |
1393 | + struct stss_t* atom = (struct stss_t*)malloc(sizeof(struct stss_t)); | |
1394 | + atom->sample_numbers_ = 0; | |
1395 | + | |
1396 | + return atom; | |
1397 | +} | |
1398 | + | |
1399 | +void stss_exit(struct stss_t* atom) | |
1400 | +{ | |
1401 | + if(atom->sample_numbers_) | |
1402 | + { | |
1403 | + free(atom->sample_numbers_); | |
1404 | + } | |
1405 | + free(atom); | |
1406 | +} | |
1407 | + | |
1408 | +static void* stss_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1409 | +{ | |
1410 | + unsigned int i; | |
1411 | + | |
1412 | + struct stss_t* atom; | |
1413 | + | |
1414 | + if(size < 8) | |
1415 | + return 0; | |
1416 | + | |
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); | |
1421 | + | |
1422 | + if(size < 8 + atom->entries_ * sizeof(uint32_t)) | |
1423 | + return 0; | |
1424 | + | |
1425 | + buffer += 8; | |
1426 | + | |
1427 | + atom->sample_numbers_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t)); | |
1428 | + | |
1429 | + for(i = 0; i != atom->entries_; ++i) | |
1430 | + { | |
1431 | + atom->sample_numbers_[i] = read_32(buffer); | |
1432 | + buffer += 4; | |
1433 | + } | |
1434 | + | |
1435 | + return atom; | |
1436 | +} | |
1437 | + | |
1438 | +static unsigned char* stss_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1439 | +{ | |
1440 | + struct stss_t const* stss = atom; | |
1441 | + unsigned int i; | |
1442 | + | |
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) | |
1447 | + { | |
1448 | + buffer = write_32(buffer, stss->sample_numbers_[i]); | |
1449 | + } | |
1450 | + | |
1451 | + return buffer; | |
1452 | +} | |
1453 | + | |
1454 | +static unsigned int stss_get_nearest_keyframe(struct stss_t const* stss, unsigned int sample) | |
1455 | +{ | |
1456 | + // scan the sync samples to find the key frame that precedes the sample number | |
1457 | + unsigned int i; | |
1458 | + unsigned int table_sample = 0; | |
1459 | + for(i = 0; i != stss->entries_; ++i) | |
1460 | + { | |
1461 | + table_sample = stss->sample_numbers_[i]; | |
1462 | + if(table_sample >= sample) | |
1463 | + break; | |
1464 | + } | |
1465 | + if(table_sample == sample) | |
1466 | + return table_sample; | |
1467 | + else | |
1468 | + return stss->sample_numbers_[i - 1]; | |
1469 | +} | |
1470 | + | |
1471 | +static struct stsc_t* stsc_init() | |
1472 | +{ | |
1473 | + struct stsc_t* atom = (struct stsc_t*)malloc(sizeof(struct stsc_t)); | |
1474 | + atom->table_ = 0; | |
1475 | + | |
1476 | + return atom; | |
1477 | +} | |
1478 | + | |
1479 | +static void stsc_exit(struct stsc_t* atom) | |
1480 | +{ | |
1481 | + if(atom->table_) | |
1482 | + { | |
1483 | + free(atom->table_); | |
1484 | + } | |
1485 | + free(atom); | |
1486 | +} | |
1487 | + | |
1488 | +static void* stsc_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1489 | +{ | |
1490 | + unsigned int i; | |
1491 | + | |
1492 | + struct stsc_t* atom; | |
1493 | + | |
1494 | + if(size < 8) | |
1495 | + return 0; | |
1496 | + | |
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); | |
1501 | + | |
1502 | + if(size < 8 + atom->entries_ * sizeof(struct stsc_table_t)) | |
1503 | + return 0; | |
1504 | + | |
1505 | + buffer += 8; | |
1506 | + | |
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))); | |
1510 | + | |
1511 | + for(i = 0; i != atom->entries_; ++i) | |
1512 | + { | |
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); | |
1516 | + buffer += 12; | |
1517 | + } | |
1518 | + | |
1519 | + return atom; | |
1520 | +} | |
1521 | + | |
1522 | +static unsigned char* stsc_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1523 | +{ | |
1524 | + struct stsc_t* stsc = atom; | |
1525 | + unsigned int i; | |
1526 | + | |
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) | |
1531 | + { | |
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_); | |
1535 | + } | |
1536 | + | |
1537 | + return buffer; | |
1538 | +} | |
1539 | + | |
1540 | +static struct stsz_t* stsz_init() | |
1541 | +{ | |
1542 | + struct stsz_t* atom = (struct stsz_t*)malloc(sizeof(struct stsz_t)); | |
1543 | + atom->sample_sizes_ = 0; | |
1544 | + | |
1545 | + return atom; | |
1546 | +} | |
1547 | + | |
1548 | +static void stsz_exit(struct stsz_t* atom) | |
1549 | +{ | |
1550 | + if(atom->sample_sizes_) | |
1551 | + { | |
1552 | + free(atom->sample_sizes_); | |
1553 | + } | |
1554 | + free(atom); | |
1555 | +} | |
1556 | + | |
1557 | +static void* stsz_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1558 | +{ | |
1559 | + unsigned int i; | |
1560 | + | |
1561 | + struct stsz_t* atom; | |
1562 | + | |
1563 | + if(size < 12) | |
1564 | + { | |
1565 | + printf("Error: not enough bytes for stsz atom\n"); | |
1566 | + return 0; | |
1567 | + } | |
1568 | + | |
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); | |
1574 | + buffer += 12; | |
1575 | + | |
1576 | + // fix for clayton.mp4, it mistakenly says there is 1 entry | |
1577 | + if(atom->sample_size_ && atom->entries_) | |
1578 | + atom->entries_ = 0; | |
1579 | + | |
1580 | + if(size < 12 + atom->entries_ * sizeof(uint32_t)) | |
1581 | + { | |
1582 | + printf("Error: stsz.entries don't match with size\n"); | |
1583 | + stsz_exit(atom); | |
1584 | + return 0; | |
1585 | + } | |
1586 | + | |
1587 | + if(!atom->sample_size_) | |
1588 | + { | |
1589 | + atom->sample_sizes_ = (uint32_t*)malloc(atom->entries_ * sizeof(uint32_t)); | |
1590 | + for(i = 0; i != atom->entries_; ++i) | |
1591 | + { | |
1592 | + atom->sample_sizes_[i] = read_32(buffer); | |
1593 | + buffer += 4; | |
1594 | + } | |
1595 | + } | |
1596 | + | |
1597 | + return atom; | |
1598 | +} | |
1599 | + | |
1600 | +static unsigned char* stsz_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1601 | +{ | |
1602 | + struct stsz_t* stsz = atom; | |
1603 | + unsigned int i; | |
1604 | + unsigned int entries = stsz->sample_size_ ? 0 : stsz->entries_; | |
1605 | + | |
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) | |
1611 | + { | |
1612 | + buffer = write_32(buffer, stsz->sample_sizes_[i]); | |
1613 | + } | |
1614 | + | |
1615 | + return buffer; | |
1616 | +} | |
1617 | + | |
1618 | +static struct stco_t* stco_init() | |
1619 | +{ | |
1620 | + struct stco_t* atom = (struct stco_t*)malloc(sizeof(struct stco_t)); | |
1621 | + atom->chunk_offsets_ = 0; | |
1622 | + | |
1623 | + return atom; | |
1624 | +} | |
1625 | + | |
1626 | +static void stco_exit(struct stco_t* atom) | |
1627 | +{ | |
1628 | + if(atom->chunk_offsets_) | |
1629 | + { | |
1630 | + free(atom->chunk_offsets_); | |
1631 | + } | |
1632 | + free(atom); | |
1633 | +} | |
1634 | + | |
1635 | +static void* stco_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1636 | +{ | |
1637 | + unsigned int i; | |
1638 | + | |
1639 | + struct stco_t* atom; | |
1640 | + | |
1641 | + if(size < 8) | |
1642 | + return 0; | |
1643 | + | |
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); | |
1648 | + buffer += 8; | |
1649 | + | |
1650 | + if(size < 8 + atom->entries_ * sizeof(uint32_t)) | |
1651 | + return 0; | |
1652 | + | |
1653 | + atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t)); | |
1654 | + for(i = 0; i != atom->entries_; ++i) | |
1655 | + { | |
1656 | + atom->chunk_offsets_[i] = read_32(buffer); | |
1657 | + buffer += 4; | |
1658 | + } | |
1659 | + | |
1660 | + return atom; | |
1661 | +} | |
1662 | + | |
1663 | +static void* co64_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1664 | +{ | |
1665 | + unsigned int i; | |
1666 | + | |
1667 | + struct stco_t* atom; | |
1668 | + | |
1669 | + if(size < 8) | |
1670 | + return 0; | |
1671 | + | |
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); | |
1676 | + buffer += 8; | |
1677 | + | |
1678 | + if(size < 8 + atom->entries_ * sizeof(uint64_t)) | |
1679 | + return 0; | |
1680 | + | |
1681 | + atom->chunk_offsets_ = (uint64_t*)malloc(atom->entries_ * sizeof(uint64_t)); | |
1682 | + for(i = 0; i != atom->entries_; ++i) | |
1683 | + { | |
1684 | + atom->chunk_offsets_[i] = read_64(buffer); | |
1685 | + buffer += 8; | |
1686 | + } | |
1687 | + | |
1688 | + return atom; | |
1689 | +} | |
1690 | + | |
1691 | +static unsigned char* stco_write(void* parent, void* atom, unsigned char* buffer) | |
1692 | +{ | |
1693 | + struct stbl_t* stbl = parent; | |
1694 | + struct stco_t* stco = atom; | |
1695 | + unsigned int i; | |
1696 | + | |
1697 | + stbl->stco_inplace_ = buffer; // newly generated stco (patched inplace) | |
1698 | + | |
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) | |
1703 | + { | |
1704 | + buffer = write_32(buffer, (uint32_t)(stco->chunk_offsets_[i])); | |
1705 | + } | |
1706 | + | |
1707 | + return buffer; | |
1708 | +} | |
1709 | + | |
1710 | +static void stco_shift_offsets(struct stco_t* stco, int offset) | |
1711 | +{ | |
1712 | + unsigned int i; | |
1713 | + for(i = 0; i != stco->entries_; ++i) | |
1714 | + stco->chunk_offsets_[i] += offset; | |
1715 | +} | |
1716 | + | |
1717 | +static void stco_shift_offsets_inplace(unsigned char* stco, int offset) | |
1718 | +{ | |
1719 | + unsigned int entries = read_32(stco + 4); | |
1720 | + unsigned int* table = (unsigned int*)(stco + 8); | |
1721 | + unsigned int i; | |
1722 | + for(i = 0; i != entries; ++i) | |
1723 | + write_32((unsigned char*)&table[i], (read_32((unsigned char*)&table[i]) + offset)); | |
1724 | +} | |
1725 | + | |
1726 | +static struct ctts_t* ctts_init() | |
1727 | +{ | |
1728 | + struct ctts_t* atom = (struct ctts_t*)malloc(sizeof(struct ctts_t)); | |
1729 | + atom->table_ = 0; | |
1730 | + | |
1731 | + return atom; | |
1732 | +} | |
1733 | + | |
1734 | +static void ctts_exit(struct ctts_t* atom) | |
1735 | +{ | |
1736 | + if(atom->table_) | |
1737 | + { | |
1738 | + free(atom->table_); | |
1739 | + } | |
1740 | + free(atom); | |
1741 | +} | |
1742 | + | |
1743 | +static void* ctts_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1744 | +{ | |
1745 | + unsigned int i; | |
1746 | + | |
1747 | + struct ctts_t* atom; | |
1748 | + | |
1749 | + if(size < 8) | |
1750 | + return 0; | |
1751 | + | |
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); | |
1756 | + | |
1757 | + if(size < 8 + atom->entries_ * sizeof(struct ctts_table_t)) | |
1758 | + return 0; | |
1759 | + | |
1760 | + buffer += 8; | |
1761 | + | |
1762 | + atom->table_ = (struct ctts_table_t*)(malloc(atom->entries_ * sizeof(struct ctts_table_t))); | |
1763 | + | |
1764 | + for(i = 0; i != atom->entries_; ++i) | |
1765 | + { | |
1766 | + atom->table_[i].sample_count_ = read_32(buffer + 0); | |
1767 | + atom->table_[i].sample_offset_ = read_32(buffer + 4); | |
1768 | + buffer += 8; | |
1769 | + } | |
1770 | + | |
1771 | + return atom; | |
1772 | +} | |
1773 | + | |
1774 | +static unsigned char* ctts_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1775 | +{ | |
1776 | + struct ctts_t const* ctts = atom; | |
1777 | + unsigned int i; | |
1778 | + | |
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) | |
1783 | + { | |
1784 | + buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_count_)); | |
1785 | + buffer = write_32(buffer, (uint32_t)(ctts->table_[i].sample_offset_)); | |
1786 | + } | |
1787 | + | |
1788 | + return buffer; | |
1789 | +} | |
1790 | + | |
1791 | +static unsigned int ctts_get_samples(struct ctts_t const* ctts) | |
1792 | +{ | |
1793 | + unsigned int samples = 0; | |
1794 | + unsigned int entries = ctts->entries_; | |
1795 | + unsigned int i; | |
1796 | + for(i = 0; i != entries; ++i) | |
1797 | + { | |
1798 | + unsigned int sample_count = ctts->table_[i].sample_count_; | |
1799 | +// unsigned int sample_offset = ctts->table_[i].sample_offset_; | |
1800 | + samples += sample_count; | |
1801 | + } | |
1802 | + | |
1803 | + return samples; | |
1804 | +} | |
1805 | + | |
1806 | +static struct stbl_t* stbl_init() | |
1807 | +{ | |
1808 | + struct stbl_t* atom = (struct stbl_t*)malloc(sizeof(struct stbl_t)); | |
1809 | + atom->unknown_atoms_ = 0; | |
1810 | + atom->stts_ = 0; | |
1811 | + atom->stss_ = 0; | |
1812 | + atom->stsc_ = 0; | |
1813 | + atom->stsz_ = 0; | |
1814 | + atom->stco_ = 0; | |
1815 | + atom->ctts_ = 0; | |
1816 | + | |
1817 | + return atom; | |
1818 | +} | |
1819 | + | |
1820 | +static void stbl_exit(struct stbl_t* atom) | |
1821 | +{ | |
1822 | + if(atom->unknown_atoms_) | |
1823 | + { | |
1824 | + unknown_atom_exit(atom->unknown_atoms_); | |
1825 | + } | |
1826 | + if(atom->stts_) | |
1827 | + { | |
1828 | + stts_exit(atom->stts_); | |
1829 | + } | |
1830 | + if(atom->stss_) | |
1831 | + { | |
1832 | + stss_exit(atom->stss_); | |
1833 | + } | |
1834 | + if(atom->stsc_) | |
1835 | + { | |
1836 | + stsc_exit(atom->stsc_); | |
1837 | + } | |
1838 | + if(atom->stsz_) | |
1839 | + { | |
1840 | + stsz_exit(atom->stsz_); | |
1841 | + } | |
1842 | + if(atom->stco_) | |
1843 | + { | |
1844 | + stco_exit(atom->stco_); | |
1845 | + } | |
1846 | + if(atom->ctts_) | |
1847 | + { | |
1848 | + ctts_exit(atom->ctts_); | |
1849 | + } | |
1850 | + | |
1851 | + free(atom); | |
1852 | +} | |
1853 | + | |
1854 | +static int stbl_add_stts(void* parent, void* child) | |
1855 | +{ | |
1856 | + struct stbl_t* stbl = parent; | |
1857 | + stbl->stts_ = child; | |
1858 | + | |
1859 | + return 1; | |
1860 | +} | |
1861 | + | |
1862 | +static int stbl_add_stss(void* parent, void* child) | |
1863 | +{ | |
1864 | + struct stbl_t* stbl = parent; | |
1865 | + stbl->stss_ = child; | |
1866 | + | |
1867 | + return 1; | |
1868 | +} | |
1869 | + | |
1870 | +static int stbl_add_stsc(void* parent, void* child) | |
1871 | +{ | |
1872 | + struct stbl_t* stbl = parent; | |
1873 | + stbl->stsc_ = child; | |
1874 | + | |
1875 | + return 1; | |
1876 | +} | |
1877 | + | |
1878 | +static int stbl_add_stsz(void* parent, void* child) | |
1879 | +{ | |
1880 | + struct stbl_t* stbl = parent; | |
1881 | + stbl->stsz_ = child; | |
1882 | + | |
1883 | + return 1; | |
1884 | +} | |
1885 | + | |
1886 | +static int stbl_add_stco(void* parent, void* child) | |
1887 | +{ | |
1888 | + struct stbl_t* stbl = parent; | |
1889 | + stbl->stco_ = child; | |
1890 | + | |
1891 | + return 1; | |
1892 | +} | |
1893 | + | |
1894 | +static int stbl_add_ctts(void* parent, void* child) | |
1895 | +{ | |
1896 | + struct stbl_t* stbl = parent; | |
1897 | + stbl->ctts_ = child; | |
1898 | + | |
1899 | + return 1; | |
1900 | +} | |
1901 | + | |
1902 | +static void* stbl_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
1903 | +{ | |
1904 | + struct stbl_t* atom = stbl_init(); | |
1905 | + | |
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 }, | |
1914 | + }; | |
1915 | + | |
1916 | + int result = atom_reader(atom_read_list, | |
1917 | + sizeof(atom_read_list) / sizeof(atom_read_list[0]), | |
1918 | + atom, | |
1919 | + buffer, size); | |
1920 | + | |
1921 | + // check for mandatory atoms | |
1922 | + if(!atom->stts_) | |
1923 | + { | |
1924 | + printf("stbl: missing stts\n"); | |
1925 | + result = 0; | |
1926 | + } | |
1927 | + | |
1928 | + if(!atom->stsc_) | |
1929 | + { | |
1930 | + printf("stbl: missing stsc\n"); | |
1931 | + result = 0; | |
1932 | + } | |
1933 | + | |
1934 | + if(!atom->stsz_) | |
1935 | + { | |
1936 | + printf("stbl: missing stsz\n"); | |
1937 | + result = 0; | |
1938 | + } | |
1939 | + | |
1940 | + if(!atom->stco_) | |
1941 | + { | |
1942 | + printf("stbl: missing stco\n"); | |
1943 | + result = 0; | |
1944 | + } | |
1945 | + | |
1946 | + if(!result) | |
1947 | + { | |
1948 | + stbl_exit(atom); | |
1949 | + return 0; | |
1950 | + } | |
1951 | + | |
1952 | + return atom; | |
1953 | +} | |
1954 | + | |
1955 | +static unsigned char* stbl_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
1956 | +{ | |
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 }, | |
1965 | + }; | |
1966 | + | |
1967 | + buffer = atom_writer(stbl->unknown_atoms_, | |
1968 | + atom_write_list, | |
1969 | + sizeof(atom_write_list) / sizeof(atom_write_list[0]), | |
1970 | + buffer); | |
1971 | + | |
1972 | + return buffer; | |
1973 | +} | |
1974 | + | |
1975 | +static unsigned int stbl_get_nearest_keyframe(struct stbl_t const* stbl, unsigned int sample) | |
1976 | +{ | |
1977 | + // If the sync atom is not present, all samples are implicit sync samples. | |
1978 | + if(!stbl->stss_) | |
1979 | + return sample; | |
1980 | + | |
1981 | + return stss_get_nearest_keyframe(stbl->stss_, sample); | |
1982 | +} | |
1983 | + | |
1984 | +static struct minf_t* minf_init() | |
1985 | +{ | |
1986 | + struct minf_t* atom = (struct minf_t*)malloc(sizeof(struct minf_t)); | |
1987 | + atom->unknown_atoms_ = 0; | |
1988 | + atom->vmhd_ = 0; | |
1989 | + atom->stbl_ = 0; | |
1990 | + | |
1991 | + return atom; | |
1992 | +} | |
1993 | + | |
1994 | +static void minf_exit(struct minf_t* atom) | |
1995 | +{ | |
1996 | + if(atom->unknown_atoms_) | |
1997 | + { | |
1998 | + unknown_atom_exit(atom->unknown_atoms_); | |
1999 | + } | |
2000 | + if(atom->vmhd_) | |
2001 | + { | |
2002 | + vmhd_exit(atom->vmhd_); | |
2003 | + } | |
2004 | + if(atom->stbl_) | |
2005 | + { | |
2006 | + stbl_exit(atom->stbl_); | |
2007 | + } | |
2008 | + free(atom); | |
2009 | +} | |
2010 | + | |
2011 | +static int minf_add_vmhd(void* parent, void* child) | |
2012 | +{ | |
2013 | + struct minf_t* minf = parent; | |
2014 | + minf->vmhd_ = child; | |
2015 | + | |
2016 | + return 1; | |
2017 | +} | |
2018 | + | |
2019 | +static int minf_add_stbl(void* parent, void* child) | |
2020 | +{ | |
2021 | + struct minf_t* minf = parent; | |
2022 | + minf->stbl_ = child; | |
2023 | + | |
2024 | + return 1; | |
2025 | +} | |
2026 | + | |
2027 | +static void* minf_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
2028 | +{ | |
2029 | + struct minf_t* atom = minf_init(); | |
2030 | + | |
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 } | |
2034 | + }; | |
2035 | + | |
2036 | + int result = atom_reader(atom_read_list, | |
2037 | + sizeof(atom_read_list) / sizeof(atom_read_list[0]), | |
2038 | + atom, | |
2039 | + buffer, size); | |
2040 | + | |
2041 | + // check for mandatory atoms | |
2042 | + if(!atom->stbl_) | |
2043 | + { | |
2044 | + printf("minf: missing stbl\n"); | |
2045 | + result = 0; | |
2046 | + } | |
2047 | + | |
2048 | + if(!result) | |
2049 | + { | |
2050 | + minf_exit(atom); | |
2051 | + return 0; | |
2052 | + } | |
2053 | + | |
2054 | + return atom; | |
2055 | +} | |
2056 | + | |
2057 | +static unsigned char* minf_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
2058 | +{ | |
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 } | |
2063 | + }; | |
2064 | + | |
2065 | + buffer = atom_writer(minf->unknown_atoms_, | |
2066 | + atom_write_list, | |
2067 | + sizeof(atom_write_list) / sizeof(atom_write_list[0]), | |
2068 | + buffer); | |
2069 | + | |
2070 | + return buffer; | |
2071 | +} | |
2072 | + | |
2073 | +static struct mdia_t* mdia_init() | |
2074 | +{ | |
2075 | + struct mdia_t* atom = (struct mdia_t*)malloc(sizeof(struct mdia_t)); | |
2076 | + atom->unknown_atoms_ = 0; | |
2077 | + atom->mdhd_ = 0; | |
2078 | + atom->hdlr_ = 0; | |
2079 | + atom->minf_ = 0; | |
2080 | + | |
2081 | + return atom; | |
2082 | +} | |
2083 | + | |
2084 | +static void mdia_exit(struct mdia_t* atom) | |
2085 | +{ | |
2086 | + if(atom->unknown_atoms_) | |
2087 | + { | |
2088 | + unknown_atom_exit(atom->unknown_atoms_); | |
2089 | + } | |
2090 | + if(atom->mdhd_) | |
2091 | + { | |
2092 | + mdhd_exit(atom->mdhd_); | |
2093 | + } | |
2094 | + if(atom->hdlr_) | |
2095 | + { | |
2096 | + hdlr_exit(atom->hdlr_); | |
2097 | + } | |
2098 | + if(atom->minf_) | |
2099 | + { | |
2100 | + minf_exit(atom->minf_); | |
2101 | + } | |
2102 | + free(atom); | |
2103 | +} | |
2104 | + | |
2105 | +static int mdia_add_mdhd(void* parent, void* child) | |
2106 | +{ | |
2107 | + struct mdia_t* mdia = parent; | |
2108 | + mdia->mdhd_ = child; | |
2109 | + | |
2110 | + return 1; | |
2111 | +} | |
2112 | + | |
2113 | +static int mdia_add_hdlr(void* parent, void* child) | |
2114 | +{ | |
2115 | + struct mdia_t* mdia = parent; | |
2116 | + mdia->hdlr_ = child; | |
2117 | + | |
2118 | + return 1; | |
2119 | +} | |
2120 | + | |
2121 | +static int mdia_add_minf(void* parent, void* child) | |
2122 | +{ | |
2123 | + struct mdia_t* mdia = parent; | |
2124 | + mdia->minf_ = child; | |
2125 | + | |
2126 | + return 1; | |
2127 | +} | |
2128 | + | |
2129 | +static void* mdia_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
2130 | +{ | |
2131 | + struct mdia_t* atom = mdia_init(); | |
2132 | + | |
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 } | |
2137 | + }; | |
2138 | + | |
2139 | + int result = atom_reader(atom_read_list, | |
2140 | + sizeof(atom_read_list) / sizeof(atom_read_list[0]), | |
2141 | + atom, | |
2142 | + buffer, size); | |
2143 | + | |
2144 | + // check for mandatory atoms | |
2145 | + if(!atom->mdhd_) | |
2146 | + { | |
2147 | + printf("mdia: missing mdhd\n"); | |
2148 | + result = 0; | |
2149 | + } | |
2150 | + | |
2151 | + if(!atom->hdlr_) | |
2152 | + { | |
2153 | + printf("mdia: missing hdlr\n"); | |
2154 | + result = 0; | |
2155 | + } | |
2156 | + | |
2157 | + if(!atom->minf_) | |
2158 | + { | |
2159 | + printf("mdia: missing minf\n"); | |
2160 | + result = 0; | |
2161 | + } | |
2162 | + | |
2163 | + if(!result) | |
2164 | + { | |
2165 | + mdia_exit(atom); | |
2166 | + return 0; | |
2167 | + } | |
2168 | + | |
2169 | + return atom; | |
2170 | +} | |
2171 | + | |
2172 | +static unsigned char* mdia_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
2173 | +{ | |
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 } | |
2179 | + }; | |
2180 | + | |
2181 | + buffer = atom_writer(mdia->unknown_atoms_, | |
2182 | + atom_write_list, | |
2183 | + sizeof(atom_write_list) / sizeof(atom_write_list[0]), | |
2184 | + buffer); | |
2185 | + | |
2186 | + return buffer; | |
2187 | +} | |
2188 | + | |
2189 | +void trak_build_index(struct trak_t* trak) | |
2190 | +{ | |
2191 | + struct stco_t const* stco = trak->mdia_->minf_->stbl_->stco_; | |
2192 | + | |
2193 | + trak->chunks_size_ = stco->entries_; | |
2194 | + trak->chunks_ = malloc(trak->chunks_size_ * sizeof(struct chunks_t)); | |
2195 | + | |
2196 | + { | |
2197 | + unsigned int i; | |
2198 | + for(i = 0; i != trak->chunks_size_; ++i) | |
2199 | + { | |
2200 | + trak->chunks_[i].pos_ = stco->chunk_offsets_[i]; | |
2201 | + } | |
2202 | + } | |
2203 | + | |
2204 | + // process chunkmap: | |
2205 | + { | |
2206 | + struct stsc_t const* stsc = trak->mdia_->minf_->stbl_->stsc_; | |
2207 | + unsigned int last = trak->chunks_size_; | |
2208 | + unsigned int i = stsc->entries_; | |
2209 | + while(i > 0) | |
2210 | + { | |
2211 | + unsigned int j; | |
2212 | + | |
2213 | + --i; | |
2214 | + | |
2215 | + for(j = stsc->table_[i].chunk_; j < last; j++) | |
2216 | + { | |
2217 | + trak->chunks_[j].id_ = stsc->table_[i].id_; | |
2218 | + trak->chunks_[j].size_ = stsc->table_[i].samples_; | |
2219 | + } | |
2220 | + last = stsc->table_[i].chunk_; | |
2221 | + } | |
2222 | + } | |
2223 | + | |
2224 | + // calc pts of chunks: | |
2225 | + { | |
2226 | + struct stsz_t const* stsz = trak->mdia_->minf_->stbl_->stsz_; | |
2227 | + unsigned int sample_size = stsz->sample_size_; | |
2228 | + unsigned int s = 0; | |
2229 | + { | |
2230 | + unsigned int j; | |
2231 | + for(j = 0; j < trak->chunks_size_; j++) | |
2232 | + { | |
2233 | + trak->chunks_[j].sample_ = s; | |
2234 | + s += trak->chunks_[j].size_; | |
2235 | + } | |
2236 | + } | |
2237 | + | |
2238 | + if(sample_size == 0) | |
2239 | + { | |
2240 | + trak->samples_size_ = stsz->entries_; | |
2241 | + } | |
2242 | + else | |
2243 | + { | |
2244 | + trak->samples_size_ = s; | |
2245 | + } | |
2246 | + | |
2247 | + trak->samples_ = malloc(trak->samples_size_ * sizeof(struct samples_t)); | |
2248 | + | |
2249 | + if(sample_size == 0) | |
2250 | + { | |
2251 | + unsigned int i; | |
2252 | + for(i = 0; i != trak->samples_size_ ; ++i) | |
2253 | + trak->samples_[i].size_ = stsz->sample_sizes_[i]; | |
2254 | + } | |
2255 | + else | |
2256 | + { | |
2257 | + unsigned int i; | |
2258 | + for(i = 0; i != trak->samples_size_ ; ++i) | |
2259 | + trak->samples_[i].size_ = sample_size; | |
2260 | + } | |
2261 | + } | |
2262 | + | |
2263 | +// i = 0; | |
2264 | +// for (j = 0; j < trak->durmap_size; j++) | |
2265 | +// i += trak->durmap[j].num; | |
2266 | +// if (i != s) { | |
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; | |
2270 | +// } | |
2271 | + | |
2272 | + // calc pts: | |
2273 | + { | |
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_; | |
2278 | + unsigned int j; | |
2279 | + for(j = 0; j < entries; j++) | |
2280 | + { | |
2281 | + unsigned int i; | |
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++) | |
2285 | + { | |
2286 | + trak->samples_[s].pts_ = pts; | |
2287 | + ++s; | |
2288 | + pts += sample_duration; | |
2289 | + } | |
2290 | + } | |
2291 | + } | |
2292 | + | |
2293 | + // calc composition times: | |
2294 | + { | |
2295 | + struct ctts_t const* ctts = trak->mdia_->minf_->stbl_->ctts_; | |
2296 | + if(ctts) | |
2297 | + { | |
2298 | + unsigned int s = 0; | |
2299 | + unsigned int entries = ctts->entries_; | |
2300 | + unsigned int j; | |
2301 | + for(j = 0; j != entries; j++) | |
2302 | + { | |
2303 | + unsigned int i; | |
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++) | |
2307 | + { | |
2308 | + trak->samples_[s].cto_ = sample_offset; | |
2309 | + ++s; | |
2310 | + } | |
2311 | + } | |
2312 | + } | |
2313 | + } | |
2314 | + | |
2315 | + // calc sample offsets | |
2316 | + { | |
2317 | + unsigned int s = 0; | |
2318 | + unsigned int j; | |
2319 | + for(j = 0; j != trak->chunks_size_; j++) | |
2320 | + { | |
2321 | + uint64_t pos = trak->chunks_[j].pos_; | |
2322 | + unsigned int i; | |
2323 | + for(i = 0; i != trak->chunks_[j].size_; i++) | |
2324 | + { | |
2325 | + trak->samples_[s].pos_ = pos; | |
2326 | + pos += trak->samples_[s].size_; | |
2327 | + ++s; | |
2328 | + } | |
2329 | + } | |
2330 | + } | |
2331 | +} | |
2332 | + | |
2333 | +void trak_update_index(struct trak_t* trak, unsigned int start, unsigned int end) | |
2334 | +{ | |
2335 | + // write samples [start,end> | |
2336 | + | |
2337 | + // stts = [entries * [sample_count, sample_duration] | |
2338 | + { | |
2339 | + struct stts_t* stts = trak->mdia_->minf_->stbl_->stts_; | |
2340 | + | |
2341 | + unsigned int entries = 0; | |
2342 | + unsigned int s; | |
2343 | + | |
2344 | + for(s = start; s != end; ++s) | |
2345 | + { | |
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) | |
2350 | + { | |
2351 | + if((trak->samples_[s + 1].pts_ - trak->samples_[s].pts_) != sample_duration) | |
2352 | + break; | |
2353 | + ++sample_count; | |
2354 | + ++s; | |
2355 | + } | |
2356 | + stts->table_[entries].sample_count_ = sample_count; | |
2357 | + stts->table_[entries].sample_duration_ = sample_duration; | |
2358 | + ++entries; | |
2359 | + } | |
2360 | + stts->entries_ = entries; | |
2361 | + | |
2362 | + if(stts_get_samples(stts) != end - start) | |
2363 | + { | |
2364 | + printf("ERROR: stts_get_samples=%d, should be %d\n", | |
2365 | + stts_get_samples(stts), end - start); | |
2366 | + } | |
2367 | + } | |
2368 | + | |
2369 | + // ctts = [entries * [sample_count, sample_offset] | |
2370 | + { | |
2371 | + struct ctts_t* ctts = trak->mdia_->minf_->stbl_->ctts_; | |
2372 | + if(ctts) | |
2373 | + { | |
2374 | + unsigned int entries = 0; | |
2375 | + unsigned int s; | |
2376 | + | |
2377 | + for(s = start; s != end; ++s) | |
2378 | + { | |
2379 | + unsigned int sample_count = 1; | |
2380 | + unsigned int sample_offset = trak->samples_[s].cto_; | |
2381 | + while(s != end - 1) | |
2382 | + { | |
2383 | + if(trak->samples_[s + 1].cto_ != sample_offset) | |
2384 | + break; | |
2385 | + ++sample_count; | |
2386 | + ++s; | |
2387 | + } | |
2388 | + // write entry | |
2389 | + ctts->table_[entries].sample_count_ = sample_count; | |
2390 | + ctts->table_[entries].sample_offset_ = sample_offset; | |
2391 | + ++entries; | |
2392 | + } | |
2393 | + ctts->entries_ = entries; | |
2394 | + if(ctts_get_samples(ctts) != end - start) | |
2395 | + { | |
2396 | + printf("ERROR: ctts_get_samples=%d, should be %d\n", | |
2397 | + ctts_get_samples(ctts), end - start); | |
2398 | + } | |
2399 | + } | |
2400 | + } | |
2401 | + | |
2402 | + // process chunkmap: | |
2403 | + { | |
2404 | + struct stsc_t* stsc = trak->mdia_->minf_->stbl_->stsc_; | |
2405 | + unsigned int i; | |
2406 | + | |
2407 | + for(i = 0; i != trak->chunks_size_; ++i) | |
2408 | + { | |
2409 | + if(trak->chunks_[i].sample_ + trak->chunks_[i].size_ > start) | |
2410 | + break; | |
2411 | + } | |
2412 | + | |
2413 | + { | |
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) | |
2419 | + { | |
2420 | + unsigned int samples = | |
2421 | + trak->chunks_[i].sample_ + trak->chunks_[i].size_ - start; | |
2422 | + unsigned int id = trak->chunks_[i].id_; | |
2423 | + | |
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; | |
2428 | + ++stsc_entries; | |
2429 | + | |
2430 | + if(i != trak->chunks_size_) | |
2431 | + { | |
2432 | + for(i += 1; i != trak->chunks_size_; ++i) | |
2433 | + { | |
2434 | + if(trak->chunks_[i].sample_ >= end) | |
2435 | + break; | |
2436 | + | |
2437 | + if(trak->chunks_[i].size_ != samples) | |
2438 | + { | |
2439 | + samples = trak->chunks_[i].size_; | |
2440 | + id = trak->chunks_[i].id_; | |
2441 | + | |
2442 | + stsc->table_[stsc_entries].chunk_ = i - chunk_start; | |
2443 | + stsc->table_[stsc_entries].samples_ = samples; | |
2444 | + stsc->table_[stsc_entries].id_ = id; | |
2445 | + ++stsc_entries; | |
2446 | + } | |
2447 | + } | |
2448 | + } | |
2449 | + } | |
2450 | + chunk_end = i; | |
2451 | + stsc->entries_ = stsc_entries; | |
2452 | + | |
2453 | + { | |
2454 | + struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_; | |
2455 | + unsigned int entries = 0; | |
2456 | + for(i = chunk_start; i != chunk_end; ++i) | |
2457 | + { | |
2458 | + stco->chunk_offsets_[entries] = stco->chunk_offsets_[i]; | |
2459 | + ++entries; | |
2460 | + } | |
2461 | + stco->entries_ = entries; | |
2462 | + | |
2463 | + // patch first chunk with correct sample offset | |
2464 | + stco->chunk_offsets_[0] = (uint32_t)trak->samples_[start].pos_; | |
2465 | + } | |
2466 | + } | |
2467 | + } | |
2468 | + | |
2469 | + // process sync samples: | |
2470 | + if(trak->mdia_->minf_->stbl_->stss_) | |
2471 | + { | |
2472 | + struct stss_t* stss = trak->mdia_->minf_->stbl_->stss_; | |
2473 | + unsigned int entries = 0; | |
2474 | + unsigned int stss_start; | |
2475 | + unsigned int i; | |
2476 | + | |
2477 | + for(i = 0; i != stss->entries_; ++i) | |
2478 | + { | |
2479 | + if(stss->sample_numbers_[i] >= start + 1) | |
2480 | + break; | |
2481 | + } | |
2482 | + stss_start = i; | |
2483 | + for(; i != stss->entries_; ++i) | |
2484 | + { | |
2485 | + unsigned int sync_sample = stss->sample_numbers_[i]; | |
2486 | + if(sync_sample >= end + 1) | |
2487 | + break; | |
2488 | + stss->sample_numbers_[entries] = sync_sample - start; | |
2489 | + ++entries; | |
2490 | + } | |
2491 | + stss->entries_ = entries; | |
2492 | + } | |
2493 | + | |
2494 | + // process sample sizes | |
2495 | + { | |
2496 | + struct stsz_t* stsz = trak->mdia_->minf_->stbl_->stsz_; | |
2497 | + | |
2498 | + if(stsz->sample_size_ == 0) | |
2499 | + { | |
2500 | + unsigned int entries = 0; | |
2501 | + unsigned int i; | |
2502 | + for(i = start; i != end; ++i) | |
2503 | + { | |
2504 | + stsz->sample_sizes_[entries] = stsz->sample_sizes_[i]; | |
2505 | + ++entries; | |
2506 | + } | |
2507 | + stsz->entries_ = entries; | |
2508 | + } | |
2509 | + } | |
2510 | +} | |
2511 | + | |
2512 | +static struct trak_t* trak_init() | |
2513 | +{ | |
2514 | + struct trak_t* trak = (struct trak_t*)malloc(sizeof(struct trak_t)); | |
2515 | + trak->unknown_atoms_ = 0; | |
2516 | + trak->tkhd_ = 0; | |
2517 | + trak->mdia_ = 0; | |
2518 | + trak->chunks_size_ = 0; | |
2519 | + trak->chunks_ = 0; | |
2520 | + trak->samples_size_ = 0; | |
2521 | + trak->samples_ = 0; | |
2522 | + | |
2523 | + return trak; | |
2524 | +} | |
2525 | + | |
2526 | +static void trak_exit(struct trak_t* trak) | |
2527 | +{ | |
2528 | + if(trak->unknown_atoms_) | |
2529 | + { | |
2530 | + unknown_atom_exit(trak->unknown_atoms_); | |
2531 | + } | |
2532 | + if(trak->tkhd_) | |
2533 | + { | |
2534 | + tkhd_exit(trak->tkhd_); | |
2535 | + } | |
2536 | + if(trak->mdia_) | |
2537 | + { | |
2538 | + mdia_exit(trak->mdia_); | |
2539 | + } | |
2540 | + if(trak->chunks_) | |
2541 | + { | |
2542 | + free(trak->chunks_); | |
2543 | + } | |
2544 | + if(trak->samples_) | |
2545 | + { | |
2546 | + free(trak->samples_); | |
2547 | + } | |
2548 | + free(trak); | |
2549 | +} | |
2550 | + | |
2551 | +static int trak_add_tkhd(void* parent, void* tkhd) | |
2552 | +{ | |
2553 | + struct trak_t* trak = parent; | |
2554 | + trak->tkhd_ = tkhd; | |
2555 | + | |
2556 | + return 1; | |
2557 | +} | |
2558 | + | |
2559 | +static int trak_add_mdia(void* parent, void* mdia) | |
2560 | +{ | |
2561 | + struct trak_t* trak = parent; | |
2562 | + trak->mdia_ = mdia; | |
2563 | + | |
2564 | + return 1; | |
2565 | +} | |
2566 | + | |
2567 | +static void* trak_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
2568 | +{ | |
2569 | + struct trak_t* atom = trak_init(); | |
2570 | + | |
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 } | |
2574 | + }; | |
2575 | + | |
2576 | + int result = atom_reader(atom_read_list, | |
2577 | + sizeof(atom_read_list) / sizeof(atom_read_list[0]), | |
2578 | + atom, | |
2579 | + buffer, size); | |
2580 | + | |
2581 | + // check for mandatory atoms | |
2582 | + if(!atom->tkhd_) | |
2583 | + { | |
2584 | + printf("trak: missing tkhd\n"); | |
2585 | + result = 0; | |
2586 | + } | |
2587 | + | |
2588 | + if(!atom->mdia_) | |
2589 | + { | |
2590 | + printf("trak: missing mdia\n"); | |
2591 | + result = 0; | |
2592 | + } | |
2593 | + | |
2594 | + if(!result) | |
2595 | + { | |
2596 | + trak_exit(atom); | |
2597 | + return 0; | |
2598 | + } | |
2599 | + | |
2600 | + trak_build_index(atom); | |
2601 | + | |
2602 | + return atom; | |
2603 | +} | |
2604 | + | |
2605 | +static unsigned char* trak_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
2606 | +{ | |
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 } | |
2611 | + }; | |
2612 | + | |
2613 | + buffer = atom_writer(trak->unknown_atoms_, | |
2614 | + atom_write_list, | |
2615 | + sizeof(atom_write_list) / sizeof(atom_write_list[0]), | |
2616 | + buffer); | |
2617 | + | |
2618 | + return buffer; | |
2619 | +} | |
2620 | + | |
2621 | +void trak_shift_offsets(struct trak_t* trak, int64_t offset) | |
2622 | +{ | |
2623 | + struct stco_t* stco = trak->mdia_->minf_->stbl_->stco_; | |
2624 | + stco_shift_offsets(stco, (int32_t)offset); | |
2625 | +} | |
2626 | + | |
2627 | +void trak_shift_offsets_inplace(struct trak_t* trak, int64_t offset) | |
2628 | +{ | |
2629 | + void* stco = trak->mdia_->minf_->stbl_->stco_inplace_; | |
2630 | + stco_shift_offsets_inplace(stco, (int32_t)offset); | |
2631 | +} | |
2632 | + | |
2633 | +static struct mvhd_t* mvhd_init() | |
2634 | +{ | |
2635 | + struct mvhd_t* atom = (struct mvhd_t*)malloc(sizeof(struct mvhd_t)); | |
2636 | + | |
2637 | + return atom; | |
2638 | +} | |
2639 | + | |
2640 | +void mvhd_exit(struct mvhd_t* atom) | |
2641 | +{ | |
2642 | + free(atom); | |
2643 | +} | |
2644 | + | |
2645 | +static void* mvhd_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
2646 | +{ | |
2647 | + unsigned int i; | |
2648 | + | |
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) | |
2653 | + { | |
2654 | + if(size < 108-8) | |
2655 | + return 0; | |
2656 | + | |
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); | |
2661 | + buffer += 20; | |
2662 | + } | |
2663 | + else | |
2664 | + { | |
2665 | + if(size < 120-8) | |
2666 | + return 0; | |
2667 | + | |
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); | |
2672 | + buffer += 32; | |
2673 | + } | |
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); | |
2679 | + buffer += 16; | |
2680 | + | |
2681 | + for(i = 0; i != 9; ++i) | |
2682 | + { | |
2683 | + atom->matrix_[i] = read_32(buffer); | |
2684 | + buffer += 4; | |
2685 | + } | |
2686 | + | |
2687 | + for(i = 0; i != 6; ++i) | |
2688 | + { | |
2689 | + atom->predefined_[i] = read_32(buffer); | |
2690 | + buffer += 4; | |
2691 | + } | |
2692 | + | |
2693 | + atom->next_track_id_ = read_32(buffer + 0); | |
2694 | + | |
2695 | + return atom; | |
2696 | +} | |
2697 | + | |
2698 | +static unsigned char* mvhd_write(void* UNUSED(parent), void* atom, unsigned char* buffer) | |
2699 | +{ | |
2700 | + struct mvhd_t const* mvhd = atom; | |
2701 | + unsigned int i; | |
2702 | + | |
2703 | + buffer = write_8(buffer, mvhd->version_); | |
2704 | + buffer = write_24(buffer, mvhd->flags_); | |
2705 | + | |
2706 | + if(mvhd->version_ == 0) | |
2707 | + { | |
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_); | |
2712 | + } | |
2713 | + else | |
2714 | + { | |
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_); | |
2719 | + } | |
2720 | + | |
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]); | |
2726 | + | |
2727 | + for(i = 0; i != 9; ++i) | |
2728 | + { | |
2729 | + buffer = write_32(buffer, mvhd->matrix_[i]); | |
2730 | + } | |
2731 | + | |
2732 | + for(i = 0; i != 6; ++i) | |
2733 | + { | |
2734 | + buffer = write_32(buffer, mvhd->predefined_[i]); | |
2735 | + } | |
2736 | + | |
2737 | + buffer = write_32(buffer, mvhd->next_track_id_); | |
2738 | + | |
2739 | + return buffer; | |
2740 | +} | |
2741 | + | |
2742 | +static struct moov_t* moov_init() | |
2743 | +{ | |
2744 | + struct moov_t* moov = malloc(sizeof(struct moov_t)); | |
2745 | + moov->unknown_atoms_ = 0; | |
2746 | + moov->mvhd_ = 0; | |
2747 | + moov->tracks_ = 0; | |
2748 | + | |
2749 | + return moov; | |
2750 | +} | |
2751 | + | |
2752 | +static void moov_exit(struct moov_t* atom) | |
2753 | +{ | |
2754 | + unsigned int i; | |
2755 | + if(atom->unknown_atoms_) | |
2756 | + { | |
2757 | + unknown_atom_exit(atom->unknown_atoms_); | |
2758 | + } | |
2759 | + if(atom->mvhd_) | |
2760 | + { | |
2761 | + mvhd_exit(atom->mvhd_); | |
2762 | + } | |
2763 | + for(i = 0; i != atom->tracks_; ++i) | |
2764 | + { | |
2765 | + trak_exit(atom->traks_[i]); | |
2766 | + } | |
2767 | + free(atom); | |
2768 | +} | |
2769 | + | |
2770 | +static int moov_add_mvhd(void* parent, void* mvhd) | |
2771 | +{ | |
2772 | + struct moov_t* moov = parent; | |
2773 | + moov->mvhd_ = mvhd; | |
2774 | + | |
2775 | + return 1; | |
2776 | +} | |
2777 | + | |
2778 | +static int moov_add_trak(void* parent, void* child) | |
2779 | +{ | |
2780 | + struct moov_t* moov = parent; | |
2781 | + struct trak_t* trak = child; | |
2782 | + if(moov->tracks_ == MAX_TRACKS) | |
2783 | + { | |
2784 | + trak_exit(trak); | |
2785 | + return 0; | |
2786 | + } | |
2787 | + | |
2788 | + if(trak->mdia_->hdlr_->handler_type_ != FOURCC('v', 'i', 'd', 'e') && | |
2789 | + trak->mdia_->hdlr_->handler_type_ != FOURCC('s', 'o', 'u', 'n')) | |
2790 | + { | |
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_); | |
2797 | + trak_exit(trak); | |
2798 | + return 1; // continue | |
2799 | + } | |
2800 | + | |
2801 | + moov->traks_[moov->tracks_] = trak; | |
2802 | + ++moov->tracks_; | |
2803 | + | |
2804 | + return 1; | |
2805 | +} | |
2806 | + | |
2807 | +static void* moov_read(void* UNUSED(parent), unsigned char* buffer, uint64_t size) | |
2808 | +{ | |
2809 | + struct moov_t* atom = moov_init(); | |
2810 | + | |
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 } | |
2814 | + }; | |
2815 | + | |
2816 | + int result = atom_reader(atom_read_list, | |
2817 | + sizeof(atom_read_list) / sizeof(atom_read_list[0]), | |
2818 | + atom, | |
2819 | + buffer, size); | |
2820 | + | |
2821 | + // check for mandatory atoms | |
2822 | + if(!atom->mvhd_) | |
2823 | + { | |
2824 | + printf("moov: missing mvhd\n"); | |
2825 | + result = 0; | |
2826 | + } | |
2827 | + | |
2828 | + if(!atom->tracks_) | |
2829 | + { | |
2830 | + printf("moov: missing trak\n"); | |
2831 | + result = 0; | |
2832 | + } | |
2833 | + | |
2834 | + if(!result) | |
2835 | + { | |
2836 | + moov_exit(atom); | |
2837 | + return 0; | |
2838 | + } | |
2839 | + | |
2840 | + return atom; | |
2841 | +} | |
2842 | + | |
2843 | +static void moov_write(struct moov_t* atom, unsigned char* buffer) | |
2844 | +{ | |
2845 | + unsigned i; | |
2846 | + | |
2847 | + unsigned char* atom_start = buffer; | |
2848 | + | |
2849 | + struct atom_write_list_t atom_write_list[] = { | |
2850 | + { FOURCC('m', 'v', 'h', 'd'), atom, atom->mvhd_, &mvhd_write }, | |
2851 | + }; | |
2852 | + | |
2853 | + // atom size | |
2854 | + buffer += 4; | |
2855 | + | |
2856 | + // atom type | |
2857 | + buffer = write_32(buffer, FOURCC('m', 'o', 'o', 'v')); | |
2858 | + | |
2859 | + buffer = atom_writer(atom->unknown_atoms_, | |
2860 | + atom_write_list, | |
2861 | + sizeof(atom_write_list) / sizeof(atom_write_list[0]), | |
2862 | + buffer); | |
2863 | + | |
2864 | + for(i = 0; i != atom->tracks_; ++i) | |
2865 | + { | |
2866 | + struct atom_write_list_t trak_atom_write_list[] = { | |
2867 | + { FOURCC('t', 'r', 'a', 'k'), atom, atom->traks_[i], &trak_write }, | |
2868 | + }; | |
2869 | + buffer = atom_writer(0, | |
2870 | + trak_atom_write_list, | |
2871 | + sizeof(trak_atom_write_list) / sizeof(trak_atom_write_list[0]), | |
2872 | + buffer); | |
2873 | + } | |
2874 | + write_32(atom_start, buffer - atom_start); | |
2875 | +} | |
2876 | + | |
2877 | +void moov_shift_offsets(struct moov_t* moov, int64_t offset) | |
2878 | +{ | |
2879 | + unsigned int i; | |
2880 | + for(i = 0; i != moov->tracks_; ++i) | |
2881 | + { | |
2882 | + trak_shift_offsets(moov->traks_[i], offset); | |
2883 | + } | |
2884 | +} | |
2885 | + | |
2886 | +void moov_shift_offsets_inplace(struct moov_t* moov, int64_t offset) | |
2887 | +{ | |
2888 | + unsigned int i; | |
2889 | + for(i = 0; i != moov->tracks_; ++i) | |
2890 | + { | |
2891 | + trak_shift_offsets_inplace(moov->traks_[i], offset); | |
2892 | + } | |
2893 | +} | |
2894 | + | |
2895 | +unsigned int moov_seek(unsigned char* moov_data, | |
2896 | + uint64_t* moov_size, | |
2897 | + float start_time, | |
2898 | + float end_time, | |
2899 | + uint64_t* mdat_start, | |
2900 | + uint64_t* mdat_size, | |
2901 | + uint64_t offset, | |
2902 | + int client_is_flash) | |
2903 | +{ | |
2904 | + struct moov_t* moov = moov_read(NULL, moov_data + ATOM_PREAMBLE_SIZE, | |
2905 | + *moov_size - ATOM_PREAMBLE_SIZE); | |
2906 | + | |
2907 | + if(moov == 0 || moov->mvhd_ == 0) | |
2908 | + { | |
2909 | + printf("Error parsing moov header\n"); | |
2910 | + return 0; | |
2911 | + } | |
2912 | + | |
2913 | + { | |
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; | |
2919 | + unsigned int i; | |
2920 | + unsigned int pass; | |
2921 | + | |
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]; | |
2926 | + | |
2927 | + uint64_t moov_duration = 0; | |
2928 | + | |
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) | |
2933 | +// { | |
2934 | +// for(i = 2; i != moov->tracks_; ++i) | |
2935 | +// { | |
2936 | +// // patch 'trak' to 'free' | |
2937 | +// unsigned char* p = moov->traks_[i].start_ - 4; | |
2938 | +// p[0] = 'f'; | |
2939 | +// p[1] = 'r'; | |
2940 | +// p[2] = 'e'; | |
2941 | +// p[3] = 'e'; | |
2942 | +// } | |
2943 | +// moov->tracks_ = 2; | |
2944 | +// } | |
2945 | + | |
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. | |
2950 | + // | |
2951 | + // fixed: | |
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) | |
2955 | + { | |
2956 | + for(i = 0; i != moov->tracks_; ++i) | |
2957 | + { | |
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; | |
2963 | + | |
2964 | + // 1st pass: stss present, 2nd pass: no stss present | |
2965 | + if(pass == 0 && !stbl->stss_) | |
2966 | + continue; | |
2967 | + if(pass == 1 && stbl->stss_) | |
2968 | + continue; | |
2969 | + | |
2970 | + // ignore empty track | |
2971 | + if(trak->mdia_->mdhd_->duration_ == 0) | |
2972 | + continue; | |
2973 | + | |
2974 | + // get start | |
2975 | + if(start == 0) | |
2976 | + { | |
2977 | + trak_sample_start[i] = start; | |
2978 | + } | |
2979 | + else | |
2980 | + { | |
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); | |
2989 | + } | |
2990 | + | |
2991 | + // get end | |
2992 | + if(end == 0) | |
2993 | + { | |
2994 | + trak_sample_end[i] = trak->samples_size_; | |
2995 | + } | |
2996 | + else | |
2997 | + { | |
2998 | + end = stts_get_sample(stbl->stts_, (uint64_t)(end * moov_to_trak_time)); | |
2999 | + if(end >= trak->samples_size_) | |
3000 | + { | |
3001 | + end = trak->samples_size_; | |
3002 | + } | |
3003 | + else | |
3004 | + { | |
3005 | + end = stbl_get_nearest_keyframe(stbl, end + 1) - 1; | |
3006 | + } | |
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); | |
3010 | + } | |
3011 | + } | |
3012 | + } | |
3013 | + | |
3014 | + printf("start=%u\n", start); | |
3015 | + printf("end=%u\n", end); | |
3016 | + | |
3017 | + if(end && start >= end) | |
3018 | + { | |
3019 | + moov_exit(moov); | |
3020 | + return 0; | |
3021 | + } | |
3022 | + | |
3023 | + for(i = 0; i != moov->tracks_; ++i) | |
3024 | + { | |
3025 | + struct trak_t* trak = moov->traks_[i]; | |
3026 | + struct stbl_t* stbl = trak->mdia_->minf_->stbl_; | |
3027 | + | |
3028 | + unsigned int start_sample = trak_sample_start[i]; | |
3029 | + unsigned int end_sample = trak_sample_end[i]; | |
3030 | + | |
3031 | + // ignore empty track | |
3032 | + if(trak->mdia_->mdhd_->duration_ == 0) | |
3033 | + continue; | |
3034 | + | |
3035 | + trak_update_index(trak, start_sample, end_sample); | |
3036 | + | |
3037 | + { | |
3038 | + uint64_t skip = | |
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); | |
3043 | + | |
3044 | + if(end_sample != trak->samples_size_) | |
3045 | + { | |
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); | |
3052 | + } | |
3053 | + } | |
3054 | + | |
3055 | + { | |
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; | |
3060 | + { | |
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); | |
3065 | + | |
3066 | + if(duration > moov_duration) | |
3067 | + moov_duration = duration; | |
3068 | + } | |
3069 | + } | |
3070 | + | |
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_)); | |
3075 | + } | |
3076 | + moov->mvhd_->duration_ = moov_duration; | |
3077 | + | |
3078 | + // subtract bytes we skip at the front of the mdat atom | |
3079 | + offset -= skip_from_start; | |
3080 | + | |
3081 | + // subtract old moov size | |
3082 | + offset -= *moov_size; | |
3083 | + | |
3084 | + printf("moov: writing header\n"); | |
3085 | + | |
3086 | + moov_write(moov, moov_data); | |
3087 | + *moov_size = read_32(moov_data); | |
3088 | + | |
3089 | + // add new moov size | |
3090 | + offset += *moov_size; | |
3091 | + | |
3092 | + printf("shifting offsets by %lld\n", offset); | |
3093 | + moov_shift_offsets_inplace(moov, offset); | |
3094 | + | |
3095 | +// moov_write(moov, moov_data); | |
3096 | + | |
3097 | +#ifdef COMPRESS_MOOV_ATOM | |
3098 | + if(!client_is_flash) | |
3099 | + { | |
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) | |
3105 | + { | |
3106 | + printf("cmov size = %lu (%ld%%)\n", destLen, 100 * destLen / sourceLen); | |
3107 | + } | |
3108 | + | |
3109 | + { | |
3110 | + const int extra_space = 4096; | |
3111 | + if(destLen + extra_space < sourceLen) | |
3112 | + { | |
3113 | + const int bytes_saved = sourceLen - destLen; | |
3114 | + uLong destLen2; | |
3115 | + int extra = 0; | |
3116 | + printf("shifting offsets by %d\n", -bytes_saved); | |
3117 | + moov_shift_offsets_inplace(moov, -bytes_saved); | |
3118 | + | |
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 | |
3123 | + | |
3124 | + printf("shifting offsets by %d\n", extra); | |
3125 | + moov_shift_offsets_inplace(moov, extra); | |
3126 | + | |
3127 | + // recompress | |
3128 | + destLen2 = compressBound(sourceLen); | |
3129 | + zstatus = compress(cmov, &destLen2, moov_data, sourceLen); | |
3130 | + if(zstatus == Z_OK) | |
3131 | + { | |
3132 | + printf("cmov size = %lu (%ld%%)\n", destLen2, 100 * destLen2 / sourceLen); | |
3133 | + | |
3134 | + if(destLen2 < destLen + extra_space) | |
3135 | + { | |
3136 | + // copy compressed movie atom | |
3137 | + unsigned char* outbuffer = moov_data; | |
3138 | + | |
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; | |
3144 | + | |
3145 | + outbuffer = write_32(outbuffer, (uint32_t)*moov_size); | |
3146 | + | |
3147 | + // skip 'moov' | |
3148 | + outbuffer += 4; | |
3149 | + | |
3150 | + outbuffer = write_32(outbuffer, cmov_size); | |
3151 | + { | |
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')); | |
3156 | + | |
3157 | + outbuffer = write_32(outbuffer, cmvd_size); | |
3158 | + { | |
3159 | + outbuffer = write_32(outbuffer, FOURCC('c', 'm', 'v', 'd')); | |
3160 | + outbuffer = write_32(outbuffer, sourceLen); | |
3161 | + memcpy(outbuffer, cmov, destLen2); | |
3162 | + outbuffer += destLen2; | |
3163 | + } | |
3164 | + } | |
3165 | + | |
3166 | + // add final padding | |
3167 | + outbuffer = write_32(outbuffer, free_size); | |
3168 | + outbuffer = write_32(outbuffer, FOURCC('f', 'r', 'e', 'e')); | |
3169 | + { | |
3170 | + const char free_bytes[8] = | |
3171 | + { | |
3172 | + 'C', 'o', 'd', 'e','S','h', 'o', 'p' | |
3173 | + }; | |
3174 | + uint32_t padding_index; | |
3175 | + for(padding_index = ATOM_PREAMBLE_SIZE; padding_index != free_size; ++padding_index) | |
3176 | + { | |
3177 | + outbuffer[padding_index] = free_bytes[padding_index % 8]; | |
3178 | + } | |
3179 | + } | |
3180 | + } | |
3181 | + else | |
3182 | + { | |
3183 | + printf("2nd pass compress overflow\n"); | |
3184 | + } | |
3185 | + } | |
3186 | + } | |
3187 | + } | |
3188 | + free(cmov); | |
3189 | + } | |
3190 | +#endif | |
3191 | + | |
3192 | + *mdat_start += skip_from_start; | |
3193 | + if(end_offset != 0) | |
3194 | + { | |
3195 | + *mdat_size = end_offset; | |
3196 | + } | |
3197 | + *mdat_size -= skip_from_start; | |
3198 | + } | |
3199 | + | |
3200 | + moov_exit(moov); | |
3201 | + | |
3202 | + return 1; | |
3203 | +} | |
3204 | + | |
3205 | +//////////////////////////////////////////////////////////////////////////////// | |
3206 | + | |
3207 | +struct mp4_atom_t | |
3208 | +{ | |
3209 | + uint32_t type_; | |
3210 | + uint32_t short_size_; | |
3211 | + uint64_t size_; | |
3212 | + uint64_t start_; | |
3213 | + uint64_t end_; | |
3214 | +}; | |
3215 | + | |
3216 | +static int mp4_atom_read_header(FILE* infile, struct mp4_atom_t* atom) | |
3217 | +{ | |
3218 | + unsigned char atom_header[8]; | |
3219 | + | |
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]); | |
3224 | + | |
3225 | + if(atom->short_size_ == 1) | |
3226 | + { | |
3227 | + fread(atom_header, 8, 1, infile); | |
3228 | + atom->size_ = read_64(&atom_header[0]); | |
3229 | + } | |
3230 | + else | |
3231 | + { | |
3232 | + atom->size_ = atom->short_size_; | |
3233 | + } | |
3234 | + | |
3235 | + atom->end_ = atom->start_ + atom->size_; | |
3236 | + | |
3237 | + return 1; | |
3238 | +} | |
3239 | + | |
3240 | +static int mp4_atom_write_header(unsigned char* outbuffer, struct mp4_atom_t* atom) | |
3241 | +{ | |
3242 | + int write_box64 = atom->short_size_ == 1 ? 1 : 0; | |
3243 | + | |
3244 | + if(write_box64) | |
3245 | + write_32(outbuffer, 1); | |
3246 | + else | |
3247 | + write_32(outbuffer, (uint32_t)atom->size_); | |
3248 | + | |
3249 | + write_32(outbuffer + 4, atom->type_); | |
3250 | + | |
3251 | + if(write_box64) | |
3252 | + { | |
3253 | + write_64(outbuffer + 8, atom->size_); | |
3254 | + return 16; | |
3255 | + } | |
3256 | + else | |
3257 | + { | |
3258 | + return 8; | |
3259 | + } | |
3260 | +} | |
3261 | + | |
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) | |
3267 | +{ | |
3268 | + FILE* infile; | |
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; | |
3275 | + | |
3276 | + *mp4_header = 0; | |
3277 | + memset(&ftyp_atom, 0, sizeof(ftyp_atom)); | |
3278 | + memset(&moov_atom, 0, sizeof(moov_atom)); | |
3279 | + memset(&mdat_atom, 0, sizeof(mdat_atom)); | |
3280 | + | |
3281 | + infile = fopen(filename, "rb"); | |
3282 | + if(infile == NULL) | |
3283 | + { | |
3284 | + return 0; | |
3285 | + } | |
3286 | + | |
3287 | + while(ftello(infile) < filesize) | |
3288 | + { | |
3289 | + struct mp4_atom_t leaf_atom; | |
3290 | + | |
3291 | + if(!mp4_atom_read_header(infile, &leaf_atom)) | |
3292 | + break; | |
3293 | + | |
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_, | |
3297 | + leaf_atom.size_); | |
3298 | + | |
3299 | + switch(leaf_atom.type_) | |
3300 | + { | |
3301 | + case FOURCC('f', 't', 'y', 'p'): | |
3302 | + ftyp_atom = leaf_atom; | |
3303 | + break; | |
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); | |
3309 | + break; | |
3310 | + case FOURCC('m', 'd', 'a', 't'): | |
3311 | + mdat_atom = leaf_atom; | |
3312 | + break; | |
3313 | + } | |
3314 | + fseeko(infile, leaf_atom.end_, SEEK_SET); | |
3315 | + } | |
3316 | + | |
3317 | + if(moov_atom.size_ == 0) | |
3318 | + { | |
3319 | + printf("Error: moov atom not found\n"); | |
3320 | + fclose(infile); | |
3321 | + return 0; | |
3322 | + } | |
3323 | + | |
3324 | + if(mdat_atom.size_ == 0) | |
3325 | + { | |
3326 | + printf("Error: mdat atom not found\n"); | |
3327 | + fclose(infile); | |
3328 | + return 0; | |
3329 | + } | |
3330 | + | |
3331 | + buffer = (unsigned char*)malloc((uint32_t)moov_atom.size_ + 4 * 1024); | |
3332 | + *mp4_header = buffer; | |
3333 | + | |
3334 | + if(ftyp_atom.size_) | |
3335 | + { | |
3336 | + fseeko(infile, ftyp_atom.start_, SEEK_SET); | |
3337 | + fread(buffer, (off_t)ftyp_atom.size_, 1, infile); | |
3338 | + buffer += ftyp_atom.size_; | |
3339 | + } | |
3340 | + | |
3341 | + { | |
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', | |
3348 | + 'n', 'g' | |
3349 | + }; | |
3350 | + memcpy(buffer, free_data, sizeof(free_data)); | |
3351 | + buffer += sizeof(free_data); | |
3352 | + } | |
3353 | + | |
3354 | + new_mdat_start = buffer - (unsigned char*)(*mp4_header) + moov_atom.size_; | |
3355 | + if(!moov_seek(moov_data, | |
3356 | + &moov_atom.size_, | |
3357 | + start_time, | |
3358 | + end_time, | |
3359 | + &mdat_atom.start_, | |
3360 | + &mdat_atom.size_, | |
3361 | + new_mdat_start - mdat_atom.start_, | |
3362 | + client_is_flash)) | |
3363 | + { | |
3364 | + free(moov_data); | |
3365 | + fclose(infile); | |
3366 | + return 0; | |
3367 | + } | |
3368 | + | |
3369 | + memcpy(buffer, moov_data, (uint32_t)moov_atom.size_); | |
3370 | + buffer += moov_atom.size_; | |
3371 | + free(moov_data); | |
3372 | + | |
3373 | + { | |
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; | |
3378 | + } | |
3379 | + | |
3380 | + *mp4_header_size = (uint32_t)(buffer - (unsigned char*)(*mp4_header)); | |
3381 | + | |
3382 | + fclose(infile); | |
3383 | + | |
3384 | + return 1; | |
3385 | +} | |
3386 | + | |
3387 | +// End Of File | |
3388 | + | |
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 | |
3391 | @@ -0,0 +1,49 @@ | |
3392 | +/******************************************************************************* | |
3393 | + moov.h (version 2) | |
3394 | + | |
3395 | + moov - A library for splitting Quicktime/MPEG4 files. | |
3396 | + http://h264.code-shop.com | |
3397 | + | |
3398 | + Copyright (C) 2007-2009 CodeShop B.V. | |
3399 | + | |
3400 | + Licensing | |
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. | |
3404 | + | |
3405 | + Creative Commons License: | |
3406 | + http://creativecommons.org/licenses/by-nc-sa/3.0/ | |
3407 | + | |
3408 | + Commercial License: | |
3409 | + http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-License-Version2 | |
3410 | +******************************************************************************/ | |
3411 | + | |
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 | |
3415 | + | |
3416 | +#ifndef _MSC_VER | |
3417 | +#include <inttypes.h> | |
3418 | +#else | |
3419 | +#include "inttypes.h" | |
3420 | +#endif | |
3421 | + | |
3422 | +#ifdef __cplusplus | |
3423 | +extern "C" { | |
3424 | +#endif | |
3425 | + | |
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); | |
3431 | + | |
3432 | +/* Returns true when the test string is a prefix of the input */ | |
3433 | +extern int starts_with(const char* input, const char* test); | |
3434 | + | |
3435 | +#ifdef __cplusplus | |
3436 | +} /* extern C definitions */ | |
3437 | +#endif | |
3438 | + | |
3439 | +// End Of File | |
3440 | + |