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