comparison src/http/modules/ngx_http_mp4_module.c @ 4098:a3e07fab98a3

Fix of case when start sample does not reside on chunk boundary.
author Igor Sysoev <igor@sysoev.ru>
date Wed, 14 Sep 2011 05:16:20 +0000
parents f9a1646b9611
children 9ee6944590c0
comparison
equal deleted inserted replaced
4097:a3870ea96ccd 4098:a3e07fab98a3
6 #include <ngx_config.h> 6 #include <ngx_config.h>
7 #include <ngx_core.h> 7 #include <ngx_core.h>
8 #include <ngx_http.h> 8 #include <ngx_http.h>
9 9
10 10
11 #define NGX_HTTP_MP4_TRAK_ATOM 0 11 #define NGX_HTTP_MP4_TRAK_ATOM 0
12 #define NGX_HTTP_MP4_TKHD_ATOM 1 12 #define NGX_HTTP_MP4_TKHD_ATOM 1
13 #define NGX_HTTP_MP4_MDIA_ATOM 2 13 #define NGX_HTTP_MP4_MDIA_ATOM 2
14 #define NGX_HTTP_MP4_MDHD_ATOM 3 14 #define NGX_HTTP_MP4_MDHD_ATOM 3
15 #define NGX_HTTP_MP4_HDLR_ATOM 4 15 #define NGX_HTTP_MP4_HDLR_ATOM 4
16 #define NGX_HTTP_MP4_MINF_ATOM 5 16 #define NGX_HTTP_MP4_MINF_ATOM 5
17 #define NGX_HTTP_MP4_VMHD_ATOM 6 17 #define NGX_HTTP_MP4_VMHD_ATOM 6
18 #define NGX_HTTP_MP4_SMHD_ATOM 7 18 #define NGX_HTTP_MP4_SMHD_ATOM 7
19 #define NGX_HTTP_MP4_DINF_ATOM 8 19 #define NGX_HTTP_MP4_DINF_ATOM 8
20 #define NGX_HTTP_MP4_STBL_ATOM 9 20 #define NGX_HTTP_MP4_STBL_ATOM 9
21 #define NGX_HTTP_MP4_STSD_ATOM 10 21 #define NGX_HTTP_MP4_STSD_ATOM 10
22 #define NGX_HTTP_MP4_STTS_ATOM 11 22 #define NGX_HTTP_MP4_STTS_ATOM 11
23 #define NGX_HTTP_MP4_STTS_DATA 12 23 #define NGX_HTTP_MP4_STTS_DATA 12
24 #define NGX_HTTP_MP4_STSS_ATOM 13 24 #define NGX_HTTP_MP4_STSS_ATOM 13
25 #define NGX_HTTP_MP4_STSS_DATA 14 25 #define NGX_HTTP_MP4_STSS_DATA 14
26 #define NGX_HTTP_MP4_CTTS_ATOM 15 26 #define NGX_HTTP_MP4_CTTS_ATOM 15
27 #define NGX_HTTP_MP4_CTTS_DATA 16 27 #define NGX_HTTP_MP4_CTTS_DATA 16
28 #define NGX_HTTP_MP4_STSC_ATOM 17 28 #define NGX_HTTP_MP4_STSC_ATOM 17
29 #define NGX_HTTP_MP4_STSC_DATA 18 29 #define NGX_HTTP_MP4_STSC_CHUNK 18
30 #define NGX_HTTP_MP4_STSZ_ATOM 19 30 #define NGX_HTTP_MP4_STSC_DATA 19
31 #define NGX_HTTP_MP4_STSZ_DATA 20 31 #define NGX_HTTP_MP4_STSZ_ATOM 20
32 #define NGX_HTTP_MP4_STCO_ATOM 21 32 #define NGX_HTTP_MP4_STSZ_DATA 21
33 #define NGX_HTTP_MP4_STCO_DATA 22 33 #define NGX_HTTP_MP4_STCO_ATOM 22
34 34 #define NGX_HTTP_MP4_STCO_DATA 23
35 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_STCO_DATA 35
36 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_STCO_DATA
36 37
37 38
38 typedef struct { 39 typedef struct {
39 size_t buffer_size; 40 size_t buffer_size;
40 size_t max_buffer_size; 41 size_t max_buffer_size;
41 } ngx_http_mp4_conf_t; 42 } ngx_http_mp4_conf_t;
43
44
45 typedef struct {
46 u_char chunk[4];
47 u_char samples[4];
48 u_char id[4];
49 } ngx_mp4_stsc_entry_t;
42 50
43 51
44 typedef struct { 52 typedef struct {
45 uint32_t timescale; 53 uint32_t timescale;
46 uint32_t time_to_sample_entries; 54 uint32_t time_to_sample_entries;
50 uint32_t sample_sizes_entries; 58 uint32_t sample_sizes_entries;
51 uint32_t chunks; 59 uint32_t chunks;
52 60
53 ngx_uint_t start_sample; 61 ngx_uint_t start_sample;
54 ngx_uint_t start_chunk; 62 ngx_uint_t start_chunk;
63 ngx_uint_t chunk_samples;
64 ngx_uint_t chunk_samples_size;
55 off_t start_offset; 65 off_t start_offset;
56 66
57 size_t tkhd_size; 67 size_t tkhd_size;
58 size_t mdhd_size; 68 size_t mdhd_size;
59 size_t hdlr_size; 69 size_t hdlr_size;
80 ngx_buf_t stss_atom_buf; 90 ngx_buf_t stss_atom_buf;
81 ngx_buf_t stss_data_buf; 91 ngx_buf_t stss_data_buf;
82 ngx_buf_t ctts_atom_buf; 92 ngx_buf_t ctts_atom_buf;
83 ngx_buf_t ctts_data_buf; 93 ngx_buf_t ctts_data_buf;
84 ngx_buf_t stsc_atom_buf; 94 ngx_buf_t stsc_atom_buf;
95 ngx_buf_t stsc_chunk_buf;
85 ngx_buf_t stsc_data_buf; 96 ngx_buf_t stsc_data_buf;
86 ngx_buf_t stsz_atom_buf; 97 ngx_buf_t stsz_atom_buf;
87 ngx_buf_t stsz_data_buf; 98 ngx_buf_t stsz_data_buf;
88 ngx_buf_t tsco_atom_buf; 99 ngx_buf_t tsco_atom_buf;
89 ngx_buf_t tsco_data_buf; 100 ngx_buf_t tsco_data_buf;
101
102 ngx_mp4_stsc_entry_t stsc_chunk_entry;
90 } ngx_http_mp4_trak_t; 103 } ngx_http_mp4_trak_t;
91 104
92 105
93 typedef struct { 106 typedef struct {
94 ngx_file_t file; 107 ngx_file_t file;
2153 u_char version[1]; 2166 u_char version[1];
2154 u_char flags[3]; 2167 u_char flags[3];
2155 u_char entries[4]; 2168 u_char entries[4];
2156 } ngx_mp4_stsc_atom_t; 2169 } ngx_mp4_stsc_atom_t;
2157 2170
2158 typedef struct {
2159 u_char chunk[4];
2160 u_char samples[4];
2161 u_char id[4];
2162 } ngx_mp4_stsc_entry_t;
2163
2164 2171
2165 static ngx_int_t 2172 static ngx_int_t
2166 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) 2173 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2167 { 2174 {
2168 u_char *atom_header, *atom_table, *atom_end; 2175 u_char *atom_header, *atom_table, *atom_end;
2219 static ngx_int_t 2226 static ngx_int_t
2220 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, 2227 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2221 ngx_http_mp4_trak_t *trak) 2228 ngx_http_mp4_trak_t *trak)
2222 { 2229 {
2223 size_t atom_size; 2230 size_t atom_size;
2224 uint32_t entries, chunk, samples, prev_chunk, prev_samples; 2231 uint32_t start_sample, entries, chunk, samples, id,
2225 ngx_buf_t *atom, *data; 2232 next_chunk, n;
2233 ngx_buf_t *atom, *data, *buf;
2226 ngx_mp4_stsc_atom_t *stsc_atom; 2234 ngx_mp4_stsc_atom_t *stsc_atom;
2227 ngx_mp4_stsc_entry_t *entry, *end; 2235 ngx_mp4_stsc_entry_t *entry, *first, *end;
2228 2236
2229 /* 2237 /*
2230 * mdia.minf.stbl.stsc updating requires trak->start_sample 2238 * mdia.minf.stbl.stsc updating requires trak->start_sample
2231 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd 2239 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2232 * atom which may reside after mdia.minf 2240 * atom which may reside after mdia.minf
2242 "no mp4 stsc atoms were found in \"%s\"", 2250 "no mp4 stsc atoms were found in \"%s\"",
2243 mp4->file.name.data); 2251 mp4->file.name.data);
2244 return NGX_ERROR; 2252 return NGX_ERROR;
2245 } 2253 }
2246 2254
2247 chunk = 0; 2255 start_sample = (uint32_t) trak->start_sample;
2248 samples = 1; 2256 entries = trak->sample_to_chunk_entries - 1;
2249 prev_chunk = 0; 2257
2250 prev_samples = 0;
2251
2252 entries = trak->sample_to_chunk_entries;
2253 entry = (ngx_mp4_stsc_entry_t *) data->pos; 2258 entry = (ngx_mp4_stsc_entry_t *) data->pos;
2254 end = (ngx_mp4_stsc_entry_t *) data->last; 2259 end = (ngx_mp4_stsc_entry_t *) data->last;
2255 2260
2261 chunk = ngx_mp4_get_32value(entry->chunk);
2262 samples = ngx_mp4_get_32value(entry->samples);
2263 id = ngx_mp4_get_32value(entry->id);
2264 entry++;
2265
2256 while (entry < end) { 2266 while (entry < end) {
2257 2267
2258 chunk = ngx_mp4_get_32value(entry->chunk); 2268 next_chunk = ngx_mp4_get_32value(entry->chunk);
2259 samples = ngx_mp4_get_32value(entry->samples); 2269
2260 2270 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2261 trak->start_sample -= (chunk - prev_chunk) * prev_samples; 2271 "start_sample:%uD, chunk:%uD, chunks:%uD, "
2262 2272 "samples:%uD, id:%uD",
2263 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2273 start_sample, chunk, next_chunk - chunk, samples, id);
2264 "chunk:%ui, samples:%ui, id:%ui", 2274
2265 chunk, samples, ngx_mp4_get_32value(entry->id)); 2275 n = (next_chunk - chunk) * samples;
2266 2276
2267 if (trak->start_sample < samples) { 2277 if (start_sample <= n) {
2268 goto found; 2278 goto found;
2269 } 2279 }
2270 2280
2271 prev_chunk = chunk; 2281 start_sample -= n;
2272 prev_samples = samples; 2282
2283 chunk = next_chunk;
2284 samples = ngx_mp4_get_32value(entry->samples);
2285 id = ngx_mp4_get_32value(entry->id);
2273 entries--; 2286 entries--;
2274 entry++; 2287 entry++;
2275 } 2288 }
2276 2289
2290 next_chunk = trak->chunks;
2291
2292 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2293 "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
2294 start_sample, chunk, next_chunk - chunk, samples);
2295
2296 n = (next_chunk - chunk) * samples;
2297
2298 if (start_sample > n) {
2299 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2300 "start time is out mp4 stsc chunks in \"%s\"",
2301 mp4->file.name.data);
2302 return NGX_ERROR;
2303 }
2304
2305 found:
2306
2277 entries++; 2307 entries++;
2278 entry--; 2308 entry--;
2279 2309
2280 found: 2310 if (samples == 0) {
2311 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2312 "zero number of samples in \"%s\"",
2313 mp4->file.name.data);
2314 return NGX_ERROR;
2315 }
2316
2317 trak->start_chunk = chunk - 1;
2318
2319 trak->start_chunk += start_sample / samples;
2320 trak->chunk_samples = start_sample % samples;
2321
2322 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2323 "start chunk:%ui, samples:%uD",
2324 trak->start_chunk, trak->chunk_samples);
2281 2325
2282 data->pos = (u_char *) entry; 2326 data->pos = (u_char *) entry;
2283
2284 chunk = trak->start_sample / samples;
2285 trak->start_chunk = chunk;
2286 atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos); 2327 atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos);
2328
2329 if (trak->chunk_samples) {
2330
2331 first = &trak->stsc_chunk_entry;
2332 ngx_mp4_set_32value(first->chunk, 1);
2333 ngx_mp4_set_32value(first->samples, samples - trak->chunk_samples);
2334 ngx_mp4_set_32value(first->id, id);
2335
2336 buf = &trak->stsc_chunk_buf;
2337 buf->temporary = 1;
2338 buf->pos = (u_char *) first;
2339 buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2340
2341 trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf;
2342
2343 ngx_mp4_set_32value(entry->chunk, 2);
2344
2345 atom_size += sizeof(ngx_mp4_stsc_entry_t);
2346 }
2347
2348 while (++entry < end) {
2349 chunk = ngx_mp4_get_32value(entry->chunk);
2350 chunk -= trak->start_chunk;
2351 ngx_mp4_set_32value(entry->chunk, chunk);
2352 }
2353
2287 trak->size += atom_size; 2354 trak->size += atom_size;
2288
2289 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2290 "start chunk:%ui", chunk);
2291 2355
2292 atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf; 2356 atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
2293 stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos; 2357 stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
2294 2358
2295 ngx_mp4_set_32value(stsc_atom->size, atom_size); 2359 ngx_mp4_set_32value(stsc_atom->size, atom_size);
2362 2426
2363 trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data; 2427 trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
2364 2428
2365 } else { 2429 } else {
2366 /* if size != 0 then all samples are the same size */ 2430 /* if size != 0 then all samples are the same size */
2431 /* TODO : chunk samples */
2367 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; 2432 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
2368 ngx_mp4_set_32value(atom_header, atom_size); 2433 ngx_mp4_set_32value(atom_header, atom_size);
2369 trak->size += atom_size; 2434 trak->size += atom_size;
2370 } 2435 }
2371 2436
2378 static void 2443 static void
2379 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, 2444 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2380 ngx_http_mp4_trak_t *trak) 2445 ngx_http_mp4_trak_t *trak)
2381 { 2446 {
2382 size_t atom_size; 2447 size_t atom_size;
2448 uint32_t *pos, *end;
2383 ngx_buf_t *atom, *data; 2449 ngx_buf_t *atom, *data;
2384 ngx_mp4_stsz_atom_t *stsz_atom; 2450 ngx_mp4_stsz_atom_t *stsz_atom;
2385 2451
2386 /* 2452 /*
2387 * mdia.minf.stbl.stsz updating requires trak->start_sample 2453 * mdia.minf.stbl.stsz updating requires trak->start_sample
2394 2460
2395 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf; 2461 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
2396 2462
2397 if (data) { 2463 if (data) {
2398 data->pos += trak->start_sample * sizeof(uint32_t); 2464 data->pos += trak->start_sample * sizeof(uint32_t);
2465 end = (uint32_t *) data->pos;
2466
2467 for (pos = end - trak->chunk_samples; pos < end; pos++) {
2468 trak->chunk_samples_size += ngx_mp4_get_32value(pos);
2469 }
2470
2471 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2472 "chunk samples sizes:%uD", trak->chunk_samples_size);
2473
2399 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); 2474 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
2400 trak->size += atom_size; 2475 trak->size += atom_size;
2401 2476
2402 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf; 2477 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
2403 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos; 2478 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
2473 static ngx_int_t 2548 static ngx_int_t
2474 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, 2549 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
2475 ngx_http_mp4_trak_t *trak) 2550 ngx_http_mp4_trak_t *trak)
2476 { 2551 {
2477 size_t atom_size; 2552 size_t atom_size;
2478 uint32_t start_chunk;
2479 ngx_buf_t *atom, *data; 2553 ngx_buf_t *atom, *data;
2480 ngx_mp4_stco_atom_t *stco_atom; 2554 ngx_mp4_stco_atom_t *stco_atom;
2481 2555
2482 /* 2556 /*
2483 * mdia.minf.stbl.stco updating requires trak->start_chunk 2557 * mdia.minf.stbl.stco updating requires trak->start_chunk
2486 */ 2560 */
2487 2561
2488 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2562 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2489 "mp4 stco atom update"); 2563 "mp4 stco atom update");
2490 2564
2491 if (trak->start_chunk > trak->chunks) {
2492 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2493 "start time is out mp4 stco chunks in \"%s\"",
2494 mp4->file.name.data);
2495 return NGX_ERROR;
2496 }
2497
2498 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf; 2565 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
2499 2566
2500 if (data == NULL) { 2567 if (data == NULL) {
2501 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, 2568 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2502 "no mp4 stco atoms were found in \"%s\"", 2569 "no mp4 stco atoms were found in \"%s\"",
2503 mp4->file.name.data); 2570 mp4->file.name.data);
2504 return NGX_ERROR; 2571 return NGX_ERROR;
2505 } 2572 }
2506 2573
2507 start_chunk = trak->start_chunk; 2574 data->pos += trak->start_chunk * sizeof(uint32_t);
2508
2509 data->pos += start_chunk * sizeof(uint32_t);
2510 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); 2575 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
2511 trak->size += atom_size; 2576 trak->size += atom_size;
2512 2577
2513 trak->start_offset = ngx_mp4_get_32value(data->pos); 2578 trak->start_offset = ngx_mp4_get_32value(data->pos);
2579 trak->start_offset += trak->chunk_samples_size;
2580 ngx_mp4_set_32value(data->pos, trak->start_offset);
2514 2581
2515 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2582 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2516 "start chunk offset:%uD", trak->start_offset); 2583 "start chunk offset:%uD", trak->start_offset);
2517 2584
2518 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf; 2585 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
2519 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos; 2586 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
2520 2587
2521 ngx_mp4_set_32value(stco_atom->size, atom_size); 2588 ngx_mp4_set_32value(stco_atom->size, atom_size);
2522 ngx_mp4_set_32value(stco_atom->entries, trak->chunks - start_chunk); 2589 ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk);
2523 2590
2524 return NGX_OK; 2591 return NGX_OK;
2525 } 2592 }
2526 2593
2527 2594