~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Nginx/http/modules/ngx_http_mp4_module.c

Version: ~ [ nginx-1.4.1 ] ~ [ nginx-1.5.0 ] ~

  1 
  2 /*
  3  * Copyright (C) Igor Sysoev
  4  * Copyright (C) Nginx, Inc.
  5  */
  6 
  7 #include <ngx_config.h>
  8 #include <ngx_core.h>
  9 #include <ngx_http.h>
 10 
 11 
 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
 38 
 39 #define NGX_HTTP_MP4_LAST_ATOM    NGX_HTTP_MP4_CO64_DATA
 40 
 41 
 42 typedef struct {
 43     size_t                buffer_size;
 44     size_t                max_buffer_size;
 45 } ngx_http_mp4_conf_t;
 46 
 47 
 48 typedef struct {
 49     u_char                chunk[4];
 50     u_char                samples[4];
 51     u_char                id[4];
 52 } ngx_mp4_stsc_entry_t;
 53 
 54 
 55 typedef struct {
 56     uint32_t              timescale;
 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;
 62     uint32_t              chunks;
 63 
 64     ngx_uint_t            start_sample;
 65     ngx_uint_t            start_chunk;
 66     ngx_uint_t            chunk_samples;
 67     uint64_t              chunk_samples_size;
 68     off_t                 start_offset;
 69 
 70     size_t                tkhd_size;
 71     size_t                mdhd_size;
 72     size_t                hdlr_size;
 73     size_t                vmhd_size;
 74     size_t                smhd_size;
 75     size_t                dinf_size;
 76     size_t                size;
 77 
 78     ngx_chain_t           out[NGX_HTTP_MP4_LAST_ATOM + 1];
 79 
 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;
106 
107     ngx_mp4_stsc_entry_t  stsc_chunk_entry;
108 } ngx_http_mp4_trak_t;
109 
110 
111 typedef struct {
112     ngx_file_t            file;
113 
114     u_char               *buffer;
115     u_char               *buffer_start;
116     u_char               *buffer_pos;
117     u_char               *buffer_end;
118     size_t                buffer_size;
119 
120     off_t                 offset;
121     off_t                 end;
122     off_t                 content_length;
123     ngx_uint_t            start;
124     uint32_t              timescale;
125     ngx_http_request_t   *request;
126     ngx_array_t           trak;
127     ngx_http_mp4_trak_t   traks[2];
128 
129     size_t                ftyp_size;
130     size_t                moov_size;
131 
132     ngx_chain_t          *out;
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;
138 
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;
144 
145     u_char                moov_atom_header[8];
146     u_char                mdat_atom_header[16];
147 } ngx_http_mp4_file_t;
148 
149 
150 typedef struct {
151     char                 *name;
152     ngx_int_t           (*handler)(ngx_http_mp4_file_t *mp4,
153                                    uint64_t atom_data_size);
154 } ngx_http_mp4_atom_handler_t;
155 
156 
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
161 
162 
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
168 
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]) )
174 
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)
180 
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]) )
190 
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)
200 
201 #define ngx_mp4_last_trak(mp4)                                                \
202     &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
203 
204 
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,
216     off_t start_offset);
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);
286 
287 static ngx_command_t  ngx_http_mp4_commands[] = {
288 
289     { ngx_string("mp4"),
290       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
291       ngx_http_mp4,
292       0,
293       0,
294       NULL },
295 
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),
301       NULL },
302 
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),
308       NULL },
309 
310       ngx_null_command
311 };
312 
313 
314 static ngx_http_module_t  ngx_http_mp4_module_ctx = {
315     NULL,                          /* preconfiguration */
316     NULL,                          /* postconfiguration */
317 
318     NULL,                          /* create main configuration */
319     NULL,                          /* init main configuration */
320 
321     NULL,                          /* create server configuration */
322     NULL,                          /* merge server configuration */
323 
324     ngx_http_mp4_create_conf,      /* create location configuration */
325     ngx_http_mp4_merge_conf        /* merge location configuration */
326 };
327 
328 
329 ngx_module_t  ngx_http_mp4_module = {
330     NGX_MODULE_V1,
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
342 };
343 
344 
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 },
349     { NULL, NULL }
350 };
351 
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 },
356     { NULL, NULL }
357 };
358 
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 },
362     { NULL, NULL }
363 };
364 
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 },
369     { NULL, NULL }
370 };
371 
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 },
377     { NULL, NULL }
378 };
379 
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 },
389     { NULL, NULL }
390 };
391 
392 
393 static ngx_int_t
394 ngx_http_mp4_handler(ngx_http_request_t *r)
395 {
396     u_char                    *last;
397     size_t                     root;
398     ngx_int_t                  rc, start;
399     ngx_uint_t                 level;
400     ngx_str_t                  path, value;
401     ngx_log_t                 *log;
402     ngx_buf_t                 *b;
403     ngx_chain_t                out;
404     ngx_http_mp4_file_t       *mp4;
405     ngx_open_file_info_t       of;
406     ngx_http_core_loc_conf_t  *clcf;
407 
408     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
409         return NGX_HTTP_NOT_ALLOWED;
410     }
411 
412     if (r->uri.data[r->uri.len - 1] == '/') {
413         return NGX_DECLINED;
414     }
415 
416     rc = ngx_http_discard_request_body(r);
417 
418     if (rc != NGX_OK) {
419         return rc;
420     }
421 
422     last = ngx_http_map_uri_to_path(r, &path, &root, 0);
423     if (last == NULL) {
424         return NGX_HTTP_INTERNAL_SERVER_ERROR;
425     }
426 
427     log = r->connection->log;
428 
429     path.len = last - path.data;
430 
431     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
432                    "http mp4 filename: \"%V\"", &path);
433 
434     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
435 
436     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
437 
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;
444 
445     if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
446         return NGX_HTTP_INTERNAL_SERVER_ERROR;
447     }
448 
449     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
450         != NGX_OK)
451     {
452         switch (of.err) {
453 
454         case 0:
455             return NGX_HTTP_INTERNAL_SERVER_ERROR;
456 
457         case NGX_ENOENT:
458         case NGX_ENOTDIR:
459         case NGX_ENAMETOOLONG:
460 
461             level = NGX_LOG_ERR;
462             rc = NGX_HTTP_NOT_FOUND;
463             break;
464 
465         case NGX_EACCES:
466 #if (NGX_HAVE_OPENAT)
467         case NGX_EMLINK:
468         case NGX_ELOOP:
469 #endif
470 
471             level = NGX_LOG_ERR;
472             rc = NGX_HTTP_FORBIDDEN;
473             break;
474 
475         default:
476 
477             level = NGX_LOG_CRIT;
478             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
479             break;
480         }
481 
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);
485         }
486 
487         return rc;
488     }
489 
490     if (!of.is_file) {
491 
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);
495         }
496 
497         return NGX_DECLINED;
498     }
499 
500     r->root_tested = !r->error_page;
501     r->allow_ranges = 1;
502 
503     start = -1;
504     r->headers_out.content_length_n = of.size;
505     mp4 = NULL;
506     b = NULL;
507 
508     if (r->args.len) {
509 
510         if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
511 
512             /*
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.
516              */
517 
518             ngx_set_errno(0);
519             start = (int) (strtod((char *) value.data, NULL) * 1000);
520 
521             if (ngx_errno == 0 && start >= 0) {
522                 r->allow_ranges = 0;
523 
524                 mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
525                 if (mp4 == NULL) {
526                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
527                 }
528 
529                 mp4->file.fd = of.fd;
530                 mp4->file.name = path;
531                 mp4->file.log = r->connection->log;;
532                 mp4->end = of.size;
533                 mp4->start = (ngx_uint_t) start;
534                 mp4->request = r;
535 
536                 switch (ngx_http_mp4_process(mp4)) {
537 
538                 case NGX_DECLINED:
539                     if (mp4->buffer) {
540                         ngx_pfree(r->pool, mp4->buffer);
541                     }
542 
543                     ngx_pfree(r->pool, mp4);
544                     mp4 = NULL;
545 
546                     break;
547 
548                 case NGX_OK:
549                     r->headers_out.content_length_n = mp4->content_length;
550                     break;
551 
552                 default: /* NGX_ERROR */
553                     if (mp4->buffer) {
554                         ngx_pfree(r->pool, mp4->buffer);
555                     }
556 
557                     ngx_pfree(r->pool, mp4);
558 
559                     return NGX_HTTP_INTERNAL_SERVER_ERROR;
560                 }
561             }
562         }
563     }
564 
565     log->action = "sending mp4 to client";
566 
567     if (clcf->directio <= of.size) {
568 
569         /*
570          * DIRECTIO is set on transfer only
571          * to allow kernel to cache "moov" atom
572          */
573 
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);
577         }
578 
579         of.is_directio = 1;
580 
581         if (mp4) {
582             mp4->file.directio = 1;
583         }
584     }
585 
586     r->headers_out.status = NGX_HTTP_OK;
587     r->headers_out.last_modified_time = of.mtime;
588 
589     if (ngx_http_set_etag(r) != NGX_OK) {
590         return NGX_HTTP_INTERNAL_SERVER_ERROR;
591     }
592 
593     if (ngx_http_set_content_type(r) != NGX_OK) {
594         return NGX_HTTP_INTERNAL_SERVER_ERROR;
595     }
596 
597     if (mp4 == NULL) {
598         b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
599         if (b == NULL) {
600             return NGX_HTTP_INTERNAL_SERVER_ERROR;
601         }
602 
603         b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
604         if (b->file == NULL) {
605             return NGX_HTTP_INTERNAL_SERVER_ERROR;
606         }
607     }
608 
609     rc = ngx_http_send_header(r);
610 
611     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
612         return rc;
613     }
614 
615     if (mp4) {
616         return ngx_http_output_filter(r, mp4->out);
617     }
618 
619     b->file_pos = 0;
620     b->file_last = of.size;
621 
622     b->in_file = b->file_last ? 1 : 0;
623     b->last_buf = (r == r->main) ? 1 : 0;
624     b->last_in_chain = 1;
625 
626     b->file->fd = of.fd;
627     b->file->name = path;
628     b->file->log = log;
629     b->file->directio = of.is_directio;
630 
631     out.buf = b;
632     out.next = NULL;
633 
634     return ngx_http_output_filter(r, &out);
635 }
636 
637 
638 static ngx_int_t
639 ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
640 {
641     off_t                  start_offset, adjustment;
642     ngx_int_t              rc;
643     ngx_uint_t             i, j;
644     ngx_chain_t          **prev;
645     ngx_http_mp4_trak_t   *trak;
646     ngx_http_mp4_conf_t   *conf;
647 
648     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
649                    "mp4 start:%ui", mp4->start);
650 
651     conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
652 
653     mp4->buffer_size = conf->buffer_size;
654 
655     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);
656     if (rc != NGX_OK) {
657         return rc;
658     }
659 
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);
664         return NGX_ERROR;
665     }
666 
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);
671         return NGX_ERROR;
672     }
673 
674     prev = &mp4->out;
675 
676     if (mp4->ftyp_atom.buf) {
677         *prev = &mp4->ftyp_atom;
678         prev = &mp4->ftyp_atom.next;
679     }
680 
681     *prev = &mp4->moov_atom;
682     prev = &mp4->moov_atom.next;
683 
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;
688     }
689 
690     start_offset = mp4->end;
691     trak = mp4->trak.elts;
692 
693     for (i = 0; i < mp4->trak.nelts; i++) {
694 
695         if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
696             return NGX_ERROR;
697         }
698 
699         if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
700             return NGX_ERROR;
701         }
702 
703         ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);
704 
705         if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
706             return NGX_ERROR;
707         }
708 
709         if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
710             return NGX_ERROR;
711         }
712 
713         if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
714             if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
715                 return NGX_ERROR;
716             }
717 
718         } else {
719             if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
720                 return NGX_ERROR;
721             }
722         }
723 
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]);
731 
732         mp4->moov_size += trak[i].size;
733 
734         if (start_offset > trak[i].start_offset) {
735             start_offset = trak[i].start_offset;
736         }
737 
738         *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
739         prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
740 
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;
745             }
746         }
747     }
748 
749     mp4->moov_size += 8;
750 
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;
754 
755     *prev = &mp4->mdat_atom;
756 
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);
761         return NGX_ERROR;
762     }
763 
764     adjustment = mp4->ftyp_size + mp4->moov_size
765                  + ngx_http_mp4_update_mdat_atom(mp4, start_offset)
766                  - start_offset;
767 
768     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
769                    "mp4 adjustment:%O", adjustment);
770 
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);
774         } else {
775             ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
776         }
777     }
778 
779     return NGX_OK;
780 }
781 
782 
783 typedef struct {
784     u_char    size[4];
785     u_char    name[4];
786 } ngx_mp4_atom_header_t;
787 
788 typedef struct {
789     u_char    size[4];
790     u_char    name[4];
791     u_char    size64[8];
792 } ngx_mp4_atom_header64_t;
793 
794 
795 static ngx_int_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)
798 {
799     off_t        end;
800     size_t       atom_header_size;
801     u_char      *atom_header, *atom_name;
802     uint64_t     atom_size;
803     ngx_int_t    rc;
804     ngx_uint_t   n;
805 
806     end = mp4->offset + atom_data_size;
807 
808     while (mp4->offset < end) {
809 
810         if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {
811             return NGX_ERROR;
812         }
813 
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);
817 
818         if (atom_size == 0) {
819             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
820                            "mp4 atom end");
821             return NGX_OK;
822         }
823 
824         if (atom_size < sizeof(ngx_mp4_atom_header_t)) {
825 
826             if (atom_size == 1) {
827 
828                 if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))
829                     != NGX_OK)
830                 {
831                     return NGX_ERROR;
832                 }
833 
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);
838 
839             } else {
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);
843                 return NGX_ERROR;
844             }
845         }
846 
847         if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {
848             return NGX_ERROR;
849         }
850 
851         atom_header = mp4->buffer_pos;
852         atom_name = atom_header + sizeof(uint32_t);
853 
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);
857 
858         if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)
859             || mp4->offset + (off_t) atom_size > end)
860         {
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);
864             return NGX_ERROR;
865         }
866 
867         for (n = 0; atom[n].name; n++) {
868 
869             if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {
870 
871                 ngx_mp4_atom_next(mp4, atom_header_size);
872 
873                 rc = atom[n].handler(mp4, atom_size - atom_header_size);
874                 if (rc != NGX_OK) {
875                     return rc;
876                 }
877 
878                 goto next;
879             }
880         }
881 
882         ngx_mp4_atom_next(mp4, atom_size);
883 
884     next:
885         continue;
886     }
887 
888     return NGX_OK;
889 }
890 
891 
892 static ngx_int_t
893 ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)
894 {
895     ssize_t  n;
896 
897     if (mp4->buffer_pos + size <= mp4->buffer_end) {
898         return NGX_OK;
899     }
900 
901     if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {
902         mp4->buffer_size = (size_t) (mp4->end - mp4->offset);
903     }
904 
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);
908         return NGX_ERROR;
909     }
910 
911     if (mp4->buffer == NULL) {
912         mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
913         if (mp4->buffer == NULL) {
914             return NGX_ERROR;
915         }
916 
917         mp4->buffer_start = mp4->buffer;
918     }
919 
920     n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
921                       mp4->offset);
922 
923     if (n == NGX_ERROR) {
924         return NGX_ERROR;
925     }
926 
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);
931         return NGX_ERROR;
932     }
933 
934     mp4->buffer_pos = mp4->buffer_start;
935     mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;
936 
937     return NGX_OK;
938 }
939 
940 
941 static ngx_int_t
942 ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
943 {
944     u_char     *ftyp_atom;
945     size_t      atom_size;
946     ngx_buf_t  *atom;
947 
948     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
949 
950     if (atom_data_size > 1024
951         || ngx_mp4_atom_data(mp4) + atom_data_size > mp4->buffer_end)
952     {
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);
956         return NGX_ERROR;
957     }
958 
959     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
960 
961     ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);
962     if (ftyp_atom == NULL) {
963         return NGX_ERROR;
964     }
965 
966     ngx_mp4_set_32value(ftyp_atom, atom_size);
967     ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');
968 
969     /*
970      * only moov atom content is guaranteed to be in mp4->buffer
971      * during sending response, so ftyp atom content should be copied
972      */
973     ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),
974                ngx_mp4_atom_data(mp4), (size_t) atom_data_size);
975 
976     atom = &mp4->ftyp_atom_buf;
977     atom->temporary = 1;
978     atom->pos = ftyp_atom;
979     atom->last = ftyp_atom + atom_size;
980 
981     mp4->ftyp_atom.buf = atom;
982     mp4->ftyp_size = atom_size;
983     mp4->content_length = atom_size;
984 
985     ngx_mp4_atom_next(mp4, atom_data_size);
986 
987     return NGX_OK;
988 }
989 
990 
991 /*
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.
994  */
995 #define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS  (4 * 1024)
996 
997 static ngx_int_t
998 ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
999 {
1000     ngx_int_t             rc;
1001     ngx_uint_t            no_mdat;
1002     ngx_buf_t            *atom;
1003     ngx_http_mp4_conf_t  *conf;
1004 
1005     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
1006 
1007     no_mdat = (mp4->mdat_atom.buf == NULL);
1008 
1009     if (no_mdat && mp4->start == 0) {
1010         /*
1011          * send original file if moov atom resides before
1012          * mdat atom and client requests integral file
1013          */
1014         return NGX_DECLINED;
1015     }
1016 
1017     conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
1018 
1019     if (atom_data_size > mp4->buffer_size) {
1020 
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);
1026             return NGX_ERROR;
1027         }
1028 
1029         ngx_pfree(mp4->request->pool, mp4->buffer);
1030         mp4->buffer = NULL;
1031         mp4->buffer_pos = NULL;
1032         mp4->buffer_end = NULL;
1033 
1034         mp4->buffer_size = (size_t) atom_data_size
1035                          + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;
1036     }
1037 
1038     if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {
1039         return NGX_ERROR;
1040     }
1041 
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;
1046 
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;
1051 
1052     mp4->moov_atom.buf = &mp4->moov_atom_buf;
1053 
1054     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);
1055 
1056     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");
1057 
1058     if (no_mdat) {
1059         mp4->buffer_start = mp4->buffer_pos;
1060         mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;
1061 
1062         if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {
1063             mp4->buffer = NULL;
1064             mp4->buffer_pos = NULL;
1065             mp4->buffer_end = NULL;
1066         }
1067 
1068     } else {
1069         /* skip atoms after moov atom */
1070         mp4->offset = mp4->end;
1071     }
1072 
1073     return rc;
1074 }
1075 
1076 
1077 static ngx_int_t
1078 ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1079 {
1080     ngx_buf_t  *data;
1081 
1082     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");
1083 
1084     data = &mp4->mdat_data_buf;
1085     data->file = &mp4->file;
1086     data->in_file = 1;
1087     data->last_buf = 1;
1088     data->last_in_chain = 1;
1089     data->file_last = mp4->offset + atom_data_size;
1090 
1091     mp4->mdat_atom.buf = &mp4->mdat_atom_buf;
1092     mp4->mdat_atom.next = &mp4->mdat_data;
1093     mp4->mdat_data.buf = data;
1094 
1095     if (mp4->trak.nelts) {
1096         /* skip atoms after mdat atom */
1097         mp4->offset = mp4->end;
1098 
1099     } else {
1100         ngx_mp4_atom_next(mp4, atom_data_size);
1101     }
1102 
1103     return NGX_OK;
1104 }
1105 
1106 
1107 static size_t
1108 ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset)
1109 {
1110     off_t       atom_data_size;
1111     u_char     *atom_header;
1112     uint32_t    atom_header_size;
1113     uint64_t    atom_size;
1114     ngx_buf_t  *atom;
1115 
1116     atom_data_size = mp4->mdat_data.buf->file_last - start_offset;
1117     mp4->mdat_data.buf->file_pos = start_offset;
1118 
1119     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1120                    "mdat new offset @%O:%O", start_offset, atom_data_size);
1121 
1122     atom_header = mp4->mdat_atom_header;
1123 
1124     if ((uint64_t) atom_data_size > 0xffffffff) {
1125         atom_size = 1;
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);
1129     } else {
1130         atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;
1131         atom_header_size = sizeof(ngx_mp4_atom_header_t);
1132     }
1133 
1134     mp4->content_length += atom_header_size + atom_data_size;
1135 
1136     ngx_mp4_set_32value(atom_header, atom_size);
1137     ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');
1138 
1139     atom = &mp4->mdat_atom_buf;
1140     atom->temporary = 1;
1141     atom->pos = atom_header;
1142     atom->last = atom_header + atom_header_size;
1143 
1144     return atom_header_size;
1145 }
1146 
1147 
1148 typedef struct {
1149     u_char    size[4];
1150     u_char    name[4];
1151     u_char    version[1];
1152     u_char    flags[3];
1153     u_char    creation_time[4];
1154     u_char    modification_time[4];
1155     u_char    timescale[4];
1156     u_char    duration[4];
1157     u_char    rate[4];
1158     u_char    volume[2];
1159     u_char    reserved[10];
1160     u_char    matrix[36];
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;
1169 
1170 typedef struct {
1171     u_char    size[4];
1172     u_char    name[4];
1173     u_char    version[1];
1174     u_char    flags[3];
1175     u_char    creation_time[8];
1176     u_char    modification_time[8];
1177     u_char    timescale[4];
1178     u_char    duration[8];
1179     u_char    rate[4];
1180     u_char    volume[2];
1181     u_char    reserved[10];
1182     u_char    matrix[36];
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;
1191 
1192 
1193 static ngx_int_t
1194 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1195 {
1196     u_char                 *atom_header;
1197     size_t                  atom_size;
1198     uint32_t                timescale;
1199     uint64_t                duration;
1200     ngx_buf_t              *atom;
1201     ngx_mp4_mvhd_atom_t    *mvhd_atom;
1202     ngx_mp4_mvhd64_atom_t  *mvhd64_atom;
1203 
1204     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
1205 
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');
1210 
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);
1214         return NGX_ERROR;
1215     }
1216 
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);
1221 
1222     } else {
1223         /* version 1: 64-bit duration */
1224 
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);
1229             return NGX_ERROR;
1230         }
1231 
1232         timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);
1233         duration = ngx_mp4_get_64value(mvhd64_atom->duration);
1234     }
1235 
1236     mp4->timescale = timescale;
1237 
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);
1241 
1242     duration -= (uint64_t) mp4->start * timescale / 1000;
1243 
1244     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1245                    "mvhd new duration:%uL, time:%.3fs",
1246                    duration, (double) duration / timescale);
1247 
1248     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1249     ngx_mp4_set_32value(mvhd_atom->size, atom_size);
1250 
1251     if (mvhd_atom->version[0] == 0) {
1252         ngx_mp4_set_32value(mvhd_atom->duration, duration);
1253 
1254     } else {
1255         ngx_mp4_set_64value(mvhd64_atom->duration, duration);
1256     }
1257 
1258     atom = &mp4->mvhd_atom_buf;
1259     atom->temporary = 1;
1260     atom->pos = atom_header;
1261     atom->last = atom_header + atom_size;
1262 
1263     mp4->mvhd_atom.buf = atom;
1264 
1265     ngx_mp4_atom_next(mp4, atom_data_size);
1266 
1267     return NGX_OK;
1268 }
1269 
1270 
1271 static ngx_int_t
1272 ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1273 {
1274     u_char               *atom_header, *atom_end;
1275     off_t                 atom_file_end;
1276     ngx_int_t             rc;
1277     ngx_buf_t            *atom;
1278     ngx_http_mp4_trak_t  *trak;
1279 
1280     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");
1281 
1282     trak = ngx_array_push(&mp4->trak);
1283     if (trak == NULL) {
1284         return NGX_ERROR;
1285     }
1286 
1287     ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
1288 
1289     atom_header = ngx_mp4_atom_header(mp4);
1290     ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');
1291 
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);
1296 
1297     trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
1298 
1299     atom_end = mp4->buffer_pos + atom_data_size;
1300     atom_file_end = mp4->offset + atom_data_size;
1301 
1302     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
1303 
1304     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1305                    "mp4 trak atom: %i", rc);
1306 
1307     if (rc == NGX_DECLINED) {
1308         /* skip this trak */
1309         ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
1310         mp4->trak.nelts--;
1311         mp4->buffer_pos = atom_end;
1312         mp4->offset = atom_file_end;
1313         return NGX_OK;
1314     }
1315 
1316     return rc;
1317 }
1318 
1319 
1320 static void
1321 ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
1322     ngx_http_mp4_trak_t *trak)
1323 {
1324     ngx_buf_t  *atom;
1325 
1326     trak->size += sizeof(ngx_mp4_atom_header_t);
1327     atom = &trak->trak_atom_buf;
1328     ngx_mp4_set_32value(atom->pos, trak->size);
1329 }
1330 
1331 
1332 static ngx_int_t
1333 ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1334 {
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);
1338 
1339     return NGX_ERROR;
1340 }
1341 
1342 
1343 typedef struct {
1344     u_char    size[4];
1345     u_char    name[4];
1346     u_char    version[1];
1347     u_char    flags[3];
1348     u_char    creation_time[4];
1349     u_char    modification_time[4];
1350     u_char    track_id[4];
1351     u_char    reserved1[4];
1352     u_char    duration[4];
1353     u_char    reserved2[8];
1354     u_char    layer[2];
1355     u_char    group[2];
1356     u_char    volume[2];
1357     u_char    reverved3[2];
1358     u_char    matrix[36];
1359     u_char    width[4];
1360     u_char    heigth[4];
1361 } ngx_mp4_tkhd_atom_t;
1362 
1363 typedef struct {
1364     u_char    size[4];
1365     u_char    name[4];
1366     u_char    version[1];
1367     u_char    flags[3];
1368     u_char    creation_time[8];
1369     u_char    modification_time[8];
1370     u_char    track_id[4];
1371     u_char    reserved1[4];
1372     u_char    duration[8];
1373     u_char    reserved2[8];
1374     u_char    layer[2];
1375     u_char    group[2];
1376     u_char    volume[2];
1377     u_char    reverved3[2];
1378     u_char    matrix[36];
1379     u_char    width[4];
1380     u_char    heigth[4];
1381 } ngx_mp4_tkhd64_atom_t;
1382 
1383 
1384 static ngx_int_t
1385 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1386 {
1387     u_char                 *atom_header;
1388     size_t                  atom_size;
1389     uint64_t                duration;
1390     ngx_buf_t              *atom;
1391     ngx_http_mp4_trak_t    *trak;
1392     ngx_mp4_tkhd_atom_t    *tkhd_atom;
1393     ngx_mp4_tkhd64_atom_t  *tkhd64_atom;
1394 
1395     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");
1396 
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');
1401 
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);
1405         return NGX_ERROR;
1406     }
1407 
1408     if (tkhd_atom->version[0] == 0) {
1409         /* version 0: 32-bit duration */
1410         duration = ngx_mp4_get_32value(tkhd_atom->duration);
1411 
1412     } else {
1413         /* version 1: 64-bit duration */
1414 
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);
1419             return NGX_ERROR;
1420         }
1421 
1422         duration = ngx_mp4_get_64value(tkhd64_atom->duration);
1423     }
1424 
1425     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1426                    "tkhd duration:%uL, time:%.3fs",
1427                    duration, (double) duration / mp4->timescale);
1428 
1429     duration -= (uint64_t) mp4->start * mp4->timescale / 1000;
1430 
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);
1434 
1435     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1436 
1437     trak = ngx_mp4_last_trak(mp4);
1438     trak->tkhd_size = atom_size;
1439 
1440     ngx_mp4_set_32value(tkhd_atom->size, atom_size);
1441 
1442     if (tkhd_atom->version[0] == 0) {
1443         ngx_mp4_set_32value(tkhd_atom->duration, duration);
1444 
1445     } else {
1446         ngx_mp4_set_64value(tkhd64_atom->duration, duration);
1447     }
1448 
1449     atom = &trak->tkhd_atom_buf;
1450     atom->temporary = 1;
1451     atom->pos = atom_header;
1452     atom->last = atom_header + atom_size;
1453 
1454     trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;
1455 
1456     ngx_mp4_atom_next(mp4, atom_data_size);
1457 
1458     return NGX_OK;
1459 }
1460 
1461 
1462 static ngx_int_t
1463 ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1464 {
1465     u_char               *atom_header;
1466     ngx_buf_t            *atom;
1467     ngx_http_mp4_trak_t  *trak;
1468 
1469     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");
1470 
1471     atom_header = ngx_mp4_atom_header(mp4);
1472     ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');
1473 
1474     trak = ngx_mp4_last_trak(mp4);
1475 
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);
1480 
1481     trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;
1482 
1483     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);
1484 }
1485 
1486 
1487 static void
1488 ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
1489     ngx_http_mp4_trak_t *trak)
1490 {
1491     ngx_buf_t  *atom;
1492 
1493     trak->size += sizeof(ngx_mp4_atom_header_t);
1494     atom = &trak->mdia_atom_buf;
1495     ngx_mp4_set_32value(atom->pos, trak->size);
1496 }
1497 
1498 
1499 typedef struct {
1500     u_char    size[4];
1501     u_char    name[4];
1502     u_char    version[1];
1503     u_char    flags[3];
1504     u_char    creation_time[4];
1505     u_char    modification_time[4];
1506     u_char    timescale[4];
1507     u_char    duration[4];
1508     u_char    language[2];
1509     u_char    quality[2];
1510 } ngx_mp4_mdhd_atom_t;
1511 
1512 typedef struct {
1513     u_char    size[4];
1514     u_char    name[4];
1515     u_char    version[1];
1516     u_char    flags[3];
1517     u_char    creation_time[8];
1518     u_char    modification_time[8];
1519     u_char    timescale[4];
1520     u_char    duration[8];
1521     u_char    language[2];
1522     u_char    quality[2];
1523 } ngx_mp4_mdhd64_atom_t;
1524 
1525 
1526 static ngx_int_t
1527 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1528 {
1529     u_char                 *atom_header;
1530     size_t                  atom_size;
1531     uint32_t                timescale;
1532     uint64_t                duration;
1533     ngx_buf_t              *atom;
1534     ngx_http_mp4_trak_t    *trak;
1535     ngx_mp4_mdhd_atom_t    *mdhd_atom;
1536     ngx_mp4_mdhd64_atom_t  *mdhd64_atom;
1537 
1538     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");
1539 
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');
1544 
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);
1548         return NGX_ERROR;
1549     }
1550 
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);
1555 
1556     } else {
1557         /* version 1: 64-bit duration and 32-bit timescale */
1558 
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);
1563             return NGX_ERROR;
1564         }
1565 
1566         timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);
1567         duration = ngx_mp4_get_64value(mdhd64_atom->duration);
1568     }
1569 
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);
1573 
1574     duration -= (uint64_t) mp4->start * timescale / 1000;
1575 
1576     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1577                    "mdhd new duration:%uL, time:%.3fs",
1578                    duration, (double) duration / timescale);
1579 
1580     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1581 
1582     trak = ngx_mp4_last_trak(mp4);
1583     trak->mdhd_size = atom_size;
1584     trak->timescale = timescale;
1585 
1586     ngx_mp4_set_32value(mdhd_atom->size, atom_size);
1587 
1588     if (mdhd_atom->version[0] == 0) {
1589         ngx_mp4_set_32value(mdhd_atom->duration, duration);
1590 
1591     } else {
1592         ngx_mp4_set_64value(mdhd64_atom->duration, duration);
1593     }
1594 
1595     atom = &trak->mdhd_atom_buf;
1596     atom->temporary = 1;
1597     atom->pos = atom_header;
1598     atom->last = atom_header + atom_size;
1599 
1600     trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;
1601 
1602     ngx_mp4_atom_next(mp4, atom_data_size);
1603 
1604     return NGX_OK;
1605 }
1606 
1607 
1608 static ngx_int_t
1609 ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1610 {
1611     u_char              *atom_header;
1612     size_t               atom_size;
1613     ngx_buf_t            *atom;
1614     ngx_http_mp4_trak_t  *trak;
1615 
1616     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");
1617 
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');
1622 
1623     trak = ngx_mp4_last_trak(mp4);
1624 
1625     atom = &trak->hdlr_atom_buf;
1626     atom->temporary = 1;
1627     atom->pos = atom_header;
1628     atom->last = atom_header + atom_size;
1629 
1630     trak->hdlr_size = atom_size;
1631     trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;
1632 
1633     ngx_mp4_atom_next(mp4, atom_data_size);
1634 
1635     return NGX_OK;
1636 }
1637 
1638 
1639 static ngx_int_t
1640 ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1641 {
1642     u_char               *atom_header;
1643     ngx_buf_t            *atom;
1644     ngx_http_mp4_trak_t  *trak;
1645 
1646     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");
1647 
1648     atom_header = ngx_mp4_atom_header(mp4);
1649     ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');
1650 
1651     trak = ngx_mp4_last_trak(mp4);
1652 
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);
1657 
1658     trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;
1659 
1660     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);
1661 }
1662 
1663 
1664 static void
1665 ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
1666     ngx_http_mp4_trak_t *trak)
1667 {
1668     ngx_buf_t  *atom;
1669 
1670     trak->size += sizeof(ngx_mp4_atom_header_t)
1671                + trak->vmhd_size
1672                + trak->smhd_size
1673                + trak->dinf_size;
1674     atom = &trak->minf_atom_buf;
1675     ngx_mp4_set_32value(atom->pos, trak->size);
1676 }
1677 
1678 
1679 static ngx_int_t
1680 ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1681 {
1682     u_char              *atom_header;
1683     size_t               atom_size;
1684     ngx_buf_t            *atom;
1685     ngx_http_mp4_trak_t  *trak;
1686 
1687     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");
1688 
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');
1693 
1694     trak = ngx_mp4_last_trak(mp4);
1695 
1696     atom = &trak->vmhd_atom_buf;
1697     atom->temporary = 1;
1698     atom->pos = atom_header;
1699     atom->last = atom_header + atom_size;
1700 
1701     trak->vmhd_size += atom_size;
1702     trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;
1703 
1704     ngx_mp4_atom_next(mp4, atom_data_size);
1705 
1706     return NGX_OK;
1707 }
1708 
1709 
1710 static ngx_int_t
1711 ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1712 {
1713     u_char              *atom_header;
1714     size_t               atom_size;
1715     ngx_buf_t            *atom;
1716     ngx_http_mp4_trak_t  *trak;
1717 
1718     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");
1719 
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');
1724 
1725     trak = ngx_mp4_last_trak(mp4);
1726 
1727     atom = &trak->smhd_atom_buf;
1728     atom->temporary = 1;
1729     atom->pos = atom_header;
1730     atom->last = atom_header + atom_size;
1731 
1732     trak->vmhd_size += atom_size;
1733     trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;
1734 
1735     ngx_mp4_atom_next(mp4, atom_data_size);
1736 
1737     return NGX_OK;
1738 }
1739 
1740 
1741 static ngx_int_t
1742 ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1743 {
1744     u_char              *atom_header;
1745     size_t               atom_size;
1746     ngx_buf_t            *atom;
1747     ngx_http_mp4_trak_t  *trak;
1748 
1749     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");
1750 
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');
1755 
1756     trak = ngx_mp4_last_trak(mp4);
1757 
1758     atom = &trak->dinf_atom_buf;
1759     atom->temporary = 1;
1760     atom->pos = atom_header;
1761     atom->last = atom_header + atom_size;
1762 
1763     trak->dinf_size += atom_size;
1764     trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;
1765 
1766     ngx_mp4_atom_next(mp4, atom_data_size);
1767 
1768     return NGX_OK;
1769 }
1770 
1771 
1772 static ngx_int_t
1773 ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1774 {
1775     u_char               *atom_header;
1776     ngx_buf_t            *atom;
1777     ngx_http_mp4_trak_t  *trak;
1778 
1779     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");
1780 
1781     atom_header = ngx_mp4_atom_header(mp4);
1782     ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');
1783 
1784     trak = ngx_mp4_last_trak(mp4);
1785 
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);
1790 
1791     trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;
1792 
1793     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);
1794 }
1795 
1796 
1797 static void
1798 ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
1799     ngx_http_mp4_trak_t *trak)
1800 {
1801     ngx_buf_t  *atom;
1802 
1803     trak->size += sizeof(ngx_mp4_atom_header_t);
1804     atom = &trak->stbl_atom_buf;
1805     ngx_mp4_set_32value(atom->pos, trak->size);
1806 }
1807 
1808 
1809 typedef struct {
1810     u_char    size[4];
1811     u_char    name[4];
1812     u_char    version[1];
1813     u_char    flags[3];
1814     u_char    entries[4];
1815 
1816     u_char    media_size[4];
1817     u_char    media_name[4];
1818 } ngx_mp4_stsd_atom_t;
1819 
1820 
1821 static ngx_int_t
1822 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1823 {
1824     u_char               *atom_header, *atom_table;
1825     size_t                atom_size;
1826     ngx_buf_t            *atom;
1827     ngx_mp4_stsd_atom_t  *stsd_atom;
1828     ngx_http_mp4_trak_t  *trak;
1829 
1830     /* sample description atom */
1831 
1832     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");
1833 
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');
1840 
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);
1844         return NGX_ERROR;
1845     }
1846 
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);
1851 
1852     trak = ngx_mp4_last_trak(mp4);
1853 
1854     atom = &trak->stsd_atom_buf;
1855     atom->temporary = 1;
1856     atom->pos = atom_header;
1857     atom->last = atom_table;
1858 
1859     trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;
1860     trak->size += atom_size;
1861 
1862     ngx_mp4_atom_next(mp4, atom_data_size);
1863 
1864     return NGX_OK;
1865 }
1866 
1867 
1868 typedef struct {
1869     u_char    size[4];
1870     u_char    name[4];
1871     u_char    version[1];
1872     u_char    flags[3];
1873     u_char    entries[4];
1874 } ngx_mp4_stts_atom_t;
1875 
1876 typedef struct {
1877     u_char    count[4];
1878     u_char    duration[4];
1879 } ngx_mp4_stts_entry_t;
1880 
1881 
1882 static ngx_int_t
1883 ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1884 {
1885     u_char               *atom_header, *atom_table, *atom_end;
1886     uint32_t              entries;
1887     ngx_buf_t            *atom, *data;
1888     ngx_mp4_stts_atom_t  *stts_atom;
1889     ngx_http_mp4_trak_t  *trak;
1890 
1891     /* time-to-sample atom */
1892 
1893     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");
1894 
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');
1898 
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);
1902         return NGX_ERROR;
1903     }
1904 
1905     entries = ngx_mp4_get_32value(stts_atom->entries);
1906 
1907     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1908                    "mp4 time-to-sample entries:%uD", entries);
1909 
1910     if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)
1911         + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)
1912     {
1913         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1914                       "\"%s\" mp4 stts atom too small", mp4->file.name.data);
1915         return NGX_ERROR;
1916     }
1917 
1918     atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);
1919     atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);
1920 
1921     trak = ngx_mp4_last_trak(mp4);
1922     trak->time_to_sample_entries = entries;
1923 
1924     atom = &trak->stts_atom_buf;
1925     atom->temporary = 1;
1926     atom->pos = atom_header;
1927     atom->last = atom_table;
1928 
1929     data = &trak->stts_data_buf;
1930     data->temporary = 1;
1931     data->pos = atom_table;
1932     data->last = atom_end;
1933 
1934     trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;
1935     trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;
1936 
1937     ngx_mp4_atom_next(mp4, atom_data_size);
1938 
1939     return NGX_OK;
1940 }
1941 
1942 
1943 static ngx_int_t
1944 ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
1945     ngx_http_mp4_trak_t *trak)
1946 {
1947     size_t                 atom_size;
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;
1954 
1955     /*
1956      * mdia.minf.stbl.stts updating requires trak->timescale
1957      * from mdia.mdhd atom which may reside after mdia.minf
1958      */
1959 
1960     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1961                    "mp4 stts atom update");
1962 
1963     data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
1964 
1965     if (data == NULL) {
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);
1969         return NGX_ERROR;
1970     }
1971 
1972     entries = trak->time_to_sample_entries;
1973     start_time = (uint64_t) mp4->start * trak->timescale / 1000;
1974 
1975     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1976                    "time-to-sample start_time:%uL", start_time);
1977 
1978     start_sample = 0;
1979     entry = (ngx_mp4_stts_entry_t *) data->pos;
1980     end = (ngx_mp4_stts_entry_t *) data->last;
1981 
1982     while (entry < end) {
1983         count = ngx_mp4_get_32value(entry->count);
1984         duration = ngx_mp4_get_32value(entry->duration);
1985 
1986         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1987                        "count:%uD, duration:%uD", count, duration);
1988 
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);
1993             goto found;
1994         }
1995 
1996         start_sample += count;
1997         start_time -= count * duration;
1998         entries--;
1999         entry++;
2000     }
2001 
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);
2005 
2006     return NGX_ERROR;
2007 
2008 found:
2009 
2010     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2011                    "start_sample:%ui, new count:%uD", start_sample, count);
2012 
2013     trak->start_sample = start_sample;
2014 
2015     data->pos = (u_char *) entry;
2016     atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
2017     trak->size += atom_size;
2018 
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);
2023 
2024     return NGX_OK;
2025 }
2026 
2027 
2028 typedef struct {
2029     u_char    size[4];
2030     u_char    name[4];
2031     u_char    version[1];
2032     u_char    flags[3];
2033     u_char    entries[4];
2034 } ngx_http_mp4_stss_atom_t;
2035 
2036 
2037 static ngx_int_t
2038 ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2039 {
2040     u_char                    *atom_header, *atom_table, *atom_end;
2041     uint32_t                   entries;
2042     ngx_buf_t                 *atom, *data;
2043     ngx_http_mp4_trak_t       *trak;
2044     ngx_http_mp4_stss_atom_t  *stss_atom;
2045 
2046     /* sync samples atom */
2047 
2048     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");
2049 
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');
2053 
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);
2057         return NGX_ERROR;
2058     }
2059 
2060     entries = ngx_mp4_get_32value(stss_atom->entries);
2061 
2062     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2063                    "sync sample entries:%uD", entries);
2064 
2065     trak = ngx_mp4_last_trak(mp4);
2066     trak->sync_samples_entries = entries;
2067 
2068     atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);
2069 
2070     atom = &trak->stss_atom_buf;
2071     atom->temporary = 1;
2072     atom->pos = atom_header;
2073     atom->last = atom_table;
2074 
2075     if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)
2076         + entries * sizeof(uint32_t) > atom_data_size)
2077     {
2078         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2079                       "\"%s\" mp4 stss atom too small", mp4->file.name.data);
2080         return NGX_ERROR;
2081     }
2082 
2083     atom_end = atom_table + entries * sizeof(uint32_t);
2084 
2085     data = &trak->stss_data_buf;
2086     data->temporary = 1;
2087     data->pos = atom_table;
2088     data->last = atom_end;
2089 
2090     trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;
2091     trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;
2092 
2093     ngx_mp4_atom_next(mp4, atom_data_size);
2094 
2095     return NGX_OK;
2096 }
2097 
2098 
2099 static ngx_int_t
2100 ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
2101     ngx_http_mp4_trak_t *trak)
2102 {
2103     size_t                     atom_size;
2104     uint32_t                   entries, sample, start_sample, *entry, *end;
2105     ngx_buf_t                 *atom, *data;
2106     ngx_http_mp4_stss_atom_t  *stss_atom;
2107 
2108     /*
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
2112      */
2113 
2114     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2115                    "mp4 stss atom update");
2116 
2117     data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
2118 
2119     if (data == NULL) {
2120         return NGX_OK;
2121     }
2122 
2123     /* sync samples starts from 1 */
2124     start_sample = trak->start_sample + 1;
2125     entries = trak->sync_samples_entries;
2126 
2127     entry = (uint32_t *) data->pos;
2128     end = (uint32_t *) data->last;
2129 
2130     while (entry < end) {
2131         sample = ngx_mp4_get_32value(entry);
2132 
2133         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2134                        "start:%uD, sync:%uD", start_sample, sample);
2135 
2136         if (sample >= start_sample) {
2137             goto found;
2138         }
2139 
2140         entries--;
2141         entry++;
2142     }
2143 
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);
2147 
2148     return NGX_ERROR;
2149 
2150 found:
2151 
2152     data->pos = (u_char *) entry;
2153 
2154     start_sample = trak->start_sample;
2155 
2156     while (entry < end) {
2157         sample = ngx_mp4_get_32value(entry);
2158         sample -= start_sample;
2159         ngx_mp4_set_32value(entry, sample);
2160         entry++;
2161     }
2162 
2163     atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
2164     trak->size += atom_size;
2165 
2166     atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
2167     stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
2168 
2169     ngx_mp4_set_32value(stss_atom->size, atom_size);
2170     ngx_mp4_set_32value(stss_atom->entries, entries);
2171 
2172     return NGX_OK;
2173 }
2174 
2175 
2176 typedef struct {
2177     u_char    size[4];
2178     u_char    name[4];
2179     u_char    version[1];
2180     u_char    flags[3];
2181     u_char    entries[4];
2182 } ngx_mp4_ctts_atom_t;
2183 
2184 typedef struct {
2185     u_char    count[4];
2186     u_char    offset[4];
2187 } ngx_mp4_ctts_entry_t;
2188 
2189 
2190 static ngx_int_t
2191 ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2192 {
2193     u_char               *atom_header, *atom_table, *atom_end;
2194     uint32_t              entries;
2195     ngx_buf_t            *atom, *data;
2196     ngx_mp4_ctts_atom_t  *ctts_atom;
2197     ngx_http_mp4_trak_t  *trak;
2198 
2199     /* composition offsets atom */
2200 
2201     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");
2202 
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');
2206 
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);
2210         return NGX_ERROR;
2211     }
2212 
2213     entries = ngx_mp4_get_32value(ctts_atom->entries);
2214 
2215     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2216                    "composition offset entries:%uD", entries);
2217 
2218     trak = ngx_mp4_last_trak(mp4);
2219     trak->composition_offset_entries = entries;
2220 
2221     atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);
2222 
2223     atom = &trak->ctts_atom_buf;
2224     atom->temporary = 1;
2225     atom->pos = atom_header;
2226     atom->last = atom_table;
2227 
2228     if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)
2229         + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)
2230     {
2231         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2232                       "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
2233         return NGX_ERROR;
2234     }
2235 
2236     atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);
2237 
2238     data = &trak->ctts_data_buf;
2239     data->temporary = 1;
2240     data->pos = atom_table;
2241     data->last = atom_end;
2242 
2243     trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;
2244     trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;
2245 
2246     ngx_mp4_atom_next(mp4, atom_data_size);
2247 
2248     return NGX_OK;
2249 }
2250 
2251 
2252 static void
2253 ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2254     ngx_http_mp4_trak_t *trak)
2255 {
2256     size_t                 atom_size;
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;
2261 
2262     /*
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
2266      */
2267 
2268     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2269                    "mp4 ctts atom update");
2270 
2271     data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
2272 
2273     if (data == NULL) {
2274         return;
2275     }
2276 
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;
2282 
2283     while (entry < end) {
2284         count = ngx_mp4_get_32value(entry->count);
2285 
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));
2289 
2290          if (start_sample <= count) {
2291              count -= (start_sample - 1);
2292              ngx_mp4_set_32value(entry->count, count);
2293              goto found;
2294          }
2295 
2296          start_sample -= count;
2297          entries--;
2298          entry++;
2299     }
2300 
2301     trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
2302     trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
2303 
2304     return;
2305 
2306 found:
2307 
2308     data->pos = (u_char *) entry;
2309     atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
2310     trak->size += atom_size;
2311 
2312     atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
2313     ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
2314 
2315     ngx_mp4_set_32value(ctts_atom->size, atom_size);
2316     ngx_mp4_set_32value(ctts_atom->entries, entries);
2317 
2318     return;
2319 }
2320 
2321 
2322 typedef struct {
2323     u_char    size[4];
2324     u_char    name[4];
2325     u_char    version[1];
2326     u_char    flags[3];
2327     u_char    entries[4];
2328 } ngx_mp4_stsc_atom_t;
2329 
2330 
2331 static ngx_int_t
2332 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2333 {
2334     u_char               *atom_header, *atom_table, *atom_end;
2335     uint32_t              entries;
2336     ngx_buf_t            *atom, *data;
2337     ngx_mp4_stsc_atom_t  *stsc_atom;
2338     ngx_http_mp4_trak_t  *trak;
2339 
2340     /* sample-to-chunk atom */
2341 
2342     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");
2343 
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');
2347 
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);
2351         return NGX_ERROR;
2352     }
2353 
2354     entries = ngx_mp4_get_32value(stsc_atom->entries);
2355 
2356     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2357                    "sample-to-chunk entries:%uD", entries);
2358 
2359     if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)
2360         + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)
2361     {
2362         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2363                       "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
2364         return NGX_ERROR;
2365     }
2366 
2367     atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);
2368     atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);
2369 
2370     trak = ngx_mp4_last_trak(mp4);
2371     trak->sample_to_chunk_entries = entries;
2372 
2373     atom = &trak->stsc_atom_buf;
2374     atom->temporary = 1;
2375     atom->pos = atom_header;
2376     atom->last = atom_table;
2377 
2378     data = &trak->stsc_data_buf;
2379     data->temporary = 1;
2380     data->pos = atom_table;
2381     data->last = atom_end;
2382 
2383     trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;
2384     trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;
2385 
2386     ngx_mp4_atom_next(mp4, atom_data_size);
2387 
2388     return NGX_OK;
2389 }
2390 
2391 
2392 static ngx_int_t
2393 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2394     ngx_http_mp4_trak_t *trak)
2395 {
2396     size_t                 atom_size;
2397     uint32_t               start_sample, entries, chunk, samples, id,
2398                            next_chunk, n;
2399     ngx_buf_t             *atom, *data, *buf;
2400     ngx_mp4_stsc_atom_t   *stsc_atom;
2401     ngx_mp4_stsc_entry_t  *entry, *first, *end;
2402 
2403     /*
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
2407      */
2408 
2409     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2410                    "mp4 stsc atom update");
2411 
2412     data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
2413 
2414     if (data == NULL) {
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);
2418         return NGX_ERROR;
2419     }
2420 
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);
2425         return NGX_ERROR;
2426     }
2427 
2428     start_sample = (uint32_t) trak->start_sample;
2429     entries = trak->sample_to_chunk_entries - 1;
2430 
2431     entry = (ngx_mp4_stsc_entry_t *) data->pos;
2432     end = (ngx_mp4_stsc_entry_t *) data->last;
2433 
2434     chunk = ngx_mp4_get_32value(entry->chunk);
2435     samples = ngx_mp4_get_32value(entry->samples);
2436     id = ngx_mp4_get_32value(entry->id);
2437     entry++;
2438 
2439     while (entry < end) {
2440 
2441         next_chunk = ngx_mp4_get_32value(entry->chunk);
2442 
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);
2447 
2448         n = (next_chunk - chunk) * samples;
2449 
2450         if (start_sample <= n) {
2451             goto found;
2452         }
2453 
2454         start_sample -= n;
2455 
2456         chunk = next_chunk;
2457         samples = ngx_mp4_get_32value(entry->samples);
2458         id = ngx_mp4_get_32value(entry->id);
2459         entries--;
2460         entry++;
2461     }
2462 
2463     next_chunk = trak->chunks;
2464 
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);
2468 
2469     n = (next_chunk - chunk) * samples;
2470 
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);
2475         return NGX_ERROR;
2476     }
2477 
2478 found:
2479 
2480     entries++;
2481     entry--;
2482 
2483     if (samples == 0) {
2484         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2485                       "zero number of samples in \"%s\"",
2486                       mp4->file.name.data);
2487         return NGX_ERROR;
2488     }
2489 
2490     trak->start_chunk = chunk - 1;
2491 
2492     trak->start_chunk += start_sample / samples;
2493     trak->chunk_samples = start_sample % samples;
2494 
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);
2498 
2499     data->pos = (u_char *) entry;
2500     atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos);
2501 
2502     ngx_mp4_set_32value(entry->chunk, 1);
2503 
2504     if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) {
2505 
2506         /* last chunk in the entry */
2507 
2508         ngx_mp4_set_32value(entry->samples, samples - trak->chunk_samples);
2509 
2510     } else if (trak->chunk_samples) {
2511 
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);
2516 
2517         buf = &trak->stsc_chunk_buf;
2518         buf->temporary = 1;
2519         buf->pos = (u_char *) first;
2520         buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2521 
2522         trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf;
2523 
2524         ngx_mp4_set_32value(entry->chunk, 2);
2525 
2526         entries++;
2527         atom_size += sizeof(ngx_mp4_stsc_entry_t);
2528     }
2529 
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);
2534     }
2535 
2536     trak->size += atom_size;
2537 
2538     atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
2539     stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
2540 
2541     ngx_mp4_set_32value(stsc_atom->size, atom_size);
2542     ngx_mp4_set_32value(stsc_atom->entries, entries);
2543 
2544     return NGX_OK;
2545 }
2546 
2547 
2548 typedef struct {
2549     u_char    size[4];
2550     u_char    name[4];
2551     u_char    version[1];
2552     u_char    flags[3];
2553     u_char    uniform_size[4];
2554     u_char    entries[4];
2555 } ngx_mp4_stsz_atom_t;
2556 
2557 
2558 static ngx_int_t
2559 ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2560 {
2561     u_char               *atom_header, *atom_table, *atom_end;
2562     size_t                atom_size;
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;
2567 
2568     /* sample sizes atom */
2569 
2570     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");
2571 
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');
2575 
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);
2579         return NGX_ERROR;
2580     }
2581 
2582     size = ngx_mp4_get_32value(stsz_atom->uniform_size);
2583     entries = ngx_mp4_get_32value(stsz_atom->entries);
2584 
2585     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2586                    "sample uniform size:%uD, entries:%uD", size, entries);
2587 
2588     trak = ngx_mp4_last_trak(mp4);
2589     trak->sample_sizes_entries = entries;
2590 
2591     atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);
2592 
2593     atom = &trak->stsz_atom_buf;
2594     atom->temporary = 1;
2595     atom->pos = atom_header;
2596     atom->last = atom_table;
2597 
2598     trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;
2599 
2600     if (size == 0) {
2601         if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)
2602             + entries * sizeof(uint32_t) > atom_data_size)
2603         {
2604             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2605                           "\"%s\" mp4 stsz atom too small",
2606                           mp4->file.name.data);
2607             return NGX_ERROR;
2608         }
2609 
2610         atom_end = atom_table + entries * sizeof(uint32_t);
2611 
2612         data = &trak->stsz_data_buf;
2613         data->temporary = 1;
2614         data->pos = atom_table;
2615         data->last = atom_end;
2616 
2617         trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
2618 
2619     } else {
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;
2625     }
2626 
2627     ngx_mp4_atom_next(mp4, atom_data_size);
2628 
2629     return NGX_OK;
2630 }
2631 
2632 
2633 static ngx_int_t
2634 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2635     ngx_http_mp4_trak_t *trak)
2636 {
2637     size_t                atom_size;
2638     uint32_t             *pos, *end;
2639     ngx_buf_t            *atom, *data;
2640     ngx_mp4_stsz_atom_t  *stsz_atom;
2641 
2642     /*
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
2646      */
2647 
2648     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2649                    "mp4 stsz atom update");
2650 
2651     data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
2652 
2653     if (data) {
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);
2658             return NGX_ERROR;
2659         }
2660 
2661         data->pos += trak->start_sample * sizeof(uint32_t);
2662         end = (uint32_t *) data->pos;
2663 
2664         for (pos = end - trak->chunk_samples; pos < end; pos++) {
2665             trak->chunk_samples_size += ngx_mp4_get_32value(pos);
2666         }
2667 
2668         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2669                        "chunk samples sizes:%uL", trak->chunk_samples_size);
2670 
2671         atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
2672         trak->size += atom_size;
2673 
2674         atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
2675         stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
2676 
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);
2680     }
2681 
2682     return NGX_OK;
2683 }
2684 
2685 
2686 typedef struct {
2687     u_char    size[4];
2688     u_char    name[4];
2689     u_char    version[1];
2690     u_char    flags[3];
2691     u_char    entries[4];
2692 } ngx_mp4_stco_atom_t;
2693 
2694 
2695 static ngx_int_t
2696 ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2697 {
2698     u_char               *atom_header, *atom_table, *atom_end;
2699     uint32_t              entries;
2700     ngx_buf_t            *atom, *data;
2701     ngx_mp4_stco_atom_t  *stco_atom;
2702     ngx_http_mp4_trak_t  *trak;
2703 
2704     /* chunk offsets atom */
2705 
2706     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");
2707 
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');
2711 
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);
2715         return NGX_ERROR;
2716     }
2717 
2718     entries = ngx_mp4_get_32value(stco_atom->entries);
2719 
2720     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
2721 
2722     if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)
2723         + entries * sizeof(uint32_t) > atom_data_size)
2724     {
2725         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2726                       "\"%s\" mp4 stco atom too small", mp4->file.name.data);
2727         return NGX_ERROR;
2728     }
2729 
2730     atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);
2731     atom_end = atom_table + entries * sizeof(uint32_t);
2732 
2733     trak = ngx_mp4_last_trak(mp4);
2734     trak->chunks = entries;
2735 
2736     atom = &trak->stco_atom_buf;
2737     atom->temporary = 1;
2738     atom->pos = atom_header;
2739     atom->last = atom_table;
2740 
2741     data = &trak->stco_data_buf;
2742     data->temporary = 1;
2743     data->pos = atom_table;
2744     data->last = atom_end;
2745 
2746     trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
2747     trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;
2748 
2749     ngx_mp4_atom_next(mp4, atom_data_size);
2750 
2751     return NGX_OK;
2752 }
2753 
2754 
2755 static ngx_int_t
2756 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
2757     ngx_http_mp4_trak_t *trak)
2758 {
2759     size_t                atom_size;
2760     ngx_buf_t            *atom, *data;
2761     ngx_mp4_stco_atom_t  *stco_atom;
2762 
2763     /*
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
2767      */
2768 
2769     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2770                    "mp4 stco atom update");
2771 
2772     data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
2773 
2774     if (data == NULL) {
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);
2778         return NGX_ERROR;
2779     }
2780 
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);
2785         return NGX_ERROR;
2786     }
2787 
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;
2791 
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);
2795 
2796     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2797                    "start chunk offset:%uD", trak->start_offset);
2798 
2799     atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
2800     stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
2801 
2802     ngx_mp4_set_32value(stco_atom->size, atom_size);
2803     ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk);
2804 
2805     return NGX_OK;
2806 }
2807 
2808 
2809 static void
2810 ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
2811     ngx_http_mp4_trak_t *trak, int32_t adjustment)
2812 {
2813     uint32_t    offset, *entry, *end;
2814     ngx_buf_t  *data;
2815 
2816     /*
2817      * moov.trak.mdia.minf.stbl.stco adjustment requires
2818      * minimal start offset of all traks and new moov atom size
2819      */
2820 
2821     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2822                    "mp4 stco atom adjustment");
2823 
2824     data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
2825     entry = (uint32_t *) data->pos;
2826     end = (uint32_t *) data->last;
2827 
2828     while (entry < end) {
2829         offset = ngx_mp4_get_32value(entry);
2830         offset += adjustment;
2831         ngx_mp4_set_32value(entry, offset);
2832         entry++;
2833     }
2834 }
2835 
2836 
2837 typedef struct {
2838     u_char    size[4];
2839     u_char    name[4];
2840     u_char    version[1];
2841     u_char    flags[3];
2842     u_char    entries[4];
2843 } ngx_mp4_co64_atom_t;
2844 
2845 
2846 static ngx_int_t
2847 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2848 {
2849     u_char               *atom_header, *atom_table, *atom_end;
2850     uint32_t              entries;
2851     ngx_buf_t            *atom, *data;
2852     ngx_mp4_co64_atom_t  *co64_atom;
2853     ngx_http_mp4_trak_t  *trak;
2854 
2855     /* chunk offsets atom */
2856 
2857     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
2858 
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');
2862 
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);
2866         return NGX_ERROR;
2867     }
2868 
2869     entries = ngx_mp4_get_32value(co64_atom->entries);
2870 
2871     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
2872 
2873     if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)
2874         + entries * sizeof(uint64_t) > atom_data_size)
2875     {
2876         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2877                       "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
2878         return NGX_ERROR;
2879     }
2880 
2881     atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
2882     atom_end = atom_table + entries * sizeof(uint64_t);
2883 
2884     trak = ngx_mp4_last_trak(mp4);
2885     trak->chunks = entries;
2886 
2887     atom = &trak->co64_atom_buf;
2888     atom->temporary = 1;
2889     atom->pos = atom_header;
2890     atom->last = atom_table;
2891 
2892     data = &trak->co64_data_buf;
2893     data->temporary = 1;
2894     data->pos = atom_table;
2895     data->last = atom_end;
2896 
2897     trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
2898     trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
2899 
2900     ngx_mp4_atom_next(mp4, atom_data_size);
2901 
2902     return NGX_OK;
2903 }
2904 
2905 
2906 static ngx_int_t
2907 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
2908     ngx_http_mp4_trak_t *trak)
2909 {
2910     size_t                atom_size;
2911     ngx_buf_t            *atom, *data;
2912     ngx_mp4_co64_atom_t  *co64_atom;
2913 
2914     /*
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
2918      */
2919 
2920     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2921                    "mp4 co64 atom update");
2922 
2923     data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
2924 
2925     if (data == NULL) {
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);
2929         return NGX_ERROR;
2930     }
2931 
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);
2936         return NGX_ERROR;
2937     }
2938 
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;
2942 
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);
2946 
2947     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2948                    "start chunk offset:%uL", trak->start_offset);
2949 
2950     atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
2951     co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
2952 
2953     ngx_mp4_set_32value(co64_atom->size, atom_size);
2954     ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk);
2955 
2956     return NGX_OK;
2957 }
2958 
2959 
2960 static void
2961 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
2962     ngx_http_mp4_trak_t *trak, off_t adjustment)
2963 {
2964     uint64_t    offset, *entry, *end;
2965     ngx_buf_t  *data;
2966 
2967     /*
2968      * moov.trak.mdia.minf.stbl.co64 adjustment requires
2969      * minimal start offset of all traks and new moov atom size
2970      */
2971 
2972     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2973                    "mp4 co64 atom adjustment");
2974 
2975     data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
2976     entry = (uint64_t *) data->pos;
2977     end = (uint64_t *) data->last;
2978 
2979     while (entry < end) {
2980         offset = ngx_mp4_get_64value(entry);
2981         offset += adjustment;
2982         ngx_mp4_set_64value(entry, offset);
2983         entry++;
2984     }
2985 }
2986 
2987 
2988 static char *
2989 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2990 {
2991     ngx_http_core_loc_conf_t  *clcf;
2992 
2993     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2994     clcf->handler = ngx_http_mp4_handler;
2995 
2996     return NGX_CONF_OK;
2997 }
2998 
2999 
3000 static void *
3001 ngx_http_mp4_create_conf(ngx_conf_t *cf)
3002 {
3003     ngx_http_mp4_conf_t  *conf;
3004 
3005     conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));
3006     if (conf == NULL) {
3007         return NULL;
3008     }
3009 
3010     conf->buffer_size = NGX_CONF_UNSET_SIZE;
3011     conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
3012 
3013     return conf;
3014 }
3015 
3016 
3017 static char *
3018 ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
3019 {
3020     ngx_http_mp4_conf_t *prev = parent;
3021     ngx_http_mp4_conf_t *conf = child;
3022 
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,
3025                               10 * 1024 * 1024);
3026 
3027     return NGX_CONF_OK;
3028 }
3029 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.