3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
7 #include <ngx_config.h>
12 #define NGX_HTTP_MP4_TRAK_ATOM 0
13 #define NGX_HTTP_MP4_TKHD_ATOM 1
14 #define NGX_HTTP_MP4_MDIA_ATOM 2
15 #define NGX_HTTP_MP4_MDHD_ATOM 3
16 #define NGX_HTTP_MP4_HDLR_ATOM 4
17 #define NGX_HTTP_MP4_MINF_ATOM 5
18 #define NGX_HTTP_MP4_VMHD_ATOM 6
19 #define NGX_HTTP_MP4_SMHD_ATOM 7
20 #define NGX_HTTP_MP4_DINF_ATOM 8
21 #define NGX_HTTP_MP4_STBL_ATOM 9
22 #define NGX_HTTP_MP4_STSD_ATOM 10
23 #define NGX_HTTP_MP4_STTS_ATOM 11
24 #define NGX_HTTP_MP4_STTS_DATA 12
25 #define NGX_HTTP_MP4_STSS_ATOM 13
26 #define NGX_HTTP_MP4_STSS_DATA 14
27 #define NGX_HTTP_MP4_CTTS_ATOM 15
28 #define NGX_HTTP_MP4_CTTS_DATA 16
29 #define NGX_HTTP_MP4_STSC_ATOM 17
30 #define NGX_HTTP_MP4_STSC_CHUNK 18
31 #define NGX_HTTP_MP4_STSC_DATA 19
32 #define NGX_HTTP_MP4_STSZ_ATOM 20
33 #define NGX_HTTP_MP4_STSZ_DATA 21
34 #define NGX_HTTP_MP4_STCO_ATOM 22
35 #define NGX_HTTP_MP4_STCO_DATA 23
36 #define NGX_HTTP_MP4_CO64_ATOM 24
37 #define NGX_HTTP_MP4_CO64_DATA 25
39 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
44 size_t max_buffer_size
;
45 } ngx_http_mp4_conf_t
;
52 } ngx_mp4_stsc_entry_t
;
57 uint32_t time_to_sample_entries
;
58 uint32_t sample_to_chunk_entries
;
59 uint32_t sync_samples_entries
;
60 uint32_t composition_offset_entries
;
61 uint32_t sample_sizes_entries
;
64 ngx_uint_t start_sample
;
65 ngx_uint_t start_chunk
;
66 ngx_uint_t chunk_samples
;
67 uint64_t chunk_samples_size
;
78 ngx_chain_t out
[NGX_HTTP_MP4_LAST_ATOM
+ 1];
80 ngx_buf_t trak_atom_buf
;
81 ngx_buf_t tkhd_atom_buf
;
82 ngx_buf_t mdia_atom_buf
;
83 ngx_buf_t mdhd_atom_buf
;
84 ngx_buf_t hdlr_atom_buf
;
85 ngx_buf_t minf_atom_buf
;
86 ngx_buf_t vmhd_atom_buf
;
87 ngx_buf_t smhd_atom_buf
;
88 ngx_buf_t dinf_atom_buf
;
89 ngx_buf_t stbl_atom_buf
;
90 ngx_buf_t stsd_atom_buf
;
91 ngx_buf_t stts_atom_buf
;
92 ngx_buf_t stts_data_buf
;
93 ngx_buf_t stss_atom_buf
;
94 ngx_buf_t stss_data_buf
;
95 ngx_buf_t ctts_atom_buf
;
96 ngx_buf_t ctts_data_buf
;
97 ngx_buf_t stsc_atom_buf
;
98 ngx_buf_t stsc_chunk_buf
;
99 ngx_buf_t stsc_data_buf
;
100 ngx_buf_t stsz_atom_buf
;
101 ngx_buf_t stsz_data_buf
;
102 ngx_buf_t stco_atom_buf
;
103 ngx_buf_t stco_data_buf
;
104 ngx_buf_t co64_atom_buf
;
105 ngx_buf_t co64_data_buf
;
107 ngx_mp4_stsc_entry_t stsc_chunk_entry
;
108 } ngx_http_mp4_trak_t
;
115 u_char
*buffer_start
;
122 off_t content_length
;
125 ngx_http_request_t
*request
;
127 ngx_http_mp4_trak_t traks
[2];
133 ngx_chain_t ftyp_atom
;
134 ngx_chain_t moov_atom
;
135 ngx_chain_t mvhd_atom
;
136 ngx_chain_t mdat_atom
;
137 ngx_chain_t mdat_data
;
139 ngx_buf_t ftyp_atom_buf
;
140 ngx_buf_t moov_atom_buf
;
141 ngx_buf_t mvhd_atom_buf
;
142 ngx_buf_t mdat_atom_buf
;
143 ngx_buf_t mdat_data_buf
;
145 u_char moov_atom_header
[8];
146 u_char mdat_atom_header
[16];
147 } ngx_http_mp4_file_t
;
152 ngx_int_t (*handler
)(ngx_http_mp4_file_t
*mp4
,
153 uint64_t atom_data_size
);
154 } ngx_http_mp4_atom_handler_t
;
157 #define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8)
158 #define ngx_mp4_atom_data(mp4) mp4->buffer_pos
159 #define ngx_mp4_atom_data_size(t) (uint64_t) (sizeof(t) - 8)
160 #define ngx_mp4_atom_next(mp4, n) mp4->buffer_pos += n; mp4->offset += n
163 #define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \
164 ((u_char *) (p))[4] = n1; \
165 ((u_char *) (p))[5] = n2; \
166 ((u_char *) (p))[6] = n3; \
167 ((u_char *) (p))[7] = n4
169 #define ngx_mp4_get_32value(p) \
170 ( ((uint32_t) ((u_char *) (p))[0] << 24) \
171 + ( ((u_char *) (p))[1] << 16) \
172 + ( ((u_char *) (p))[2] << 8) \
173 + ( ((u_char *) (p))[3]) )
175 #define ngx_mp4_set_32value(p, n) \
176 ((u_char *) (p))[0] = (u_char) ((n) >> 24); \
177 ((u_char *) (p))[1] = (u_char) ((n) >> 16); \
178 ((u_char *) (p))[2] = (u_char) ((n) >> 8); \
179 ((u_char *) (p))[3] = (u_char) (n)
181 #define ngx_mp4_get_64value(p) \
182 ( ((uint64_t) ((u_char *) (p))[0] << 56) \
183 + ((uint64_t) ((u_char *) (p))[1] << 48) \
184 + ((uint64_t) ((u_char *) (p))[2] << 40) \
185 + ((uint64_t) ((u_char *) (p))[3] << 32) \
186 + ((uint64_t) ((u_char *) (p))[4] << 24) \
187 + ( ((u_char *) (p))[5] << 16) \
188 + ( ((u_char *) (p))[6] << 8) \
189 + ( ((u_char *) (p))[7]) )
191 #define ngx_mp4_set_64value(p, n) \
192 ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \
193 ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \
194 ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \
195 ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \
196 ((u_char *) (p))[4] = (u_char) ( (n) >> 24); \
197 ((u_char *) (p))[5] = (u_char) ( (n) >> 16); \
198 ((u_char *) (p))[6] = (u_char) ( (n) >> 8); \
199 ((u_char *) (p))[7] = (u_char) (n)
201 #define ngx_mp4_last_trak(mp4) \
202 &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
205 static ngx_int_t
ngx_http_mp4_process(ngx_http_mp4_file_t
*mp4
);
206 static ngx_int_t
ngx_http_mp4_read_atom(ngx_http_mp4_file_t
*mp4
,
207 ngx_http_mp4_atom_handler_t
*atom
, uint64_t atom_data_size
);
208 static ngx_int_t
ngx_http_mp4_read(ngx_http_mp4_file_t
*mp4
, size_t size
);
209 static ngx_int_t
ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t
*mp4
,
210 uint64_t atom_data_size
);
211 static ngx_int_t
ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t
*mp4
,
212 uint64_t atom_data_size
);
213 static ngx_int_t
ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t
*mp4
,
214 uint64_t atom_data_size
);
215 static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t
*mp4
,
217 static ngx_int_t
ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t
*mp4
,
218 uint64_t atom_data_size
);
219 static ngx_int_t
ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t
*mp4
,
220 uint64_t atom_data_size
);
221 static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t
*mp4
,
222 ngx_http_mp4_trak_t
*trak
);
223 static ngx_int_t
ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t
*mp4
,
224 uint64_t atom_data_size
);
225 static ngx_int_t
ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t
*mp4
,
226 uint64_t atom_data_size
);
227 static ngx_int_t
ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t
*mp4
,
228 uint64_t atom_data_size
);
229 static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t
*mp4
,
230 ngx_http_mp4_trak_t
*trak
);
231 static ngx_int_t
ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t
*mp4
,
232 uint64_t atom_data_size
);
233 static ngx_int_t
ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t
*mp4
,
234 uint64_t atom_data_size
);
235 static ngx_int_t
ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t
*mp4
,
236 uint64_t atom_data_size
);
237 static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t
*mp4
,
238 ngx_http_mp4_trak_t
*trak
);
239 static ngx_int_t
ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t
*mp4
,
240 uint64_t atom_data_size
);
241 static ngx_int_t
ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t
*mp4
,
242 uint64_t atom_data_size
);
243 static ngx_int_t
ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t
*mp4
,
244 uint64_t atom_data_size
);
245 static ngx_int_t
ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t
*mp4
,
246 uint64_t atom_data_size
);
247 static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t
*mp4
,
248 ngx_http_mp4_trak_t
*trak
);
249 static ngx_int_t
ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t
*mp4
,
250 uint64_t atom_data_size
);
251 static ngx_int_t
ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t
*mp4
,
252 uint64_t atom_data_size
);
253 static ngx_int_t
ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t
*mp4
,
254 ngx_http_mp4_trak_t
*trak
);
255 static ngx_int_t
ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t
*mp4
,
256 uint64_t atom_data_size
);
257 static ngx_int_t
ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t
*mp4
,
258 ngx_http_mp4_trak_t
*trak
);
259 static ngx_int_t
ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t
*mp4
,
260 uint64_t atom_data_size
);
261 static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t
*mp4
,
262 ngx_http_mp4_trak_t
*trak
);
263 static ngx_int_t
ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t
*mp4
,
264 uint64_t atom_data_size
);
265 static ngx_int_t
ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t
*mp4
,
266 ngx_http_mp4_trak_t
*trak
);
267 static ngx_int_t
ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t
*mp4
,
268 uint64_t atom_data_size
);
269 static ngx_int_t
ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t
*mp4
,
270 ngx_http_mp4_trak_t
*trak
);
271 static ngx_int_t
ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t
*mp4
,
272 uint64_t atom_data_size
);
273 static ngx_int_t
ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t
*mp4
,
274 ngx_http_mp4_trak_t
*trak
);
275 static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t
*mp4
,
276 ngx_http_mp4_trak_t
*trak
, int32_t adjustment
);
277 static ngx_int_t
ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t
*mp4
,
278 uint64_t atom_data_size
);
279 static ngx_int_t
ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t
*mp4
,
280 ngx_http_mp4_trak_t
*trak
);
281 static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t
*mp4
,
282 ngx_http_mp4_trak_t
*trak
, off_t adjustment
);
283 static char *ngx_http_mp4(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
);
284 static void *ngx_http_mp4_create_conf(ngx_conf_t
*cf
);
285 static char *ngx_http_mp4_merge_conf(ngx_conf_t
*cf
, void *parent
, void *child
);
287 static ngx_command_t ngx_http_mp4_commands
[] = {
290 NGX_HTTP_LOC_CONF
|NGX_CONF_NOARGS
,
296 { ngx_string("mp4_buffer_size"),
297 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
298 ngx_conf_set_size_slot
,
299 NGX_HTTP_LOC_CONF_OFFSET
,
300 offsetof(ngx_http_mp4_conf_t
, buffer_size
),
303 { ngx_string("mp4_max_buffer_size"),
304 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_TAKE1
,
305 ngx_conf_set_size_slot
,
306 NGX_HTTP_LOC_CONF_OFFSET
,
307 offsetof(ngx_http_mp4_conf_t
, max_buffer_size
),
314 static ngx_http_module_t ngx_http_mp4_module_ctx
= {
315 NULL
, /* preconfiguration */
316 NULL
, /* postconfiguration */
318 NULL
, /* create main configuration */
319 NULL
, /* init main configuration */
321 NULL
, /* create server configuration */
322 NULL
, /* merge server configuration */
324 ngx_http_mp4_create_conf
, /* create location configuration */
325 ngx_http_mp4_merge_conf
/* merge location configuration */
329 ngx_module_t ngx_http_mp4_module
= {
331 &ngx_http_mp4_module_ctx
, /* module context */
332 ngx_http_mp4_commands
, /* module directives */
333 NGX_HTTP_MODULE
, /* module type */
334 NULL
, /* init master */
335 NULL
, /* init module */
336 NULL
, /* init process */
337 NULL
, /* init thread */
338 NULL
, /* exit thread */
339 NULL
, /* exit process */
340 NULL
, /* exit master */
341 NGX_MODULE_V1_PADDING
345 static ngx_http_mp4_atom_handler_t ngx_http_mp4_atoms
[] = {
346 { "ftyp", ngx_http_mp4_read_ftyp_atom
},
347 { "moov", ngx_http_mp4_read_moov_atom
},
348 { "mdat", ngx_http_mp4_read_mdat_atom
},
352 static ngx_http_mp4_atom_handler_t ngx_http_mp4_moov_atoms
[] = {
353 { "mvhd", ngx_http_mp4_read_mvhd_atom
},
354 { "trak", ngx_http_mp4_read_trak_atom
},
355 { "cmov", ngx_http_mp4_read_cmov_atom
},
359 static ngx_http_mp4_atom_handler_t ngx_http_mp4_trak_atoms
[] = {
360 { "tkhd", ngx_http_mp4_read_tkhd_atom
},
361 { "mdia", ngx_http_mp4_read_mdia_atom
},
365 static ngx_http_mp4_atom_handler_t ngx_http_mp4_mdia_atoms
[] = {
366 { "mdhd", ngx_http_mp4_read_mdhd_atom
},
367 { "hdlr", ngx_http_mp4_read_hdlr_atom
},
368 { "minf", ngx_http_mp4_read_minf_atom
},
372 static ngx_http_mp4_atom_handler_t ngx_http_mp4_minf_atoms
[] = {
373 { "vmhd", ngx_http_mp4_read_vmhd_atom
},
374 { "smhd", ngx_http_mp4_read_smhd_atom
},
375 { "dinf", ngx_http_mp4_read_dinf_atom
},
376 { "stbl", ngx_http_mp4_read_stbl_atom
},
380 static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms
[] = {
381 { "stsd", ngx_http_mp4_read_stsd_atom
},
382 { "stts", ngx_http_mp4_read_stts_atom
},
383 { "stss", ngx_http_mp4_read_stss_atom
},
384 { "ctts", ngx_http_mp4_read_ctts_atom
},
385 { "stsc", ngx_http_mp4_read_stsc_atom
},
386 { "stsz", ngx_http_mp4_read_stsz_atom
},
387 { "stco", ngx_http_mp4_read_stco_atom
},
388 { "co64", ngx_http_mp4_read_co64_atom
},
394 ngx_http_mp4_handler(ngx_http_request_t
*r
)
400 ngx_str_t path
, value
;
404 ngx_http_mp4_file_t
*mp4
;
405 ngx_open_file_info_t of
;
406 ngx_http_core_loc_conf_t
*clcf
;
408 if (!(r
->method
& (NGX_HTTP_GET
|NGX_HTTP_HEAD
))) {
409 return NGX_HTTP_NOT_ALLOWED
;
412 if (r
->uri
.data
[r
->uri
.len
- 1] == '/') {
416 rc
= ngx_http_discard_request_body(r
);
422 last
= ngx_http_map_uri_to_path(r
, &path
, &root
, 0);
424 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
427 log
= r
->connection
->log
;
429 path
.len
= last
- path
.data
;
431 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, log
, 0,
432 "http mp4 filename: \"%V\"", &path
);
434 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
436 ngx_memzero(&of
, sizeof(ngx_open_file_info_t
));
438 of
.read_ahead
= clcf
->read_ahead
;
439 of
.directio
= NGX_MAX_OFF_T_VALUE
;
440 of
.valid
= clcf
->open_file_cache_valid
;
441 of
.min_uses
= clcf
->open_file_cache_min_uses
;
442 of
.errors
= clcf
->open_file_cache_errors
;
443 of
.events
= clcf
->open_file_cache_events
;
445 if (ngx_http_set_disable_symlinks(r
, clcf
, &path
, &of
) != NGX_OK
) {
446 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
449 if (ngx_open_cached_file(clcf
->open_file_cache
, &path
, &of
, r
->pool
)
455 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
459 case NGX_ENAMETOOLONG
:
462 rc
= NGX_HTTP_NOT_FOUND
;
466 #if (NGX_HAVE_OPENAT)
472 rc
= NGX_HTTP_FORBIDDEN
;
477 level
= NGX_LOG_CRIT
;
478 rc
= NGX_HTTP_INTERNAL_SERVER_ERROR
;
482 if (rc
!= NGX_HTTP_NOT_FOUND
|| clcf
->log_not_found
) {
483 ngx_log_error(level
, log
, of
.err
,
484 "%s \"%s\" failed", of
.failed
, path
.data
);
492 if (ngx_close_file(of
.fd
) == NGX_FILE_ERROR
) {
493 ngx_log_error(NGX_LOG_ALERT
, log
, ngx_errno
,
494 ngx_close_file_n
" \"%s\" failed", path
.data
);
500 r
->root_tested
= !r
->error_page
;
504 r
->headers_out
.content_length_n
= of
.size
;
510 if (ngx_http_arg(r
, (u_char
*) "start", 5, &value
) == NGX_OK
) {
513 * A Flash player may send start value with a lot of digits
514 * after dot so strtod() is used instead of atofp(). NaNs and
515 * infinities become negative numbers after (int) conversion.
519 start
= (int) (strtod((char *) value
.data
, NULL
) * 1000);
521 if (ngx_errno
== 0 && start
>= 0) {
524 mp4
= ngx_pcalloc(r
->pool
, sizeof(ngx_http_mp4_file_t
));
526 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
529 mp4
->file
.fd
= of
.fd
;
530 mp4
->file
.name
= path
;
531 mp4
->file
.log
= r
->connection
->log
;;
533 mp4
->start
= (ngx_uint_t
) start
;
536 switch (ngx_http_mp4_process(mp4
)) {
540 ngx_pfree(r
->pool
, mp4
->buffer
);
543 ngx_pfree(r
->pool
, mp4
);
549 r
->headers_out
.content_length_n
= mp4
->content_length
;
552 default: /* NGX_ERROR */
554 ngx_pfree(r
->pool
, mp4
->buffer
);
557 ngx_pfree(r
->pool
, mp4
);
559 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
565 log
->action
= "sending mp4 to client";
567 if (clcf
->directio
<= of
.size
) {
570 * DIRECTIO is set on transfer only
571 * to allow kernel to cache "moov" atom
574 if (ngx_directio_on(of
.fd
) == NGX_FILE_ERROR
) {
575 ngx_log_error(NGX_LOG_ALERT
, log
, ngx_errno
,
576 ngx_directio_on_n
" \"%s\" failed", path
.data
);
582 mp4
->file
.directio
= 1;
586 r
->headers_out
.status
= NGX_HTTP_OK
;
587 r
->headers_out
.last_modified_time
= of
.mtime
;
589 if (ngx_http_set_etag(r
) != NGX_OK
) {
590 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
593 if (ngx_http_set_content_type(r
) != NGX_OK
) {
594 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
598 b
= ngx_pcalloc(r
->pool
, sizeof(ngx_buf_t
));
600 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
603 b
->file
= ngx_pcalloc(r
->pool
, sizeof(ngx_file_t
));
604 if (b
->file
== NULL
) {
605 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
609 rc
= ngx_http_send_header(r
);
611 if (rc
== NGX_ERROR
|| rc
> NGX_OK
|| r
->header_only
) {
616 return ngx_http_output_filter(r
, mp4
->out
);
620 b
->file_last
= of
.size
;
622 b
->in_file
= b
->file_last
? 1 : 0;
623 b
->last_buf
= (r
== r
->main
) ? 1 : 0;
624 b
->last_in_chain
= 1;
627 b
->file
->name
= path
;
629 b
->file
->directio
= of
.is_directio
;
634 return ngx_http_output_filter(r
, &out
);
639 ngx_http_mp4_process(ngx_http_mp4_file_t
*mp4
)
641 off_t start_offset
, adjustment
;
645 ngx_http_mp4_trak_t
*trak
;
646 ngx_http_mp4_conf_t
*conf
;
648 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
649 "mp4 start:%ui", mp4
->start
);
651 conf
= ngx_http_get_module_loc_conf(mp4
->request
, ngx_http_mp4_module
);
653 mp4
->buffer_size
= conf
->buffer_size
;
655 rc
= ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_atoms
, mp4
->end
);
660 if (mp4
->trak
.nelts
== 0) {
661 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
662 "no mp4 trak atoms were found in \"%s\"",
663 mp4
->file
.name
.data
);
667 if (mp4
->mdat_atom
.buf
== NULL
) {
668 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
669 "no mp4 mdat atom was found in \"%s\"",
670 mp4
->file
.name
.data
);
676 if (mp4
->ftyp_atom
.buf
) {
677 *prev
= &mp4
->ftyp_atom
;
678 prev
= &mp4
->ftyp_atom
.next
;
681 *prev
= &mp4
->moov_atom
;
682 prev
= &mp4
->moov_atom
.next
;
684 if (mp4
->mvhd_atom
.buf
) {
685 mp4
->moov_size
+= mp4
->mvhd_atom_buf
.last
- mp4
->mvhd_atom_buf
.pos
;
686 *prev
= &mp4
->mvhd_atom
;
687 prev
= &mp4
->mvhd_atom
.next
;
690 start_offset
= mp4
->end
;
691 trak
= mp4
->trak
.elts
;
693 for (i
= 0; i
< mp4
->trak
.nelts
; i
++) {
695 if (ngx_http_mp4_update_stts_atom(mp4
, &trak
[i
]) != NGX_OK
) {
699 if (ngx_http_mp4_update_stss_atom(mp4
, &trak
[i
]) != NGX_OK
) {
703 ngx_http_mp4_update_ctts_atom(mp4
, &trak
[i
]);
705 if (ngx_http_mp4_update_stsc_atom(mp4
, &trak
[i
]) != NGX_OK
) {
709 if (ngx_http_mp4_update_stsz_atom(mp4
, &trak
[i
]) != NGX_OK
) {
713 if (trak
[i
].out
[NGX_HTTP_MP4_CO64_DATA
].buf
) {
714 if (ngx_http_mp4_update_co64_atom(mp4
, &trak
[i
]) != NGX_OK
) {
719 if (ngx_http_mp4_update_stco_atom(mp4
, &trak
[i
]) != NGX_OK
) {
724 ngx_http_mp4_update_stbl_atom(mp4
, &trak
[i
]);
725 ngx_http_mp4_update_minf_atom(mp4
, &trak
[i
]);
726 trak
[i
].size
+= trak
[i
].mdhd_size
;
727 trak
[i
].size
+= trak
[i
].hdlr_size
;
728 ngx_http_mp4_update_mdia_atom(mp4
, &trak
[i
]);
729 trak
[i
].size
+= trak
[i
].tkhd_size
;
730 ngx_http_mp4_update_trak_atom(mp4
, &trak
[i
]);
732 mp4
->moov_size
+= trak
[i
].size
;
734 if (start_offset
> trak
[i
].start_offset
) {
735 start_offset
= trak
[i
].start_offset
;
738 *prev
= &trak
[i
].out
[NGX_HTTP_MP4_TRAK_ATOM
];
739 prev
= &trak
[i
].out
[NGX_HTTP_MP4_TRAK_ATOM
].next
;
741 for (j
= 0; j
< NGX_HTTP_MP4_LAST_ATOM
+ 1; j
++) {
742 if (trak
[i
].out
[j
].buf
) {
743 *prev
= &trak
[i
].out
[j
];
744 prev
= &trak
[i
].out
[j
].next
;
751 ngx_mp4_set_32value(mp4
->moov_atom_header
, mp4
->moov_size
);
752 ngx_mp4_set_atom_name(mp4
->moov_atom_header
, 'm', 'o', 'o', 'v');
753 mp4
->content_length
+= mp4
->moov_size
;
755 *prev
= &mp4
->mdat_atom
;
757 if (start_offset
> mp4
->mdat_data
.buf
->file_last
) {
758 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
759 "start time is out mp4 mdat atom in \"%s\"",
760 mp4
->file
.name
.data
);
764 adjustment
= mp4
->ftyp_size
+ mp4
->moov_size
765 + ngx_http_mp4_update_mdat_atom(mp4
, start_offset
)
768 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
769 "mp4 adjustment:%O", adjustment
);
771 for (i
= 0; i
< mp4
->trak
.nelts
; i
++) {
772 if (trak
[i
].out
[NGX_HTTP_MP4_CO64_DATA
].buf
) {
773 ngx_http_mp4_adjust_co64_atom(mp4
, &trak
[i
], adjustment
);
775 ngx_http_mp4_adjust_stco_atom(mp4
, &trak
[i
], (int32_t) adjustment
);
786 } ngx_mp4_atom_header_t
;
792 } ngx_mp4_atom_header64_t
;
796 ngx_http_mp4_read_atom(ngx_http_mp4_file_t
*mp4
,
797 ngx_http_mp4_atom_handler_t
*atom
, uint64_t atom_data_size
)
800 size_t atom_header_size
;
801 u_char
*atom_header
, *atom_name
;
806 end
= mp4
->offset
+ atom_data_size
;
808 while (mp4
->offset
< end
) {
810 if (ngx_http_mp4_read(mp4
, sizeof(uint32_t)) != NGX_OK
) {
814 atom_header
= mp4
->buffer_pos
;
815 atom_size
= ngx_mp4_get_32value(atom_header
);
816 atom_header_size
= sizeof(ngx_mp4_atom_header_t
);
818 if (atom_size
== 0) {
819 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
824 if (atom_size
< sizeof(ngx_mp4_atom_header_t
)) {
826 if (atom_size
== 1) {
828 if (ngx_http_mp4_read(mp4
, sizeof(ngx_mp4_atom_header64_t
))
834 /* 64-bit atom size */
835 atom_header
= mp4
->buffer_pos
;
836 atom_size
= ngx_mp4_get_64value(atom_header
+ 8);
837 atom_header_size
= sizeof(ngx_mp4_atom_header64_t
);
840 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
841 "\"%s\" mp4 atom is too small:%uL",
842 mp4
->file
.name
.data
, atom_size
);
847 if (ngx_http_mp4_read(mp4
, sizeof(ngx_mp4_atom_header_t
)) != NGX_OK
) {
851 atom_header
= mp4
->buffer_pos
;
852 atom_name
= atom_header
+ sizeof(uint32_t);
854 ngx_log_debug4(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
855 "mp4 atom: %*s @%O:%uL",
856 4, atom_name
, mp4
->offset
, atom_size
);
858 if (atom_size
> (uint64_t) (NGX_MAX_OFF_T_VALUE
- mp4
->offset
)
859 || mp4
->offset
+ (off_t
) atom_size
> end
)
861 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
862 "\"%s\" mp4 atom too large:%uL",
863 mp4
->file
.name
.data
, atom_size
);
867 for (n
= 0; atom
[n
].name
; n
++) {
869 if (ngx_strncmp(atom_name
, atom
[n
].name
, 4) == 0) {
871 ngx_mp4_atom_next(mp4
, atom_header_size
);
873 rc
= atom
[n
].handler(mp4
, atom_size
- atom_header_size
);
882 ngx_mp4_atom_next(mp4
, atom_size
);
893 ngx_http_mp4_read(ngx_http_mp4_file_t
*mp4
, size_t size
)
897 if (mp4
->buffer_pos
+ size
<= mp4
->buffer_end
) {
901 if (mp4
->offset
+ (off_t
) mp4
->buffer_size
> mp4
->end
) {
902 mp4
->buffer_size
= (size_t) (mp4
->end
- mp4
->offset
);
905 if (mp4
->buffer_size
< size
) {
906 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
907 "\"%s\" mp4 file truncated", mp4
->file
.name
.data
);
911 if (mp4
->buffer
== NULL
) {
912 mp4
->buffer
= ngx_palloc(mp4
->request
->pool
, mp4
->buffer_size
);
913 if (mp4
->buffer
== NULL
) {
917 mp4
->buffer_start
= mp4
->buffer
;
920 n
= ngx_read_file(&mp4
->file
, mp4
->buffer_start
, mp4
->buffer_size
,
923 if (n
== NGX_ERROR
) {
927 if ((size_t) n
!= mp4
->buffer_size
) {
928 ngx_log_error(NGX_LOG_CRIT
, mp4
->file
.log
, 0,
929 ngx_read_file_n
" read only %z of %z from \"%s\"",
930 n
, mp4
->buffer_size
, mp4
->file
.name
.data
);
934 mp4
->buffer_pos
= mp4
->buffer_start
;
935 mp4
->buffer_end
= mp4
->buffer_start
+ mp4
->buffer_size
;
942 ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
948 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 ftyp atom");
950 if (atom_data_size
> 1024
951 || ngx_mp4_atom_data(mp4
) + atom_data_size
> mp4
->buffer_end
)
953 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
954 "\"%s\" mp4 ftyp atom is too large:%uL",
955 mp4
->file
.name
.data
, atom_data_size
);
959 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
961 ftyp_atom
= ngx_palloc(mp4
->request
->pool
, atom_size
);
962 if (ftyp_atom
== NULL
) {
966 ngx_mp4_set_32value(ftyp_atom
, atom_size
);
967 ngx_mp4_set_atom_name(ftyp_atom
, 'f', 't', 'y', 'p');
970 * only moov atom content is guaranteed to be in mp4->buffer
971 * during sending response, so ftyp atom content should be copied
973 ngx_memcpy(ftyp_atom
+ sizeof(ngx_mp4_atom_header_t
),
974 ngx_mp4_atom_data(mp4
), (size_t) atom_data_size
);
976 atom
= &mp4
->ftyp_atom_buf
;
978 atom
->pos
= ftyp_atom
;
979 atom
->last
= ftyp_atom
+ atom_size
;
981 mp4
->ftyp_atom
.buf
= atom
;
982 mp4
->ftyp_size
= atom_size
;
983 mp4
->content_length
= atom_size
;
985 ngx_mp4_atom_next(mp4
, atom_data_size
);
992 * Small excess buffer to process atoms after moov atom, mp4->buffer_start
993 * will be set to this buffer part after moov atom processing.
995 #define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS (4 * 1024)
998 ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1003 ngx_http_mp4_conf_t
*conf
;
1005 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 moov atom");
1007 no_mdat
= (mp4
->mdat_atom
.buf
== NULL
);
1009 if (no_mdat
&& mp4
->start
== 0) {
1011 * send original file if moov atom resides before
1012 * mdat atom and client requests integral file
1014 return NGX_DECLINED
;
1017 conf
= ngx_http_get_module_loc_conf(mp4
->request
, ngx_http_mp4_module
);
1019 if (atom_data_size
> mp4
->buffer_size
) {
1021 if (atom_data_size
> conf
->max_buffer_size
) {
1022 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1023 "\"%s\" mp4 moov atom is too large:%uL, "
1024 "you may want to increase mp4_max_buffer_size",
1025 mp4
->file
.name
.data
, atom_data_size
);
1029 ngx_pfree(mp4
->request
->pool
, mp4
->buffer
);
1031 mp4
->buffer_pos
= NULL
;
1032 mp4
->buffer_end
= NULL
;
1034 mp4
->buffer_size
= (size_t) atom_data_size
1035 + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS
* no_mdat
;
1038 if (ngx_http_mp4_read(mp4
, (size_t) atom_data_size
) != NGX_OK
) {
1042 mp4
->trak
.elts
= &mp4
->traks
;
1043 mp4
->trak
.size
= sizeof(ngx_http_mp4_trak_t
);
1044 mp4
->trak
.nalloc
= 2;
1045 mp4
->trak
.pool
= mp4
->request
->pool
;
1047 atom
= &mp4
->moov_atom_buf
;
1048 atom
->temporary
= 1;
1049 atom
->pos
= mp4
->moov_atom_header
;
1050 atom
->last
= mp4
->moov_atom_header
+ 8;
1052 mp4
->moov_atom
.buf
= &mp4
->moov_atom_buf
;
1054 rc
= ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_moov_atoms
, atom_data_size
);
1056 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 moov atom done");
1059 mp4
->buffer_start
= mp4
->buffer_pos
;
1060 mp4
->buffer_size
= NGX_HTTP_MP4_MOOV_BUFFER_EXCESS
;
1062 if (mp4
->buffer_start
+ mp4
->buffer_size
> mp4
->buffer_end
) {
1064 mp4
->buffer_pos
= NULL
;
1065 mp4
->buffer_end
= NULL
;
1069 /* skip atoms after moov atom */
1070 mp4
->offset
= mp4
->end
;
1078 ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1082 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 mdat atom");
1084 data
= &mp4
->mdat_data_buf
;
1085 data
->file
= &mp4
->file
;
1088 data
->last_in_chain
= 1;
1089 data
->file_last
= mp4
->offset
+ atom_data_size
;
1091 mp4
->mdat_atom
.buf
= &mp4
->mdat_atom_buf
;
1092 mp4
->mdat_atom
.next
= &mp4
->mdat_data
;
1093 mp4
->mdat_data
.buf
= data
;
1095 if (mp4
->trak
.nelts
) {
1096 /* skip atoms after mdat atom */
1097 mp4
->offset
= mp4
->end
;
1100 ngx_mp4_atom_next(mp4
, atom_data_size
);
1108 ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t
*mp4
, off_t start_offset
)
1110 off_t atom_data_size
;
1111 u_char
*atom_header
;
1112 uint32_t atom_header_size
;
1116 atom_data_size
= mp4
->mdat_data
.buf
->file_last
- start_offset
;
1117 mp4
->mdat_data
.buf
->file_pos
= start_offset
;
1119 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1120 "mdat new offset @%O:%O", start_offset
, atom_data_size
);
1122 atom_header
= mp4
->mdat_atom_header
;
1124 if ((uint64_t) atom_data_size
> 0xffffffff) {
1126 atom_header_size
= sizeof(ngx_mp4_atom_header64_t
);
1127 ngx_mp4_set_64value(atom_header
+ sizeof(ngx_mp4_atom_header_t
),
1128 sizeof(ngx_mp4_atom_header64_t
) + atom_data_size
);
1130 atom_size
= sizeof(ngx_mp4_atom_header_t
) + atom_data_size
;
1131 atom_header_size
= sizeof(ngx_mp4_atom_header_t
);
1134 mp4
->content_length
+= atom_header_size
+ atom_data_size
;
1136 ngx_mp4_set_32value(atom_header
, atom_size
);
1137 ngx_mp4_set_atom_name(atom_header
, 'm', 'd', 'a', 't');
1139 atom
= &mp4
->mdat_atom_buf
;
1140 atom
->temporary
= 1;
1141 atom
->pos
= atom_header
;
1142 atom
->last
= atom_header
+ atom_header_size
;
1144 return atom_header_size
;
1153 u_char creation_time
[4];
1154 u_char modification_time
[4];
1155 u_char timescale
[4];
1159 u_char reserved
[10];
1161 u_char preview_time
[4];
1162 u_char preview_duration
[4];
1163 u_char poster_time
[4];
1164 u_char selection_time
[4];
1165 u_char selection_duration
[4];
1166 u_char current_time
[4];
1167 u_char next_track_id
[4];
1168 } ngx_mp4_mvhd_atom_t
;
1175 u_char creation_time
[8];
1176 u_char modification_time
[8];
1177 u_char timescale
[4];
1181 u_char reserved
[10];
1183 u_char preview_time
[4];
1184 u_char preview_duration
[4];
1185 u_char poster_time
[4];
1186 u_char selection_time
[4];
1187 u_char selection_duration
[4];
1188 u_char current_time
[4];
1189 u_char next_track_id
[4];
1190 } ngx_mp4_mvhd64_atom_t
;
1194 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1196 u_char
*atom_header
;
1201 ngx_mp4_mvhd_atom_t
*mvhd_atom
;
1202 ngx_mp4_mvhd64_atom_t
*mvhd64_atom
;
1204 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 mvhd atom");
1206 atom_header
= ngx_mp4_atom_header(mp4
);
1207 mvhd_atom
= (ngx_mp4_mvhd_atom_t
*) atom_header
;
1208 mvhd64_atom
= (ngx_mp4_mvhd64_atom_t
*) atom_header
;
1209 ngx_mp4_set_atom_name(atom_header
, 'm', 'v', 'h', 'd');
1211 if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t
) > atom_data_size
) {
1212 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1213 "\"%s\" mp4 mvhd atom too small", mp4
->file
.name
.data
);
1217 if (mvhd_atom
->version
[0] == 0) {
1218 /* version 0: 32-bit duration */
1219 timescale
= ngx_mp4_get_32value(mvhd_atom
->timescale
);
1220 duration
= ngx_mp4_get_32value(mvhd_atom
->duration
);
1223 /* version 1: 64-bit duration */
1225 if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t
) > atom_data_size
) {
1226 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1227 "\"%s\" mp4 mvhd atom too small",
1228 mp4
->file
.name
.data
);
1232 timescale
= ngx_mp4_get_32value(mvhd64_atom
->timescale
);
1233 duration
= ngx_mp4_get_64value(mvhd64_atom
->duration
);
1236 mp4
->timescale
= timescale
;
1238 ngx_log_debug3(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1239 "mvhd timescale:%uD, duration:%uL, time:%.3fs",
1240 timescale
, duration
, (double) duration
/ timescale
);
1242 duration
-= (uint64_t) mp4
->start
* timescale
/ 1000;
1244 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1245 "mvhd new duration:%uL, time:%.3fs",
1246 duration
, (double) duration
/ timescale
);
1248 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1249 ngx_mp4_set_32value(mvhd_atom
->size
, atom_size
);
1251 if (mvhd_atom
->version
[0] == 0) {
1252 ngx_mp4_set_32value(mvhd_atom
->duration
, duration
);
1255 ngx_mp4_set_64value(mvhd64_atom
->duration
, duration
);
1258 atom
= &mp4
->mvhd_atom_buf
;
1259 atom
->temporary
= 1;
1260 atom
->pos
= atom_header
;
1261 atom
->last
= atom_header
+ atom_size
;
1263 mp4
->mvhd_atom
.buf
= atom
;
1265 ngx_mp4_atom_next(mp4
, atom_data_size
);
1272 ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1274 u_char
*atom_header
, *atom_end
;
1275 off_t atom_file_end
;
1278 ngx_http_mp4_trak_t
*trak
;
1280 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 trak atom");
1282 trak
= ngx_array_push(&mp4
->trak
);
1287 ngx_memzero(trak
, sizeof(ngx_http_mp4_trak_t
));
1289 atom_header
= ngx_mp4_atom_header(mp4
);
1290 ngx_mp4_set_atom_name(atom_header
, 't', 'r', 'a', 'k');
1292 atom
= &trak
->trak_atom_buf
;
1293 atom
->temporary
= 1;
1294 atom
->pos
= atom_header
;
1295 atom
->last
= atom_header
+ sizeof(ngx_mp4_atom_header_t
);
1297 trak
->out
[NGX_HTTP_MP4_TRAK_ATOM
].buf
= atom
;
1299 atom_end
= mp4
->buffer_pos
+ atom_data_size
;
1300 atom_file_end
= mp4
->offset
+ atom_data_size
;
1302 rc
= ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_trak_atoms
, atom_data_size
);
1304 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1305 "mp4 trak atom: %i", rc
);
1307 if (rc
== NGX_DECLINED
) {
1308 /* skip this trak */
1309 ngx_memzero(trak
, sizeof(ngx_http_mp4_trak_t
));
1311 mp4
->buffer_pos
= atom_end
;
1312 mp4
->offset
= atom_file_end
;
1321 ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t
*mp4
,
1322 ngx_http_mp4_trak_t
*trak
)
1326 trak
->size
+= sizeof(ngx_mp4_atom_header_t
);
1327 atom
= &trak
->trak_atom_buf
;
1328 ngx_mp4_set_32value(atom
->pos
, trak
->size
);
1333 ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1335 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1336 "\"%s\" mp4 compressed moov atom (cmov) is not supported",
1337 mp4
->file
.name
.data
);
1348 u_char creation_time
[4];
1349 u_char modification_time
[4];
1351 u_char reserved1
[4];
1353 u_char reserved2
[8];
1357 u_char reverved3
[2];
1361 } ngx_mp4_tkhd_atom_t
;
1368 u_char creation_time
[8];
1369 u_char modification_time
[8];
1371 u_char reserved1
[4];
1373 u_char reserved2
[8];
1377 u_char reverved3
[2];
1381 } ngx_mp4_tkhd64_atom_t
;
1385 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1387 u_char
*atom_header
;
1391 ngx_http_mp4_trak_t
*trak
;
1392 ngx_mp4_tkhd_atom_t
*tkhd_atom
;
1393 ngx_mp4_tkhd64_atom_t
*tkhd64_atom
;
1395 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 tkhd atom");
1397 atom_header
= ngx_mp4_atom_header(mp4
);
1398 tkhd_atom
= (ngx_mp4_tkhd_atom_t
*) atom_header
;
1399 tkhd64_atom
= (ngx_mp4_tkhd64_atom_t
*) atom_header
;
1400 ngx_mp4_set_atom_name(tkhd_atom
, 't', 'k', 'h', 'd');
1402 if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t
) > atom_data_size
) {
1403 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1404 "\"%s\" mp4 tkhd atom too small", mp4
->file
.name
.data
);
1408 if (tkhd_atom
->version
[0] == 0) {
1409 /* version 0: 32-bit duration */
1410 duration
= ngx_mp4_get_32value(tkhd_atom
->duration
);
1413 /* version 1: 64-bit duration */
1415 if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t
) > atom_data_size
) {
1416 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1417 "\"%s\" mp4 tkhd atom too small",
1418 mp4
->file
.name
.data
);
1422 duration
= ngx_mp4_get_64value(tkhd64_atom
->duration
);
1425 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1426 "tkhd duration:%uL, time:%.3fs",
1427 duration
, (double) duration
/ mp4
->timescale
);
1429 duration
-= (uint64_t) mp4
->start
* mp4
->timescale
/ 1000;
1431 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1432 "tkhd new duration:%uL, time:%.3fs",
1433 duration
, (double) duration
/ mp4
->timescale
);
1435 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1437 trak
= ngx_mp4_last_trak(mp4
);
1438 trak
->tkhd_size
= atom_size
;
1440 ngx_mp4_set_32value(tkhd_atom
->size
, atom_size
);
1442 if (tkhd_atom
->version
[0] == 0) {
1443 ngx_mp4_set_32value(tkhd_atom
->duration
, duration
);
1446 ngx_mp4_set_64value(tkhd64_atom
->duration
, duration
);
1449 atom
= &trak
->tkhd_atom_buf
;
1450 atom
->temporary
= 1;
1451 atom
->pos
= atom_header
;
1452 atom
->last
= atom_header
+ atom_size
;
1454 trak
->out
[NGX_HTTP_MP4_TKHD_ATOM
].buf
= atom
;
1456 ngx_mp4_atom_next(mp4
, atom_data_size
);
1463 ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1465 u_char
*atom_header
;
1467 ngx_http_mp4_trak_t
*trak
;
1469 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "process mdia atom");
1471 atom_header
= ngx_mp4_atom_header(mp4
);
1472 ngx_mp4_set_atom_name(atom_header
, 'm', 'd', 'i', 'a');
1474 trak
= ngx_mp4_last_trak(mp4
);
1476 atom
= &trak
->mdia_atom_buf
;
1477 atom
->temporary
= 1;
1478 atom
->pos
= atom_header
;
1479 atom
->last
= atom_header
+ sizeof(ngx_mp4_atom_header_t
);
1481 trak
->out
[NGX_HTTP_MP4_MDIA_ATOM
].buf
= atom
;
1483 return ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_mdia_atoms
, atom_data_size
);
1488 ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t
*mp4
,
1489 ngx_http_mp4_trak_t
*trak
)
1493 trak
->size
+= sizeof(ngx_mp4_atom_header_t
);
1494 atom
= &trak
->mdia_atom_buf
;
1495 ngx_mp4_set_32value(atom
->pos
, trak
->size
);
1504 u_char creation_time
[4];
1505 u_char modification_time
[4];
1506 u_char timescale
[4];
1510 } ngx_mp4_mdhd_atom_t
;
1517 u_char creation_time
[8];
1518 u_char modification_time
[8];
1519 u_char timescale
[4];
1523 } ngx_mp4_mdhd64_atom_t
;
1527 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1529 u_char
*atom_header
;
1534 ngx_http_mp4_trak_t
*trak
;
1535 ngx_mp4_mdhd_atom_t
*mdhd_atom
;
1536 ngx_mp4_mdhd64_atom_t
*mdhd64_atom
;
1538 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 mdhd atom");
1540 atom_header
= ngx_mp4_atom_header(mp4
);
1541 mdhd_atom
= (ngx_mp4_mdhd_atom_t
*) atom_header
;
1542 mdhd64_atom
= (ngx_mp4_mdhd64_atom_t
*) atom_header
;
1543 ngx_mp4_set_atom_name(mdhd_atom
, 'm', 'd', 'h', 'd');
1545 if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t
) > atom_data_size
) {
1546 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1547 "\"%s\" mp4 mdhd atom too small", mp4
->file
.name
.data
);
1551 if (mdhd_atom
->version
[0] == 0) {
1552 /* version 0: everything is 32-bit */
1553 timescale
= ngx_mp4_get_32value(mdhd_atom
->timescale
);
1554 duration
= ngx_mp4_get_32value(mdhd_atom
->duration
);
1557 /* version 1: 64-bit duration and 32-bit timescale */
1559 if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t
) > atom_data_size
) {
1560 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1561 "\"%s\" mp4 mdhd atom too small",
1562 mp4
->file
.name
.data
);
1566 timescale
= ngx_mp4_get_32value(mdhd64_atom
->timescale
);
1567 duration
= ngx_mp4_get_64value(mdhd64_atom
->duration
);
1570 ngx_log_debug3(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1571 "mdhd timescale:%uD, duration:%uL, time:%.3fs",
1572 timescale
, duration
, (double) duration
/ timescale
);
1574 duration
-= (uint64_t) mp4
->start
* timescale
/ 1000;
1576 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1577 "mdhd new duration:%uL, time:%.3fs",
1578 duration
, (double) duration
/ timescale
);
1580 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1582 trak
= ngx_mp4_last_trak(mp4
);
1583 trak
->mdhd_size
= atom_size
;
1584 trak
->timescale
= timescale
;
1586 ngx_mp4_set_32value(mdhd_atom
->size
, atom_size
);
1588 if (mdhd_atom
->version
[0] == 0) {
1589 ngx_mp4_set_32value(mdhd_atom
->duration
, duration
);
1592 ngx_mp4_set_64value(mdhd64_atom
->duration
, duration
);
1595 atom
= &trak
->mdhd_atom_buf
;
1596 atom
->temporary
= 1;
1597 atom
->pos
= atom_header
;
1598 atom
->last
= atom_header
+ atom_size
;
1600 trak
->out
[NGX_HTTP_MP4_MDHD_ATOM
].buf
= atom
;
1602 ngx_mp4_atom_next(mp4
, atom_data_size
);
1609 ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1611 u_char
*atom_header
;
1614 ngx_http_mp4_trak_t
*trak
;
1616 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 hdlr atom");
1618 atom_header
= ngx_mp4_atom_header(mp4
);
1619 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1620 ngx_mp4_set_32value(atom_header
, atom_size
);
1621 ngx_mp4_set_atom_name(atom_header
, 'h', 'd', 'l', 'r');
1623 trak
= ngx_mp4_last_trak(mp4
);
1625 atom
= &trak
->hdlr_atom_buf
;
1626 atom
->temporary
= 1;
1627 atom
->pos
= atom_header
;
1628 atom
->last
= atom_header
+ atom_size
;
1630 trak
->hdlr_size
= atom_size
;
1631 trak
->out
[NGX_HTTP_MP4_HDLR_ATOM
].buf
= atom
;
1633 ngx_mp4_atom_next(mp4
, atom_data_size
);
1640 ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1642 u_char
*atom_header
;
1644 ngx_http_mp4_trak_t
*trak
;
1646 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "process minf atom");
1648 atom_header
= ngx_mp4_atom_header(mp4
);
1649 ngx_mp4_set_atom_name(atom_header
, 'm', 'i', 'n', 'f');
1651 trak
= ngx_mp4_last_trak(mp4
);
1653 atom
= &trak
->minf_atom_buf
;
1654 atom
->temporary
= 1;
1655 atom
->pos
= atom_header
;
1656 atom
->last
= atom_header
+ sizeof(ngx_mp4_atom_header_t
);
1658 trak
->out
[NGX_HTTP_MP4_MINF_ATOM
].buf
= atom
;
1660 return ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_minf_atoms
, atom_data_size
);
1665 ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t
*mp4
,
1666 ngx_http_mp4_trak_t
*trak
)
1670 trak
->size
+= sizeof(ngx_mp4_atom_header_t
)
1674 atom
= &trak
->minf_atom_buf
;
1675 ngx_mp4_set_32value(atom
->pos
, trak
->size
);
1680 ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1682 u_char
*atom_header
;
1685 ngx_http_mp4_trak_t
*trak
;
1687 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 vmhd atom");
1689 atom_header
= ngx_mp4_atom_header(mp4
);
1690 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1691 ngx_mp4_set_32value(atom_header
, atom_size
);
1692 ngx_mp4_set_atom_name(atom_header
, 'v', 'm', 'h', 'd');
1694 trak
= ngx_mp4_last_trak(mp4
);
1696 atom
= &trak
->vmhd_atom_buf
;
1697 atom
->temporary
= 1;
1698 atom
->pos
= atom_header
;
1699 atom
->last
= atom_header
+ atom_size
;
1701 trak
->vmhd_size
+= atom_size
;
1702 trak
->out
[NGX_HTTP_MP4_VMHD_ATOM
].buf
= atom
;
1704 ngx_mp4_atom_next(mp4
, atom_data_size
);
1711 ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1713 u_char
*atom_header
;
1716 ngx_http_mp4_trak_t
*trak
;
1718 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 smhd atom");
1720 atom_header
= ngx_mp4_atom_header(mp4
);
1721 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1722 ngx_mp4_set_32value(atom_header
, atom_size
);
1723 ngx_mp4_set_atom_name(atom_header
, 's', 'm', 'h', 'd');
1725 trak
= ngx_mp4_last_trak(mp4
);
1727 atom
= &trak
->smhd_atom_buf
;
1728 atom
->temporary
= 1;
1729 atom
->pos
= atom_header
;
1730 atom
->last
= atom_header
+ atom_size
;
1732 trak
->vmhd_size
+= atom_size
;
1733 trak
->out
[NGX_HTTP_MP4_SMHD_ATOM
].buf
= atom
;
1735 ngx_mp4_atom_next(mp4
, atom_data_size
);
1742 ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1744 u_char
*atom_header
;
1747 ngx_http_mp4_trak_t
*trak
;
1749 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 dinf atom");
1751 atom_header
= ngx_mp4_atom_header(mp4
);
1752 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1753 ngx_mp4_set_32value(atom_header
, atom_size
);
1754 ngx_mp4_set_atom_name(atom_header
, 'd', 'i', 'n', 'f');
1756 trak
= ngx_mp4_last_trak(mp4
);
1758 atom
= &trak
->dinf_atom_buf
;
1759 atom
->temporary
= 1;
1760 atom
->pos
= atom_header
;
1761 atom
->last
= atom_header
+ atom_size
;
1763 trak
->dinf_size
+= atom_size
;
1764 trak
->out
[NGX_HTTP_MP4_DINF_ATOM
].buf
= atom
;
1766 ngx_mp4_atom_next(mp4
, atom_data_size
);
1773 ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1775 u_char
*atom_header
;
1777 ngx_http_mp4_trak_t
*trak
;
1779 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "process stbl atom");
1781 atom_header
= ngx_mp4_atom_header(mp4
);
1782 ngx_mp4_set_atom_name(atom_header
, 's', 't', 'b', 'l');
1784 trak
= ngx_mp4_last_trak(mp4
);
1786 atom
= &trak
->stbl_atom_buf
;
1787 atom
->temporary
= 1;
1788 atom
->pos
= atom_header
;
1789 atom
->last
= atom_header
+ sizeof(ngx_mp4_atom_header_t
);
1791 trak
->out
[NGX_HTTP_MP4_STBL_ATOM
].buf
= atom
;
1793 return ngx_http_mp4_read_atom(mp4
, ngx_http_mp4_stbl_atoms
, atom_data_size
);
1798 ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t
*mp4
,
1799 ngx_http_mp4_trak_t
*trak
)
1803 trak
->size
+= sizeof(ngx_mp4_atom_header_t
);
1804 atom
= &trak
->stbl_atom_buf
;
1805 ngx_mp4_set_32value(atom
->pos
, trak
->size
);
1816 u_char media_size
[4];
1817 u_char media_name
[4];
1818 } ngx_mp4_stsd_atom_t
;
1822 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1824 u_char
*atom_header
, *atom_table
;
1827 ngx_mp4_stsd_atom_t
*stsd_atom
;
1828 ngx_http_mp4_trak_t
*trak
;
1830 /* sample description atom */
1832 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stsd atom");
1834 atom_header
= ngx_mp4_atom_header(mp4
);
1835 stsd_atom
= (ngx_mp4_stsd_atom_t
*) atom_header
;
1836 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
1837 atom_table
= atom_header
+ atom_size
;
1838 ngx_mp4_set_32value(stsd_atom
->size
, atom_size
);
1839 ngx_mp4_set_atom_name(stsd_atom
, 's', 't', 's', 'd');
1841 if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t
) > atom_data_size
) {
1842 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1843 "\"%s\" mp4 stsd atom too small", mp4
->file
.name
.data
);
1847 ngx_log_debug3(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1848 "stsd entries:%uD, media:%*s",
1849 ngx_mp4_get_32value(stsd_atom
->entries
),
1850 4, stsd_atom
->media_name
);
1852 trak
= ngx_mp4_last_trak(mp4
);
1854 atom
= &trak
->stsd_atom_buf
;
1855 atom
->temporary
= 1;
1856 atom
->pos
= atom_header
;
1857 atom
->last
= atom_table
;
1859 trak
->out
[NGX_HTTP_MP4_STSD_ATOM
].buf
= atom
;
1860 trak
->size
+= atom_size
;
1862 ngx_mp4_atom_next(mp4
, atom_data_size
);
1874 } ngx_mp4_stts_atom_t
;
1879 } ngx_mp4_stts_entry_t
;
1883 ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
1885 u_char
*atom_header
, *atom_table
, *atom_end
;
1887 ngx_buf_t
*atom
, *data
;
1888 ngx_mp4_stts_atom_t
*stts_atom
;
1889 ngx_http_mp4_trak_t
*trak
;
1891 /* time-to-sample atom */
1893 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stts atom");
1895 atom_header
= ngx_mp4_atom_header(mp4
);
1896 stts_atom
= (ngx_mp4_stts_atom_t
*) atom_header
;
1897 ngx_mp4_set_atom_name(stts_atom
, 's', 't', 't', 's');
1899 if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t
) > atom_data_size
) {
1900 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1901 "\"%s\" mp4 stts atom too small", mp4
->file
.name
.data
);
1905 entries
= ngx_mp4_get_32value(stts_atom
->entries
);
1907 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1908 "mp4 time-to-sample entries:%uD", entries
);
1910 if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t
)
1911 + entries
* sizeof(ngx_mp4_stts_entry_t
) > atom_data_size
)
1913 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1914 "\"%s\" mp4 stts atom too small", mp4
->file
.name
.data
);
1918 atom_table
= atom_header
+ sizeof(ngx_mp4_stts_atom_t
);
1919 atom_end
= atom_table
+ entries
* sizeof(ngx_mp4_stts_entry_t
);
1921 trak
= ngx_mp4_last_trak(mp4
);
1922 trak
->time_to_sample_entries
= entries
;
1924 atom
= &trak
->stts_atom_buf
;
1925 atom
->temporary
= 1;
1926 atom
->pos
= atom_header
;
1927 atom
->last
= atom_table
;
1929 data
= &trak
->stts_data_buf
;
1930 data
->temporary
= 1;
1931 data
->pos
= atom_table
;
1932 data
->last
= atom_end
;
1934 trak
->out
[NGX_HTTP_MP4_STTS_ATOM
].buf
= atom
;
1935 trak
->out
[NGX_HTTP_MP4_STTS_DATA
].buf
= data
;
1937 ngx_mp4_atom_next(mp4
, atom_data_size
);
1944 ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t
*mp4
,
1945 ngx_http_mp4_trak_t
*trak
)
1948 uint32_t entries
, count
, duration
;
1949 uint64_t start_time
;
1950 ngx_buf_t
*atom
, *data
;
1951 ngx_uint_t start_sample
;
1952 ngx_mp4_stts_atom_t
*stts_atom
;
1953 ngx_mp4_stts_entry_t
*entry
, *end
;
1956 * mdia.minf.stbl.stts updating requires trak->timescale
1957 * from mdia.mdhd atom which may reside after mdia.minf
1960 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1961 "mp4 stts atom update");
1963 data
= trak
->out
[NGX_HTTP_MP4_STTS_DATA
].buf
;
1966 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
1967 "no mp4 stts atoms were found in \"%s\"",
1968 mp4
->file
.name
.data
);
1972 entries
= trak
->time_to_sample_entries
;
1973 start_time
= (uint64_t) mp4
->start
* trak
->timescale
/ 1000;
1975 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1976 "time-to-sample start_time:%uL", start_time
);
1979 entry
= (ngx_mp4_stts_entry_t
*) data
->pos
;
1980 end
= (ngx_mp4_stts_entry_t
*) data
->last
;
1982 while (entry
< end
) {
1983 count
= ngx_mp4_get_32value(entry
->count
);
1984 duration
= ngx_mp4_get_32value(entry
->duration
);
1986 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
1987 "count:%uD, duration:%uD", count
, duration
);
1989 if (start_time
< (uint64_t) count
* duration
) {
1990 start_sample
+= (ngx_uint_t
) (start_time
/ duration
);
1991 count
-= (uint32_t) (start_time
/ duration
);
1992 ngx_mp4_set_32value(entry
->count
, count
);
1996 start_sample
+= count
;
1997 start_time
-= count
* duration
;
2002 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2003 "start time is out mp4 stts samples in \"%s\"",
2004 mp4
->file
.name
.data
);
2010 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2011 "start_sample:%ui, new count:%uD", start_sample
, count
);
2013 trak
->start_sample
= start_sample
;
2015 data
->pos
= (u_char
*) entry
;
2016 atom_size
= sizeof(ngx_mp4_stts_atom_t
) + (data
->last
- data
->pos
);
2017 trak
->size
+= atom_size
;
2019 atom
= trak
->out
[NGX_HTTP_MP4_STTS_ATOM
].buf
;
2020 stts_atom
= (ngx_mp4_stts_atom_t
*) atom
->pos
;
2021 ngx_mp4_set_32value(stts_atom
->size
, atom_size
);
2022 ngx_mp4_set_32value(stts_atom
->entries
, entries
);
2034 } ngx_http_mp4_stss_atom_t
;
2038 ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2040 u_char
*atom_header
, *atom_table
, *atom_end
;
2042 ngx_buf_t
*atom
, *data
;
2043 ngx_http_mp4_trak_t
*trak
;
2044 ngx_http_mp4_stss_atom_t
*stss_atom
;
2046 /* sync samples atom */
2048 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stss atom");
2050 atom_header
= ngx_mp4_atom_header(mp4
);
2051 stss_atom
= (ngx_http_mp4_stss_atom_t
*) atom_header
;
2052 ngx_mp4_set_atom_name(stss_atom
, 's', 't', 's', 's');
2054 if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t
) > atom_data_size
) {
2055 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2056 "\"%s\" mp4 stss atom too small", mp4
->file
.name
.data
);
2060 entries
= ngx_mp4_get_32value(stss_atom
->entries
);
2062 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2063 "sync sample entries:%uD", entries
);
2065 trak
= ngx_mp4_last_trak(mp4
);
2066 trak
->sync_samples_entries
= entries
;
2068 atom_table
= atom_header
+ sizeof(ngx_http_mp4_stss_atom_t
);
2070 atom
= &trak
->stss_atom_buf
;
2071 atom
->temporary
= 1;
2072 atom
->pos
= atom_header
;
2073 atom
->last
= atom_table
;
2075 if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t
)
2076 + entries
* sizeof(uint32_t) > atom_data_size
)
2078 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2079 "\"%s\" mp4 stss atom too small", mp4
->file
.name
.data
);
2083 atom_end
= atom_table
+ entries
* sizeof(uint32_t);
2085 data
= &trak
->stss_data_buf
;
2086 data
->temporary
= 1;
2087 data
->pos
= atom_table
;
2088 data
->last
= atom_end
;
2090 trak
->out
[NGX_HTTP_MP4_STSS_ATOM
].buf
= atom
;
2091 trak
->out
[NGX_HTTP_MP4_STSS_DATA
].buf
= data
;
2093 ngx_mp4_atom_next(mp4
, atom_data_size
);
2100 ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t
*mp4
,
2101 ngx_http_mp4_trak_t
*trak
)
2104 uint32_t entries
, sample
, start_sample
, *entry
, *end
;
2105 ngx_buf_t
*atom
, *data
;
2106 ngx_http_mp4_stss_atom_t
*stss_atom
;
2109 * mdia.minf.stbl.stss updating requires trak->start_sample
2110 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2111 * atom which may reside after mdia.minf
2114 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2115 "mp4 stss atom update");
2117 data
= trak
->out
[NGX_HTTP_MP4_STSS_DATA
].buf
;
2123 /* sync samples starts from 1 */
2124 start_sample
= trak
->start_sample
+ 1;
2125 entries
= trak
->sync_samples_entries
;
2127 entry
= (uint32_t *) data
->pos
;
2128 end
= (uint32_t *) data
->last
;
2130 while (entry
< end
) {
2131 sample
= ngx_mp4_get_32value(entry
);
2133 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2134 "start:%uD, sync:%uD", start_sample
, sample
);
2136 if (sample
>= start_sample
) {
2144 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2145 "start sample is out of mp4 stss atom in \"%s\"",
2146 mp4
->file
.name
.data
);
2152 data
->pos
= (u_char
*) entry
;
2154 start_sample
= trak
->start_sample
;
2156 while (entry
< end
) {
2157 sample
= ngx_mp4_get_32value(entry
);
2158 sample
-= start_sample
;
2159 ngx_mp4_set_32value(entry
, sample
);
2163 atom_size
= sizeof(ngx_http_mp4_stss_atom_t
) + (data
->last
- data
->pos
);
2164 trak
->size
+= atom_size
;
2166 atom
= trak
->out
[NGX_HTTP_MP4_STSS_ATOM
].buf
;
2167 stss_atom
= (ngx_http_mp4_stss_atom_t
*) atom
->pos
;
2169 ngx_mp4_set_32value(stss_atom
->size
, atom_size
);
2170 ngx_mp4_set_32value(stss_atom
->entries
, entries
);
2182 } ngx_mp4_ctts_atom_t
;
2187 } ngx_mp4_ctts_entry_t
;
2191 ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2193 u_char
*atom_header
, *atom_table
, *atom_end
;
2195 ngx_buf_t
*atom
, *data
;
2196 ngx_mp4_ctts_atom_t
*ctts_atom
;
2197 ngx_http_mp4_trak_t
*trak
;
2199 /* composition offsets atom */
2201 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 ctts atom");
2203 atom_header
= ngx_mp4_atom_header(mp4
);
2204 ctts_atom
= (ngx_mp4_ctts_atom_t
*) atom_header
;
2205 ngx_mp4_set_atom_name(ctts_atom
, 'c', 't', 't', 's');
2207 if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t
) > atom_data_size
) {
2208 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2209 "\"%s\" mp4 ctts atom too small", mp4
->file
.name
.data
);
2213 entries
= ngx_mp4_get_32value(ctts_atom
->entries
);
2215 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2216 "composition offset entries:%uD", entries
);
2218 trak
= ngx_mp4_last_trak(mp4
);
2219 trak
->composition_offset_entries
= entries
;
2221 atom_table
= atom_header
+ sizeof(ngx_mp4_ctts_atom_t
);
2223 atom
= &trak
->ctts_atom_buf
;
2224 atom
->temporary
= 1;
2225 atom
->pos
= atom_header
;
2226 atom
->last
= atom_table
;
2228 if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t
)
2229 + entries
* sizeof(ngx_mp4_ctts_entry_t
) > atom_data_size
)
2231 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2232 "\"%s\" mp4 ctts atom too small", mp4
->file
.name
.data
);
2236 atom_end
= atom_table
+ entries
* sizeof(ngx_mp4_ctts_entry_t
);
2238 data
= &trak
->ctts_data_buf
;
2239 data
->temporary
= 1;
2240 data
->pos
= atom_table
;
2241 data
->last
= atom_end
;
2243 trak
->out
[NGX_HTTP_MP4_CTTS_ATOM
].buf
= atom
;
2244 trak
->out
[NGX_HTTP_MP4_CTTS_DATA
].buf
= data
;
2246 ngx_mp4_atom_next(mp4
, atom_data_size
);
2253 ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t
*mp4
,
2254 ngx_http_mp4_trak_t
*trak
)
2257 uint32_t entries
, count
, start_sample
;
2258 ngx_buf_t
*atom
, *data
;
2259 ngx_mp4_ctts_atom_t
*ctts_atom
;
2260 ngx_mp4_ctts_entry_t
*entry
, *end
;
2263 * mdia.minf.stbl.ctts updating requires trak->start_sample
2264 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2265 * atom which may reside after mdia.minf
2268 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2269 "mp4 ctts atom update");
2271 data
= trak
->out
[NGX_HTTP_MP4_CTTS_DATA
].buf
;
2277 /* sync samples starts from 1 */
2278 start_sample
= trak
->start_sample
+ 1;
2279 entries
= trak
->composition_offset_entries
;
2280 entry
= (ngx_mp4_ctts_entry_t
*) data
->pos
;
2281 end
= (ngx_mp4_ctts_entry_t
*) data
->last
;
2283 while (entry
< end
) {
2284 count
= ngx_mp4_get_32value(entry
->count
);
2286 ngx_log_debug3(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2287 "start:%uD, count:%uD, offset:%uD",
2288 start_sample
, count
, ngx_mp4_get_32value(entry
->offset
));
2290 if (start_sample
<= count
) {
2291 count
-= (start_sample
- 1);
2292 ngx_mp4_set_32value(entry
->count
, count
);
2296 start_sample
-= count
;
2301 trak
->out
[NGX_HTTP_MP4_CTTS_ATOM
].buf
= NULL
;
2302 trak
->out
[NGX_HTTP_MP4_CTTS_DATA
].buf
= NULL
;
2308 data
->pos
= (u_char
*) entry
;
2309 atom_size
= sizeof(ngx_mp4_ctts_atom_t
) + (data
->last
- data
->pos
);
2310 trak
->size
+= atom_size
;
2312 atom
= trak
->out
[NGX_HTTP_MP4_CTTS_ATOM
].buf
;
2313 ctts_atom
= (ngx_mp4_ctts_atom_t
*) atom
->pos
;
2315 ngx_mp4_set_32value(ctts_atom
->size
, atom_size
);
2316 ngx_mp4_set_32value(ctts_atom
->entries
, entries
);
2328 } ngx_mp4_stsc_atom_t
;
2332 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2334 u_char
*atom_header
, *atom_table
, *atom_end
;
2336 ngx_buf_t
*atom
, *data
;
2337 ngx_mp4_stsc_atom_t
*stsc_atom
;
2338 ngx_http_mp4_trak_t
*trak
;
2340 /* sample-to-chunk atom */
2342 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stsc atom");
2344 atom_header
= ngx_mp4_atom_header(mp4
);
2345 stsc_atom
= (ngx_mp4_stsc_atom_t
*) atom_header
;
2346 ngx_mp4_set_atom_name(stsc_atom
, 's', 't', 's', 'c');
2348 if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t
) > atom_data_size
) {
2349 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2350 "\"%s\" mp4 stsc atom too small", mp4
->file
.name
.data
);
2354 entries
= ngx_mp4_get_32value(stsc_atom
->entries
);
2356 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2357 "sample-to-chunk entries:%uD", entries
);
2359 if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t
)
2360 + entries
* sizeof(ngx_mp4_stsc_entry_t
) > atom_data_size
)
2362 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2363 "\"%s\" mp4 stsc atom too small", mp4
->file
.name
.data
);
2367 atom_table
= atom_header
+ sizeof(ngx_mp4_stsc_atom_t
);
2368 atom_end
= atom_table
+ entries
* sizeof(ngx_mp4_stsc_entry_t
);
2370 trak
= ngx_mp4_last_trak(mp4
);
2371 trak
->sample_to_chunk_entries
= entries
;
2373 atom
= &trak
->stsc_atom_buf
;
2374 atom
->temporary
= 1;
2375 atom
->pos
= atom_header
;
2376 atom
->last
= atom_table
;
2378 data
= &trak
->stsc_data_buf
;
2379 data
->temporary
= 1;
2380 data
->pos
= atom_table
;
2381 data
->last
= atom_end
;
2383 trak
->out
[NGX_HTTP_MP4_STSC_ATOM
].buf
= atom
;
2384 trak
->out
[NGX_HTTP_MP4_STSC_DATA
].buf
= data
;
2386 ngx_mp4_atom_next(mp4
, atom_data_size
);
2393 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t
*mp4
,
2394 ngx_http_mp4_trak_t
*trak
)
2397 uint32_t start_sample
, entries
, chunk
, samples
, id
,
2399 ngx_buf_t
*atom
, *data
, *buf
;
2400 ngx_mp4_stsc_atom_t
*stsc_atom
;
2401 ngx_mp4_stsc_entry_t
*entry
, *first
, *end
;
2404 * mdia.minf.stbl.stsc updating requires trak->start_sample
2405 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2406 * atom which may reside after mdia.minf
2409 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2410 "mp4 stsc atom update");
2412 data
= trak
->out
[NGX_HTTP_MP4_STSC_DATA
].buf
;
2415 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2416 "no mp4 stsc atoms were found in \"%s\"",
2417 mp4
->file
.name
.data
);
2421 if (trak
->sample_to_chunk_entries
== 0) {
2422 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2423 "zero number of entries in stsc atom in \"%s\"",
2424 mp4
->file
.name
.data
);
2428 start_sample
= (uint32_t) trak
->start_sample
;
2429 entries
= trak
->sample_to_chunk_entries
- 1;
2431 entry
= (ngx_mp4_stsc_entry_t
*) data
->pos
;
2432 end
= (ngx_mp4_stsc_entry_t
*) data
->last
;
2434 chunk
= ngx_mp4_get_32value(entry
->chunk
);
2435 samples
= ngx_mp4_get_32value(entry
->samples
);
2436 id
= ngx_mp4_get_32value(entry
->id
);
2439 while (entry
< end
) {
2441 next_chunk
= ngx_mp4_get_32value(entry
->chunk
);
2443 ngx_log_debug5(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2444 "start_sample:%uD, chunk:%uD, chunks:%uD, "
2445 "samples:%uD, id:%uD",
2446 start_sample
, chunk
, next_chunk
- chunk
, samples
, id
);
2448 n
= (next_chunk
- chunk
) * samples
;
2450 if (start_sample
<= n
) {
2457 samples
= ngx_mp4_get_32value(entry
->samples
);
2458 id
= ngx_mp4_get_32value(entry
->id
);
2463 next_chunk
= trak
->chunks
;
2465 ngx_log_debug4(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2466 "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
2467 start_sample
, chunk
, next_chunk
- chunk
, samples
);
2469 n
= (next_chunk
- chunk
) * samples
;
2471 if (start_sample
> n
) {
2472 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2473 "start time is out mp4 stsc chunks in \"%s\"",
2474 mp4
->file
.name
.data
);
2484 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2485 "zero number of samples in \"%s\"",
2486 mp4
->file
.name
.data
);
2490 trak
->start_chunk
= chunk
- 1;
2492 trak
->start_chunk
+= start_sample
/ samples
;
2493 trak
->chunk_samples
= start_sample
% samples
;
2495 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2496 "start chunk:%ui, samples:%uD",
2497 trak
->start_chunk
, trak
->chunk_samples
);
2499 data
->pos
= (u_char
*) entry
;
2500 atom_size
= sizeof(ngx_mp4_stsc_atom_t
) + (data
->last
- data
->pos
);
2502 ngx_mp4_set_32value(entry
->chunk
, 1);
2504 if (trak
->chunk_samples
&& next_chunk
- trak
->start_chunk
== 2) {
2506 /* last chunk in the entry */
2508 ngx_mp4_set_32value(entry
->samples
, samples
- trak
->chunk_samples
);
2510 } else if (trak
->chunk_samples
) {
2512 first
= &trak
->stsc_chunk_entry
;
2513 ngx_mp4_set_32value(first
->chunk
, 1);
2514 ngx_mp4_set_32value(first
->samples
, samples
- trak
->chunk_samples
);
2515 ngx_mp4_set_32value(first
->id
, id
);
2517 buf
= &trak
->stsc_chunk_buf
;
2519 buf
->pos
= (u_char
*) first
;
2520 buf
->last
= (u_char
*) first
+ sizeof(ngx_mp4_stsc_entry_t
);
2522 trak
->out
[NGX_HTTP_MP4_STSC_CHUNK
].buf
= buf
;
2524 ngx_mp4_set_32value(entry
->chunk
, 2);
2527 atom_size
+= sizeof(ngx_mp4_stsc_entry_t
);
2530 while (++entry
< end
) {
2531 chunk
= ngx_mp4_get_32value(entry
->chunk
);
2532 chunk
-= trak
->start_chunk
;
2533 ngx_mp4_set_32value(entry
->chunk
, chunk
);
2536 trak
->size
+= atom_size
;
2538 atom
= trak
->out
[NGX_HTTP_MP4_STSC_ATOM
].buf
;
2539 stsc_atom
= (ngx_mp4_stsc_atom_t
*) atom
->pos
;
2541 ngx_mp4_set_32value(stsc_atom
->size
, atom_size
);
2542 ngx_mp4_set_32value(stsc_atom
->entries
, entries
);
2553 u_char uniform_size
[4];
2555 } ngx_mp4_stsz_atom_t
;
2559 ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2561 u_char
*atom_header
, *atom_table
, *atom_end
;
2563 uint32_t entries
, size
;
2564 ngx_buf_t
*atom
, *data
;
2565 ngx_mp4_stsz_atom_t
*stsz_atom
;
2566 ngx_http_mp4_trak_t
*trak
;
2568 /* sample sizes atom */
2570 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stsz atom");
2572 atom_header
= ngx_mp4_atom_header(mp4
);
2573 stsz_atom
= (ngx_mp4_stsz_atom_t
*) atom_header
;
2574 ngx_mp4_set_atom_name(stsz_atom
, 's', 't', 's', 'z');
2576 if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t
) > atom_data_size
) {
2577 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2578 "\"%s\" mp4 stsz atom too small", mp4
->file
.name
.data
);
2582 size
= ngx_mp4_get_32value(stsz_atom
->uniform_size
);
2583 entries
= ngx_mp4_get_32value(stsz_atom
->entries
);
2585 ngx_log_debug2(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2586 "sample uniform size:%uD, entries:%uD", size
, entries
);
2588 trak
= ngx_mp4_last_trak(mp4
);
2589 trak
->sample_sizes_entries
= entries
;
2591 atom_table
= atom_header
+ sizeof(ngx_mp4_stsz_atom_t
);
2593 atom
= &trak
->stsz_atom_buf
;
2594 atom
->temporary
= 1;
2595 atom
->pos
= atom_header
;
2596 atom
->last
= atom_table
;
2598 trak
->out
[NGX_HTTP_MP4_STSZ_ATOM
].buf
= atom
;
2601 if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t
)
2602 + entries
* sizeof(uint32_t) > atom_data_size
)
2604 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2605 "\"%s\" mp4 stsz atom too small",
2606 mp4
->file
.name
.data
);
2610 atom_end
= atom_table
+ entries
* sizeof(uint32_t);
2612 data
= &trak
->stsz_data_buf
;
2613 data
->temporary
= 1;
2614 data
->pos
= atom_table
;
2615 data
->last
= atom_end
;
2617 trak
->out
[NGX_HTTP_MP4_STSZ_DATA
].buf
= data
;
2620 /* if size != 0 then all samples are the same size */
2621 /* TODO : chunk samples */
2622 atom_size
= sizeof(ngx_mp4_atom_header_t
) + (size_t) atom_data_size
;
2623 ngx_mp4_set_32value(atom_header
, atom_size
);
2624 trak
->size
+= atom_size
;
2627 ngx_mp4_atom_next(mp4
, atom_data_size
);
2634 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t
*mp4
,
2635 ngx_http_mp4_trak_t
*trak
)
2638 uint32_t *pos
, *end
;
2639 ngx_buf_t
*atom
, *data
;
2640 ngx_mp4_stsz_atom_t
*stsz_atom
;
2643 * mdia.minf.stbl.stsz updating requires trak->start_sample
2644 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2645 * atom which may reside after mdia.minf
2648 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2649 "mp4 stsz atom update");
2651 data
= trak
->out
[NGX_HTTP_MP4_STSZ_DATA
].buf
;
2654 if (trak
->start_sample
> trak
->sample_sizes_entries
) {
2655 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2656 "start time is out mp4 stsz samples in \"%s\"",
2657 mp4
->file
.name
.data
);
2661 data
->pos
+= trak
->start_sample
* sizeof(uint32_t);
2662 end
= (uint32_t *) data
->pos
;
2664 for (pos
= end
- trak
->chunk_samples
; pos
< end
; pos
++) {
2665 trak
->chunk_samples_size
+= ngx_mp4_get_32value(pos
);
2668 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2669 "chunk samples sizes:%uL", trak
->chunk_samples_size
);
2671 atom_size
= sizeof(ngx_mp4_stsz_atom_t
) + (data
->last
- data
->pos
);
2672 trak
->size
+= atom_size
;
2674 atom
= trak
->out
[NGX_HTTP_MP4_STSZ_ATOM
].buf
;
2675 stsz_atom
= (ngx_mp4_stsz_atom_t
*) atom
->pos
;
2677 ngx_mp4_set_32value(stsz_atom
->size
, atom_size
);
2678 ngx_mp4_set_32value(stsz_atom
->entries
,
2679 trak
->sample_sizes_entries
- trak
->start_sample
);
2692 } ngx_mp4_stco_atom_t
;
2696 ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2698 u_char
*atom_header
, *atom_table
, *atom_end
;
2700 ngx_buf_t
*atom
, *data
;
2701 ngx_mp4_stco_atom_t
*stco_atom
;
2702 ngx_http_mp4_trak_t
*trak
;
2704 /* chunk offsets atom */
2706 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 stco atom");
2708 atom_header
= ngx_mp4_atom_header(mp4
);
2709 stco_atom
= (ngx_mp4_stco_atom_t
*) atom_header
;
2710 ngx_mp4_set_atom_name(stco_atom
, 's', 't', 'c', 'o');
2712 if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t
) > atom_data_size
) {
2713 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2714 "\"%s\" mp4 stco atom too small", mp4
->file
.name
.data
);
2718 entries
= ngx_mp4_get_32value(stco_atom
->entries
);
2720 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "chunks:%uD", entries
);
2722 if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t
)
2723 + entries
* sizeof(uint32_t) > atom_data_size
)
2725 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2726 "\"%s\" mp4 stco atom too small", mp4
->file
.name
.data
);
2730 atom_table
= atom_header
+ sizeof(ngx_mp4_stco_atom_t
);
2731 atom_end
= atom_table
+ entries
* sizeof(uint32_t);
2733 trak
= ngx_mp4_last_trak(mp4
);
2734 trak
->chunks
= entries
;
2736 atom
= &trak
->stco_atom_buf
;
2737 atom
->temporary
= 1;
2738 atom
->pos
= atom_header
;
2739 atom
->last
= atom_table
;
2741 data
= &trak
->stco_data_buf
;
2742 data
->temporary
= 1;
2743 data
->pos
= atom_table
;
2744 data
->last
= atom_end
;
2746 trak
->out
[NGX_HTTP_MP4_STCO_ATOM
].buf
= atom
;
2747 trak
->out
[NGX_HTTP_MP4_STCO_DATA
].buf
= data
;
2749 ngx_mp4_atom_next(mp4
, atom_data_size
);
2756 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t
*mp4
,
2757 ngx_http_mp4_trak_t
*trak
)
2760 ngx_buf_t
*atom
, *data
;
2761 ngx_mp4_stco_atom_t
*stco_atom
;
2764 * mdia.minf.stbl.stco updating requires trak->start_chunk
2765 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
2766 * atom which may reside after mdia.minf
2769 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2770 "mp4 stco atom update");
2772 data
= trak
->out
[NGX_HTTP_MP4_STCO_DATA
].buf
;
2775 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2776 "no mp4 stco atoms were found in \"%s\"",
2777 mp4
->file
.name
.data
);
2781 if (trak
->start_chunk
> trak
->chunks
) {
2782 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2783 "start time is out mp4 stco chunks in \"%s\"",
2784 mp4
->file
.name
.data
);
2788 data
->pos
+= trak
->start_chunk
* sizeof(uint32_t);
2789 atom_size
= sizeof(ngx_mp4_stco_atom_t
) + (data
->last
- data
->pos
);
2790 trak
->size
+= atom_size
;
2792 trak
->start_offset
= ngx_mp4_get_32value(data
->pos
);
2793 trak
->start_offset
+= trak
->chunk_samples_size
;
2794 ngx_mp4_set_32value(data
->pos
, trak
->start_offset
);
2796 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2797 "start chunk offset:%uD", trak
->start_offset
);
2799 atom
= trak
->out
[NGX_HTTP_MP4_STCO_ATOM
].buf
;
2800 stco_atom
= (ngx_mp4_stco_atom_t
*) atom
->pos
;
2802 ngx_mp4_set_32value(stco_atom
->size
, atom_size
);
2803 ngx_mp4_set_32value(stco_atom
->entries
, trak
->chunks
- trak
->start_chunk
);
2810 ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t
*mp4
,
2811 ngx_http_mp4_trak_t
*trak
, int32_t adjustment
)
2813 uint32_t offset
, *entry
, *end
;
2817 * moov.trak.mdia.minf.stbl.stco adjustment requires
2818 * minimal start offset of all traks and new moov atom size
2821 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2822 "mp4 stco atom adjustment");
2824 data
= trak
->out
[NGX_HTTP_MP4_STCO_DATA
].buf
;
2825 entry
= (uint32_t *) data
->pos
;
2826 end
= (uint32_t *) data
->last
;
2828 while (entry
< end
) {
2829 offset
= ngx_mp4_get_32value(entry
);
2830 offset
+= adjustment
;
2831 ngx_mp4_set_32value(entry
, offset
);
2843 } ngx_mp4_co64_atom_t
;
2847 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t
*mp4
, uint64_t atom_data_size
)
2849 u_char
*atom_header
, *atom_table
, *atom_end
;
2851 ngx_buf_t
*atom
, *data
;
2852 ngx_mp4_co64_atom_t
*co64_atom
;
2853 ngx_http_mp4_trak_t
*trak
;
2855 /* chunk offsets atom */
2857 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "mp4 co64 atom");
2859 atom_header
= ngx_mp4_atom_header(mp4
);
2860 co64_atom
= (ngx_mp4_co64_atom_t
*) atom_header
;
2861 ngx_mp4_set_atom_name(co64_atom
, 'c', 'o', '6', '4');
2863 if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t
) > atom_data_size
) {
2864 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2865 "\"%s\" mp4 co64 atom too small", mp4
->file
.name
.data
);
2869 entries
= ngx_mp4_get_32value(co64_atom
->entries
);
2871 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0, "chunks:%uD", entries
);
2873 if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t
)
2874 + entries
* sizeof(uint64_t) > atom_data_size
)
2876 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2877 "\"%s\" mp4 co64 atom too small", mp4
->file
.name
.data
);
2881 atom_table
= atom_header
+ sizeof(ngx_mp4_co64_atom_t
);
2882 atom_end
= atom_table
+ entries
* sizeof(uint64_t);
2884 trak
= ngx_mp4_last_trak(mp4
);
2885 trak
->chunks
= entries
;
2887 atom
= &trak
->co64_atom_buf
;
2888 atom
->temporary
= 1;
2889 atom
->pos
= atom_header
;
2890 atom
->last
= atom_table
;
2892 data
= &trak
->co64_data_buf
;
2893 data
->temporary
= 1;
2894 data
->pos
= atom_table
;
2895 data
->last
= atom_end
;
2897 trak
->out
[NGX_HTTP_MP4_CO64_ATOM
].buf
= atom
;
2898 trak
->out
[NGX_HTTP_MP4_CO64_DATA
].buf
= data
;
2900 ngx_mp4_atom_next(mp4
, atom_data_size
);
2907 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t
*mp4
,
2908 ngx_http_mp4_trak_t
*trak
)
2911 ngx_buf_t
*atom
, *data
;
2912 ngx_mp4_co64_atom_t
*co64_atom
;
2915 * mdia.minf.stbl.co64 updating requires trak->start_chunk
2916 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
2917 * atom which may reside after mdia.minf
2920 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2921 "mp4 co64 atom update");
2923 data
= trak
->out
[NGX_HTTP_MP4_CO64_DATA
].buf
;
2926 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2927 "no mp4 co64 atoms were found in \"%s\"",
2928 mp4
->file
.name
.data
);
2932 if (trak
->start_chunk
> trak
->chunks
) {
2933 ngx_log_error(NGX_LOG_ERR
, mp4
->file
.log
, 0,
2934 "start time is out mp4 co64 chunks in \"%s\"",
2935 mp4
->file
.name
.data
);
2939 data
->pos
+= trak
->start_chunk
* sizeof(uint64_t);
2940 atom_size
= sizeof(ngx_mp4_co64_atom_t
) + (data
->last
- data
->pos
);
2941 trak
->size
+= atom_size
;
2943 trak
->start_offset
= ngx_mp4_get_64value(data
->pos
);
2944 trak
->start_offset
+= trak
->chunk_samples_size
;
2945 ngx_mp4_set_64value(data
->pos
, trak
->start_offset
);
2947 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2948 "start chunk offset:%uL", trak
->start_offset
);
2950 atom
= trak
->out
[NGX_HTTP_MP4_CO64_ATOM
].buf
;
2951 co64_atom
= (ngx_mp4_co64_atom_t
*) atom
->pos
;
2953 ngx_mp4_set_32value(co64_atom
->size
, atom_size
);
2954 ngx_mp4_set_32value(co64_atom
->entries
, trak
->chunks
- trak
->start_chunk
);
2961 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t
*mp4
,
2962 ngx_http_mp4_trak_t
*trak
, off_t adjustment
)
2964 uint64_t offset
, *entry
, *end
;
2968 * moov.trak.mdia.minf.stbl.co64 adjustment requires
2969 * minimal start offset of all traks and new moov atom size
2972 ngx_log_debug0(NGX_LOG_DEBUG_HTTP
, mp4
->file
.log
, 0,
2973 "mp4 co64 atom adjustment");
2975 data
= trak
->out
[NGX_HTTP_MP4_CO64_DATA
].buf
;
2976 entry
= (uint64_t *) data
->pos
;
2977 end
= (uint64_t *) data
->last
;
2979 while (entry
< end
) {
2980 offset
= ngx_mp4_get_64value(entry
);
2981 offset
+= adjustment
;
2982 ngx_mp4_set_64value(entry
, offset
);
2989 ngx_http_mp4(ngx_conf_t
*cf
, ngx_command_t
*cmd
, void *conf
)
2991 ngx_http_core_loc_conf_t
*clcf
;
2993 clcf
= ngx_http_conf_get_module_loc_conf(cf
, ngx_http_core_module
);
2994 clcf
->handler
= ngx_http_mp4_handler
;
3001 ngx_http_mp4_create_conf(ngx_conf_t
*cf
)
3003 ngx_http_mp4_conf_t
*conf
;
3005 conf
= ngx_palloc(cf
->pool
, sizeof(ngx_http_mp4_conf_t
));
3010 conf
->buffer_size
= NGX_CONF_UNSET_SIZE
;
3011 conf
->max_buffer_size
= NGX_CONF_UNSET_SIZE
;
3018 ngx_http_mp4_merge_conf(ngx_conf_t
*cf
, void *parent
, void *child
)
3020 ngx_http_mp4_conf_t
*prev
= parent
;
3021 ngx_http_mp4_conf_t
*conf
= child
;
3023 ngx_conf_merge_size_value(conf
->buffer_size
, prev
->buffer_size
, 512 * 1024);
3024 ngx_conf_merge_size_value(conf
->max_buffer_size
, prev
->max_buffer_size
,