Mercurial > hg > nginx-quic
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 |