Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 #include <ngx_config.h>
0008 #include <ngx_core.h>
0009 #include <ngx_http.h>
0010 
0011 
0012 #define NGX_HTTP_MP4_TRAK_ATOM     0
0013 #define NGX_HTTP_MP4_TKHD_ATOM     1
0014 #define NGX_HTTP_MP4_MDIA_ATOM     2
0015 #define NGX_HTTP_MP4_MDHD_ATOM     3
0016 #define NGX_HTTP_MP4_HDLR_ATOM     4
0017 #define NGX_HTTP_MP4_MINF_ATOM     5
0018 #define NGX_HTTP_MP4_VMHD_ATOM     6
0019 #define NGX_HTTP_MP4_SMHD_ATOM     7
0020 #define NGX_HTTP_MP4_DINF_ATOM     8
0021 #define NGX_HTTP_MP4_STBL_ATOM     9
0022 #define NGX_HTTP_MP4_STSD_ATOM    10
0023 #define NGX_HTTP_MP4_STTS_ATOM    11
0024 #define NGX_HTTP_MP4_STTS_DATA    12
0025 #define NGX_HTTP_MP4_STSS_ATOM    13
0026 #define NGX_HTTP_MP4_STSS_DATA    14
0027 #define NGX_HTTP_MP4_CTTS_ATOM    15
0028 #define NGX_HTTP_MP4_CTTS_DATA    16
0029 #define NGX_HTTP_MP4_STSC_ATOM    17
0030 #define NGX_HTTP_MP4_STSC_START   18
0031 #define NGX_HTTP_MP4_STSC_DATA    19
0032 #define NGX_HTTP_MP4_STSC_END     20
0033 #define NGX_HTTP_MP4_STSZ_ATOM    21
0034 #define NGX_HTTP_MP4_STSZ_DATA    22
0035 #define NGX_HTTP_MP4_STCO_ATOM    23
0036 #define NGX_HTTP_MP4_STCO_DATA    24
0037 #define NGX_HTTP_MP4_CO64_ATOM    25
0038 #define NGX_HTTP_MP4_CO64_DATA    26
0039 
0040 #define NGX_HTTP_MP4_LAST_ATOM    NGX_HTTP_MP4_CO64_DATA
0041 
0042 
0043 typedef struct {
0044     size_t                buffer_size;
0045     size_t                max_buffer_size;
0046 } ngx_http_mp4_conf_t;
0047 
0048 
0049 typedef struct {
0050     u_char                chunk[4];
0051     u_char                samples[4];
0052     u_char                id[4];
0053 } ngx_mp4_stsc_entry_t;
0054 
0055 
0056 typedef struct {
0057     uint32_t              timescale;
0058     uint32_t              time_to_sample_entries;
0059     uint32_t              sample_to_chunk_entries;
0060     uint32_t              sync_samples_entries;
0061     uint32_t              composition_offset_entries;
0062     uint32_t              sample_sizes_entries;
0063     uint32_t              chunks;
0064 
0065     ngx_uint_t            start_sample;
0066     ngx_uint_t            end_sample;
0067     ngx_uint_t            start_chunk;
0068     ngx_uint_t            end_chunk;
0069     ngx_uint_t            start_chunk_samples;
0070     ngx_uint_t            end_chunk_samples;
0071     uint64_t              start_chunk_samples_size;
0072     uint64_t              end_chunk_samples_size;
0073     off_t                 start_offset;
0074     off_t                 end_offset;
0075 
0076     size_t                tkhd_size;
0077     size_t                mdhd_size;
0078     size_t                hdlr_size;
0079     size_t                vmhd_size;
0080     size_t                smhd_size;
0081     size_t                dinf_size;
0082     size_t                size;
0083 
0084     ngx_chain_t           out[NGX_HTTP_MP4_LAST_ATOM + 1];
0085 
0086     ngx_buf_t             trak_atom_buf;
0087     ngx_buf_t             tkhd_atom_buf;
0088     ngx_buf_t             mdia_atom_buf;
0089     ngx_buf_t             mdhd_atom_buf;
0090     ngx_buf_t             hdlr_atom_buf;
0091     ngx_buf_t             minf_atom_buf;
0092     ngx_buf_t             vmhd_atom_buf;
0093     ngx_buf_t             smhd_atom_buf;
0094     ngx_buf_t             dinf_atom_buf;
0095     ngx_buf_t             stbl_atom_buf;
0096     ngx_buf_t             stsd_atom_buf;
0097     ngx_buf_t             stts_atom_buf;
0098     ngx_buf_t             stts_data_buf;
0099     ngx_buf_t             stss_atom_buf;
0100     ngx_buf_t             stss_data_buf;
0101     ngx_buf_t             ctts_atom_buf;
0102     ngx_buf_t             ctts_data_buf;
0103     ngx_buf_t             stsc_atom_buf;
0104     ngx_buf_t             stsc_start_chunk_buf;
0105     ngx_buf_t             stsc_end_chunk_buf;
0106     ngx_buf_t             stsc_data_buf;
0107     ngx_buf_t             stsz_atom_buf;
0108     ngx_buf_t             stsz_data_buf;
0109     ngx_buf_t             stco_atom_buf;
0110     ngx_buf_t             stco_data_buf;
0111     ngx_buf_t             co64_atom_buf;
0112     ngx_buf_t             co64_data_buf;
0113 
0114     ngx_mp4_stsc_entry_t  stsc_start_chunk_entry;
0115     ngx_mp4_stsc_entry_t  stsc_end_chunk_entry;
0116 } ngx_http_mp4_trak_t;
0117 
0118 
0119 typedef struct {
0120     ngx_file_t            file;
0121 
0122     u_char               *buffer;
0123     u_char               *buffer_start;
0124     u_char               *buffer_pos;
0125     u_char               *buffer_end;
0126     size_t                buffer_size;
0127 
0128     off_t                 offset;
0129     off_t                 end;
0130     off_t                 content_length;
0131     ngx_uint_t            start;
0132     ngx_uint_t            length;
0133     uint32_t              timescale;
0134     ngx_http_request_t   *request;
0135     ngx_array_t           trak;
0136     ngx_http_mp4_trak_t   traks[2];
0137 
0138     size_t                ftyp_size;
0139     size_t                moov_size;
0140 
0141     ngx_chain_t          *out;
0142     ngx_chain_t           ftyp_atom;
0143     ngx_chain_t           moov_atom;
0144     ngx_chain_t           mvhd_atom;
0145     ngx_chain_t           mdat_atom;
0146     ngx_chain_t           mdat_data;
0147 
0148     ngx_buf_t             ftyp_atom_buf;
0149     ngx_buf_t             moov_atom_buf;
0150     ngx_buf_t             mvhd_atom_buf;
0151     ngx_buf_t             mdat_atom_buf;
0152     ngx_buf_t             mdat_data_buf;
0153 
0154     u_char                moov_atom_header[8];
0155     u_char                mdat_atom_header[16];
0156 } ngx_http_mp4_file_t;
0157 
0158 
0159 typedef struct {
0160     char                 *name;
0161     ngx_int_t           (*handler)(ngx_http_mp4_file_t *mp4,
0162                                    uint64_t atom_data_size);
0163 } ngx_http_mp4_atom_handler_t;
0164 
0165 
0166 #define ngx_mp4_atom_header(mp4)   (mp4->buffer_pos - 8)
0167 #define ngx_mp4_atom_data(mp4)     mp4->buffer_pos
0168 #define ngx_mp4_atom_data_size(t)  (uint64_t) (sizeof(t) - 8)
0169 
0170 
0171 #define ngx_mp4_atom_next(mp4, n)                                             \
0172     mp4->buffer_pos += (size_t) n;                                            \
0173     mp4->offset += n
0174 
0175 
0176 #define ngx_mp4_set_atom_name(p, n1, n2, n3, n4)                              \
0177     ((u_char *) (p))[4] = n1;                                                 \
0178     ((u_char *) (p))[5] = n2;                                                 \
0179     ((u_char *) (p))[6] = n3;                                                 \
0180     ((u_char *) (p))[7] = n4
0181 
0182 #define ngx_mp4_get_32value(p)                                                \
0183     ( ((uint32_t) ((u_char *) (p))[0] << 24)                                  \
0184     + (           ((u_char *) (p))[1] << 16)                                  \
0185     + (           ((u_char *) (p))[2] << 8)                                   \
0186     + (           ((u_char *) (p))[3]) )
0187 
0188 #define ngx_mp4_set_32value(p, n)                                             \
0189     ((u_char *) (p))[0] = (u_char) ((n) >> 24);                               \
0190     ((u_char *) (p))[1] = (u_char) ((n) >> 16);                               \
0191     ((u_char *) (p))[2] = (u_char) ((n) >> 8);                                \
0192     ((u_char *) (p))[3] = (u_char)  (n)
0193 
0194 #define ngx_mp4_get_64value(p)                                                \
0195     ( ((uint64_t) ((u_char *) (p))[0] << 56)                                  \
0196     + ((uint64_t) ((u_char *) (p))[1] << 48)                                  \
0197     + ((uint64_t) ((u_char *) (p))[2] << 40)                                  \
0198     + ((uint64_t) ((u_char *) (p))[3] << 32)                                  \
0199     + ((uint64_t) ((u_char *) (p))[4] << 24)                                  \
0200     + (           ((u_char *) (p))[5] << 16)                                  \
0201     + (           ((u_char *) (p))[6] << 8)                                   \
0202     + (           ((u_char *) (p))[7]) )
0203 
0204 #define ngx_mp4_set_64value(p, n)                                             \
0205     ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56);                    \
0206     ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48);                    \
0207     ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40);                    \
0208     ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32);                    \
0209     ((u_char *) (p))[4] = (u_char) (           (n) >> 24);                    \
0210     ((u_char *) (p))[5] = (u_char) (           (n) >> 16);                    \
0211     ((u_char *) (p))[6] = (u_char) (           (n) >> 8);                     \
0212     ((u_char *) (p))[7] = (u_char)             (n)
0213 
0214 #define ngx_mp4_last_trak(mp4)                                                \
0215     &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
0216 
0217 
0218 static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);
0219 static ngx_int_t ngx_http_mp4_atofp(u_char *line, size_t n, size_t point);
0220 
0221 static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);
0222 static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
0223     ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);
0224 static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);
0225 static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,
0226     uint64_t atom_data_size);
0227 static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
0228     uint64_t atom_data_size);
0229 static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
0230     uint64_t atom_data_size);
0231 static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
0232     off_t start_offset, off_t end_offset);
0233 static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
0234     uint64_t atom_data_size);
0235 static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
0236     uint64_t atom_data_size);
0237 static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
0238     ngx_http_mp4_trak_t *trak);
0239 static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,
0240     uint64_t atom_data_size);
0241 static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,
0242     uint64_t atom_data_size);
0243 static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,
0244     uint64_t atom_data_size);
0245 static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
0246     ngx_http_mp4_trak_t *trak);
0247 static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,
0248     uint64_t atom_data_size);
0249 static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,
0250     uint64_t atom_data_size);
0251 static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,
0252     uint64_t atom_data_size);
0253 static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
0254     ngx_http_mp4_trak_t *trak);
0255 static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,
0256     uint64_t atom_data_size);
0257 static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,
0258     uint64_t atom_data_size);
0259 static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
0260     uint64_t atom_data_size);
0261 static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,
0262     uint64_t atom_data_size);
0263 static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
0264     ngx_http_mp4_trak_t *trak);
0265 static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,
0266     uint64_t atom_data_size);
0267 static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
0268     uint64_t atom_data_size);
0269 static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
0270     ngx_http_mp4_trak_t *trak);
0271 static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
0272     ngx_http_mp4_trak_t *trak, ngx_uint_t start);
0273 static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
0274     uint64_t atom_data_size);
0275 static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
0276     ngx_http_mp4_trak_t *trak);
0277 static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
0278     ngx_http_mp4_trak_t *trak, ngx_uint_t start);
0279 static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
0280     uint64_t atom_data_size);
0281 static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
0282     ngx_http_mp4_trak_t *trak);
0283 static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
0284     ngx_http_mp4_trak_t *trak, ngx_uint_t start);
0285 static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
0286     uint64_t atom_data_size);
0287 static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
0288     ngx_http_mp4_trak_t *trak);
0289 static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
0290     ngx_http_mp4_trak_t *trak, ngx_uint_t start);
0291 static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
0292     uint64_t atom_data_size);
0293 static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
0294     ngx_http_mp4_trak_t *trak);
0295 static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,
0296     uint64_t atom_data_size);
0297 static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
0298     ngx_http_mp4_trak_t *trak);
0299 static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
0300     ngx_http_mp4_trak_t *trak, int32_t adjustment);
0301 static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
0302     uint64_t atom_data_size);
0303 static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
0304     ngx_http_mp4_trak_t *trak);
0305 static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
0306     ngx_http_mp4_trak_t *trak, off_t adjustment);
0307 
0308 static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
0309 static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
0310 static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
0311 
0312 
0313 static ngx_command_t  ngx_http_mp4_commands[] = {
0314 
0315     { ngx_string("mp4"),
0316       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
0317       ngx_http_mp4,
0318       0,
0319       0,
0320       NULL },
0321 
0322     { ngx_string("mp4_buffer_size"),
0323       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0324       ngx_conf_set_size_slot,
0325       NGX_HTTP_LOC_CONF_OFFSET,
0326       offsetof(ngx_http_mp4_conf_t, buffer_size),
0327       NULL },
0328 
0329     { ngx_string("mp4_max_buffer_size"),
0330       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0331       ngx_conf_set_size_slot,
0332       NGX_HTTP_LOC_CONF_OFFSET,
0333       offsetof(ngx_http_mp4_conf_t, max_buffer_size),
0334       NULL },
0335 
0336       ngx_null_command
0337 };
0338 
0339 
0340 static ngx_http_module_t  ngx_http_mp4_module_ctx = {
0341     NULL,                          /* preconfiguration */
0342     NULL,                          /* postconfiguration */
0343 
0344     NULL,                          /* create main configuration */
0345     NULL,                          /* init main configuration */
0346 
0347     NULL,                          /* create server configuration */
0348     NULL,                          /* merge server configuration */
0349 
0350     ngx_http_mp4_create_conf,      /* create location configuration */
0351     ngx_http_mp4_merge_conf        /* merge location configuration */
0352 };
0353 
0354 
0355 ngx_module_t  ngx_http_mp4_module = {
0356     NGX_MODULE_V1,
0357     &ngx_http_mp4_module_ctx,      /* module context */
0358     ngx_http_mp4_commands,         /* module directives */
0359     NGX_HTTP_MODULE,               /* module type */
0360     NULL,                          /* init master */
0361     NULL,                          /* init module */
0362     NULL,                          /* init process */
0363     NULL,                          /* init thread */
0364     NULL,                          /* exit thread */
0365     NULL,                          /* exit process */
0366     NULL,                          /* exit master */
0367     NGX_MODULE_V1_PADDING
0368 };
0369 
0370 
0371 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_atoms[] = {
0372     { "ftyp", ngx_http_mp4_read_ftyp_atom },
0373     { "moov", ngx_http_mp4_read_moov_atom },
0374     { "mdat", ngx_http_mp4_read_mdat_atom },
0375     { NULL, NULL }
0376 };
0377 
0378 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_moov_atoms[] = {
0379     { "mvhd", ngx_http_mp4_read_mvhd_atom },
0380     { "trak", ngx_http_mp4_read_trak_atom },
0381     { "cmov", ngx_http_mp4_read_cmov_atom },
0382     { NULL, NULL }
0383 };
0384 
0385 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_trak_atoms[] = {
0386     { "tkhd", ngx_http_mp4_read_tkhd_atom },
0387     { "mdia", ngx_http_mp4_read_mdia_atom },
0388     { NULL, NULL }
0389 };
0390 
0391 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_mdia_atoms[] = {
0392     { "mdhd", ngx_http_mp4_read_mdhd_atom },
0393     { "hdlr", ngx_http_mp4_read_hdlr_atom },
0394     { "minf", ngx_http_mp4_read_minf_atom },
0395     { NULL, NULL }
0396 };
0397 
0398 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_minf_atoms[] = {
0399     { "vmhd", ngx_http_mp4_read_vmhd_atom },
0400     { "smhd", ngx_http_mp4_read_smhd_atom },
0401     { "dinf", ngx_http_mp4_read_dinf_atom },
0402     { "stbl", ngx_http_mp4_read_stbl_atom },
0403     { NULL, NULL }
0404 };
0405 
0406 static ngx_http_mp4_atom_handler_t  ngx_http_mp4_stbl_atoms[] = {
0407     { "stsd", ngx_http_mp4_read_stsd_atom },
0408     { "stts", ngx_http_mp4_read_stts_atom },
0409     { "stss", ngx_http_mp4_read_stss_atom },
0410     { "ctts", ngx_http_mp4_read_ctts_atom },
0411     { "stsc", ngx_http_mp4_read_stsc_atom },
0412     { "stsz", ngx_http_mp4_read_stsz_atom },
0413     { "stco", ngx_http_mp4_read_stco_atom },
0414     { "co64", ngx_http_mp4_read_co64_atom },
0415     { NULL, NULL }
0416 };
0417 
0418 
0419 static ngx_int_t
0420 ngx_http_mp4_handler(ngx_http_request_t *r)
0421 {
0422     u_char                    *last;
0423     size_t                     root;
0424     ngx_int_t                  rc, start, end;
0425     ngx_uint_t                 level, length;
0426     ngx_str_t                  path, value;
0427     ngx_log_t                 *log;
0428     ngx_buf_t                 *b;
0429     ngx_chain_t                out;
0430     ngx_http_mp4_file_t       *mp4;
0431     ngx_open_file_info_t       of;
0432     ngx_http_core_loc_conf_t  *clcf;
0433 
0434     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
0435         return NGX_HTTP_NOT_ALLOWED;
0436     }
0437 
0438     if (r->uri.data[r->uri.len - 1] == '/') {
0439         return NGX_DECLINED;
0440     }
0441 
0442     rc = ngx_http_discard_request_body(r);
0443 
0444     if (rc != NGX_OK) {
0445         return rc;
0446     }
0447 
0448     last = ngx_http_map_uri_to_path(r, &path, &root, 0);
0449     if (last == NULL) {
0450         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0451     }
0452 
0453     log = r->connection->log;
0454 
0455     path.len = last - path.data;
0456 
0457     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
0458                    "http mp4 filename: \"%V\"", &path);
0459 
0460     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
0461 
0462     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
0463 
0464     of.read_ahead = clcf->read_ahead;
0465     of.directio = NGX_MAX_OFF_T_VALUE;
0466     of.valid = clcf->open_file_cache_valid;
0467     of.min_uses = clcf->open_file_cache_min_uses;
0468     of.errors = clcf->open_file_cache_errors;
0469     of.events = clcf->open_file_cache_events;
0470 
0471     if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
0472         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0473     }
0474 
0475     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
0476         != NGX_OK)
0477     {
0478         switch (of.err) {
0479 
0480         case 0:
0481             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0482 
0483         case NGX_ENOENT:
0484         case NGX_ENOTDIR:
0485         case NGX_ENAMETOOLONG:
0486 
0487             level = NGX_LOG_ERR;
0488             rc = NGX_HTTP_NOT_FOUND;
0489             break;
0490 
0491         case NGX_EACCES:
0492 #if (NGX_HAVE_OPENAT)
0493         case NGX_EMLINK:
0494         case NGX_ELOOP:
0495 #endif
0496 
0497             level = NGX_LOG_ERR;
0498             rc = NGX_HTTP_FORBIDDEN;
0499             break;
0500 
0501         default:
0502 
0503             level = NGX_LOG_CRIT;
0504             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
0505             break;
0506         }
0507 
0508         if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
0509             ngx_log_error(level, log, of.err,
0510                           "%s \"%s\" failed", of.failed, path.data);
0511         }
0512 
0513         return rc;
0514     }
0515 
0516     if (!of.is_file) {
0517 
0518         if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
0519             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
0520                           ngx_close_file_n " \"%s\" failed", path.data);
0521         }
0522 
0523         return NGX_DECLINED;
0524     }
0525 
0526     r->root_tested = !r->error_page;
0527     r->allow_ranges = 1;
0528 
0529     start = -1;
0530     length = 0;
0531     r->headers_out.content_length_n = of.size;
0532     mp4 = NULL;
0533     b = NULL;
0534 
0535     if (r->args.len) {
0536 
0537         if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
0538 
0539             /*
0540              * A Flash player may send start value with a lot of digits
0541              * after dot so a custom function is used instead of ngx_atofp().
0542              */
0543 
0544             start = ngx_http_mp4_atofp(value.data, value.len, 3);
0545         }
0546 
0547         if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {
0548 
0549             end = ngx_http_mp4_atofp(value.data, value.len, 3);
0550 
0551             if (end > 0) {
0552                 if (start < 0) {
0553                     start = 0;
0554                 }
0555 
0556                 if (end > start) {
0557                     length = end - start;
0558                 }
0559             }
0560         }
0561     }
0562 
0563     if (start >= 0) {
0564         r->single_range = 1;
0565 
0566         mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
0567         if (mp4 == NULL) {
0568             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0569         }
0570 
0571         mp4->file.fd = of.fd;
0572         mp4->file.name = path;
0573         mp4->file.log = r->connection->log;
0574         mp4->end = of.size;
0575         mp4->start = (ngx_uint_t) start;
0576         mp4->length = length;
0577         mp4->request = r;
0578 
0579         switch (ngx_http_mp4_process(mp4)) {
0580 
0581         case NGX_DECLINED:
0582             if (mp4->buffer) {
0583                 ngx_pfree(r->pool, mp4->buffer);
0584             }
0585 
0586             ngx_pfree(r->pool, mp4);
0587             mp4 = NULL;
0588 
0589             break;
0590 
0591         case NGX_OK:
0592             r->headers_out.content_length_n = mp4->content_length;
0593             break;
0594 
0595         default: /* NGX_ERROR */
0596             if (mp4->buffer) {
0597                 ngx_pfree(r->pool, mp4->buffer);
0598             }
0599 
0600             ngx_pfree(r->pool, mp4);
0601 
0602             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0603         }
0604     }
0605 
0606     log->action = "sending mp4 to client";
0607 
0608     if (clcf->directio <= of.size) {
0609 
0610         /*
0611          * DIRECTIO is set on transfer only
0612          * to allow kernel to cache "moov" atom
0613          */
0614 
0615         if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {
0616             ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
0617                           ngx_directio_on_n " \"%s\" failed", path.data);
0618         }
0619 
0620         of.is_directio = 1;
0621 
0622         if (mp4) {
0623             mp4->file.directio = 1;
0624         }
0625     }
0626 
0627     r->headers_out.status = NGX_HTTP_OK;
0628     r->headers_out.last_modified_time = of.mtime;
0629 
0630     if (ngx_http_set_etag(r) != NGX_OK) {
0631         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0632     }
0633 
0634     if (ngx_http_set_content_type(r) != NGX_OK) {
0635         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0636     }
0637 
0638     if (mp4 == NULL) {
0639         b = ngx_calloc_buf(r->pool);
0640         if (b == NULL) {
0641             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0642         }
0643 
0644         b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
0645         if (b->file == NULL) {
0646             return NGX_HTTP_INTERNAL_SERVER_ERROR;
0647         }
0648     }
0649 
0650     rc = ngx_http_send_header(r);
0651 
0652     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
0653         return rc;
0654     }
0655 
0656     if (mp4) {
0657         return ngx_http_output_filter(r, mp4->out);
0658     }
0659 
0660     b->file_pos = 0;
0661     b->file_last = of.size;
0662 
0663     b->in_file = b->file_last ? 1 : 0;
0664     b->last_buf = (r == r->main) ? 1 : 0;
0665     b->last_in_chain = 1;
0666 
0667     b->file->fd = of.fd;
0668     b->file->name = path;
0669     b->file->log = log;
0670     b->file->directio = of.is_directio;
0671 
0672     out.buf = b;
0673     out.next = NULL;
0674 
0675     return ngx_http_output_filter(r, &out);
0676 }
0677 
0678 
0679 static ngx_int_t
0680 ngx_http_mp4_atofp(u_char *line, size_t n, size_t point)
0681 {
0682     ngx_int_t   value, cutoff, cutlim;
0683     ngx_uint_t  dot;
0684 
0685     /* same as ngx_atofp(), but allows additional digits */
0686 
0687     if (n == 0) {
0688         return NGX_ERROR;
0689     }
0690 
0691     cutoff = NGX_MAX_INT_T_VALUE / 10;
0692     cutlim = NGX_MAX_INT_T_VALUE % 10;
0693 
0694     dot = 0;
0695 
0696     for (value = 0; n--; line++) {
0697 
0698         if (*line == '.') {
0699             if (dot) {
0700                 return NGX_ERROR;
0701             }
0702 
0703             dot = 1;
0704             continue;
0705         }
0706 
0707         if (*line < '0' || *line > '9') {
0708             return NGX_ERROR;
0709         }
0710 
0711         if (point == 0) {
0712             continue;
0713         }
0714 
0715         if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {
0716             return NGX_ERROR;
0717         }
0718 
0719         value = value * 10 + (*line - '0');
0720         point -= dot;
0721     }
0722 
0723     while (point--) {
0724         if (value > cutoff) {
0725             return NGX_ERROR;
0726         }
0727 
0728         value = value * 10;
0729     }
0730 
0731     return value;
0732 }
0733 
0734 
0735 static ngx_int_t
0736 ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
0737 {
0738     off_t                  start_offset, end_offset, adjustment;
0739     ngx_int_t              rc;
0740     ngx_uint_t             i, j;
0741     ngx_chain_t          **prev;
0742     ngx_http_mp4_trak_t   *trak;
0743     ngx_http_mp4_conf_t   *conf;
0744 
0745     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
0746                    "mp4 start:%ui, length:%ui", mp4->start, mp4->length);
0747 
0748     conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
0749 
0750     mp4->buffer_size = conf->buffer_size;
0751 
0752     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);
0753     if (rc != NGX_OK) {
0754         return rc;
0755     }
0756 
0757     if (mp4->trak.nelts == 0) {
0758         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
0759                       "no mp4 trak atoms were found in \"%s\"",
0760                       mp4->file.name.data);
0761         return NGX_ERROR;
0762     }
0763 
0764     if (mp4->mdat_atom.buf == NULL) {
0765         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
0766                       "no mp4 mdat atom was found in \"%s\"",
0767                       mp4->file.name.data);
0768         return NGX_ERROR;
0769     }
0770 
0771     prev = &mp4->out;
0772 
0773     if (mp4->ftyp_atom.buf) {
0774         *prev = &mp4->ftyp_atom;
0775         prev = &mp4->ftyp_atom.next;
0776     }
0777 
0778     *prev = &mp4->moov_atom;
0779     prev = &mp4->moov_atom.next;
0780 
0781     if (mp4->mvhd_atom.buf) {
0782         mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;
0783         *prev = &mp4->mvhd_atom;
0784         prev = &mp4->mvhd_atom.next;
0785     }
0786 
0787     start_offset = mp4->end;
0788     end_offset = 0;
0789     trak = mp4->trak.elts;
0790 
0791     for (i = 0; i < mp4->trak.nelts; i++) {
0792 
0793         if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
0794             return NGX_ERROR;
0795         }
0796 
0797         if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
0798             return NGX_ERROR;
0799         }
0800 
0801         ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);
0802 
0803         if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
0804             return NGX_ERROR;
0805         }
0806 
0807         if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
0808             return NGX_ERROR;
0809         }
0810 
0811         if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
0812             if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
0813                 return NGX_ERROR;
0814             }
0815 
0816         } else {
0817             if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
0818                 return NGX_ERROR;
0819             }
0820         }
0821 
0822         ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
0823         ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
0824         trak[i].size += trak[i].mdhd_size;
0825         trak[i].size += trak[i].hdlr_size;
0826         ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
0827         trak[i].size += trak[i].tkhd_size;
0828         ngx_http_mp4_update_trak_atom(mp4, &trak[i]);
0829 
0830         mp4->moov_size += trak[i].size;
0831 
0832         if (start_offset > trak[i].start_offset) {
0833             start_offset = trak[i].start_offset;
0834         }
0835 
0836         if (end_offset < trak[i].end_offset) {
0837             end_offset = trak[i].end_offset;
0838         }
0839 
0840         *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
0841         prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
0842 
0843         for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
0844             if (trak[i].out[j].buf) {
0845                 *prev = &trak[i].out[j];
0846                 prev = &trak[i].out[j].next;
0847             }
0848         }
0849     }
0850 
0851     if (end_offset < start_offset) {
0852         end_offset = start_offset;
0853     }
0854 
0855     mp4->moov_size += 8;
0856 
0857     ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
0858     ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');
0859     mp4->content_length += mp4->moov_size;
0860 
0861     *prev = &mp4->mdat_atom;
0862 
0863     if (start_offset > mp4->mdat_data.buf->file_last) {
0864         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
0865                       "start time is out mp4 mdat atom in \"%s\"",
0866                       mp4->file.name.data);
0867         return NGX_ERROR;
0868     }
0869 
0870     adjustment = mp4->ftyp_size + mp4->moov_size
0871                  + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)
0872                  - start_offset;
0873 
0874     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
0875                    "mp4 adjustment:%O", adjustment);
0876 
0877     for (i = 0; i < mp4->trak.nelts; i++) {
0878         if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
0879             ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
0880         } else {
0881             ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
0882         }
0883     }
0884 
0885     return NGX_OK;
0886 }
0887 
0888 
0889 typedef struct {
0890     u_char    size[4];
0891     u_char    name[4];
0892 } ngx_mp4_atom_header_t;
0893 
0894 typedef struct {
0895     u_char    size[4];
0896     u_char    name[4];
0897     u_char    size64[8];
0898 } ngx_mp4_atom_header64_t;
0899 
0900 
0901 static ngx_int_t
0902 ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
0903     ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)
0904 {
0905     off_t        end;
0906     size_t       atom_header_size;
0907     u_char      *atom_header, *atom_name;
0908     uint64_t     atom_size;
0909     ngx_int_t    rc;
0910     ngx_uint_t   n;
0911 
0912     end = mp4->offset + atom_data_size;
0913 
0914     while (mp4->offset < end) {
0915 
0916         if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {
0917             return NGX_ERROR;
0918         }
0919 
0920         atom_header = mp4->buffer_pos;
0921         atom_size = ngx_mp4_get_32value(atom_header);
0922         atom_header_size = sizeof(ngx_mp4_atom_header_t);
0923 
0924         if (atom_size == 0) {
0925             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
0926                            "mp4 atom end");
0927             return NGX_OK;
0928         }
0929 
0930         if (atom_size < sizeof(ngx_mp4_atom_header_t)) {
0931 
0932             if (atom_size == 1) {
0933 
0934                 if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))
0935                     != NGX_OK)
0936                 {
0937                     return NGX_ERROR;
0938                 }
0939 
0940                 /* 64-bit atom size */
0941                 atom_header = mp4->buffer_pos;
0942                 atom_size = ngx_mp4_get_64value(atom_header + 8);
0943                 atom_header_size = sizeof(ngx_mp4_atom_header64_t);
0944 
0945             } else {
0946                 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
0947                               "\"%s\" mp4 atom is too small:%uL",
0948                               mp4->file.name.data, atom_size);
0949                 return NGX_ERROR;
0950             }
0951         }
0952 
0953         if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {
0954             return NGX_ERROR;
0955         }
0956 
0957         atom_header = mp4->buffer_pos;
0958         atom_name = atom_header + sizeof(uint32_t);
0959 
0960         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
0961                        "mp4 atom: %*s @%O:%uL",
0962                        (size_t) 4, atom_name, mp4->offset, atom_size);
0963 
0964         if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)
0965             || mp4->offset + (off_t) atom_size > end)
0966         {
0967             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
0968                           "\"%s\" mp4 atom too large:%uL",
0969                           mp4->file.name.data, atom_size);
0970             return NGX_ERROR;
0971         }
0972 
0973         for (n = 0; atom[n].name; n++) {
0974 
0975             if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {
0976 
0977                 ngx_mp4_atom_next(mp4, atom_header_size);
0978 
0979                 rc = atom[n].handler(mp4, atom_size - atom_header_size);
0980                 if (rc != NGX_OK) {
0981                     return rc;
0982                 }
0983 
0984                 goto next;
0985             }
0986         }
0987 
0988         ngx_mp4_atom_next(mp4, atom_size);
0989 
0990     next:
0991         continue;
0992     }
0993 
0994     return NGX_OK;
0995 }
0996 
0997 
0998 static ngx_int_t
0999 ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)
1000 {
1001     ssize_t  n;
1002 
1003     if (mp4->buffer_pos + size <= mp4->buffer_end) {
1004         return NGX_OK;
1005     }
1006 
1007     if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {
1008         mp4->buffer_size = (size_t) (mp4->end - mp4->offset);
1009     }
1010 
1011     if (mp4->buffer_size < size) {
1012         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1013                       "\"%s\" mp4 file truncated", mp4->file.name.data);
1014         return NGX_ERROR;
1015     }
1016 
1017     if (mp4->buffer == NULL) {
1018         mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
1019         if (mp4->buffer == NULL) {
1020             return NGX_ERROR;
1021         }
1022 
1023         mp4->buffer_start = mp4->buffer;
1024     }
1025 
1026     n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
1027                       mp4->offset);
1028 
1029     if (n == NGX_ERROR) {
1030         return NGX_ERROR;
1031     }
1032 
1033     if ((size_t) n != mp4->buffer_size) {
1034         ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,
1035                       ngx_read_file_n " read only %z of %z from \"%s\"",
1036                       n, mp4->buffer_size, mp4->file.name.data);
1037         return NGX_ERROR;
1038     }
1039 
1040     mp4->buffer_pos = mp4->buffer_start;
1041     mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;
1042 
1043     return NGX_OK;
1044 }
1045 
1046 
1047 static ngx_int_t
1048 ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1049 {
1050     u_char     *ftyp_atom;
1051     size_t      atom_size;
1052     ngx_buf_t  *atom;
1053 
1054     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
1055 
1056     if (atom_data_size > 1024
1057         || ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)
1058     {
1059         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1060                       "\"%s\" mp4 ftyp atom is too large:%uL",
1061                       mp4->file.name.data, atom_data_size);
1062         return NGX_ERROR;
1063     }
1064 
1065     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1066 
1067     ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);
1068     if (ftyp_atom == NULL) {
1069         return NGX_ERROR;
1070     }
1071 
1072     ngx_mp4_set_32value(ftyp_atom, atom_size);
1073     ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');
1074 
1075     /*
1076      * only moov atom content is guaranteed to be in mp4->buffer
1077      * during sending response, so ftyp atom content should be copied
1078      */
1079     ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),
1080                ngx_mp4_atom_data(mp4), (size_t) atom_data_size);
1081 
1082     atom = &mp4->ftyp_atom_buf;
1083     atom->temporary = 1;
1084     atom->pos = ftyp_atom;
1085     atom->last = ftyp_atom + atom_size;
1086 
1087     mp4->ftyp_atom.buf = atom;
1088     mp4->ftyp_size = atom_size;
1089     mp4->content_length = atom_size;
1090 
1091     ngx_mp4_atom_next(mp4, atom_data_size);
1092 
1093     return NGX_OK;
1094 }
1095 
1096 
1097 /*
1098  * Small excess buffer to process atoms after moov atom, mp4->buffer_start
1099  * will be set to this buffer part after moov atom processing.
1100  */
1101 #define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS  (4 * 1024)
1102 
1103 static ngx_int_t
1104 ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1105 {
1106     ngx_int_t             rc;
1107     ngx_uint_t            no_mdat;
1108     ngx_buf_t            *atom;
1109     ngx_http_mp4_conf_t  *conf;
1110 
1111     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
1112 
1113     no_mdat = (mp4->mdat_atom.buf == NULL);
1114 
1115     if (no_mdat && mp4->start == 0 && mp4->length == 0) {
1116         /*
1117          * send original file if moov atom resides before
1118          * mdat atom and client requests integral file
1119          */
1120         return NGX_DECLINED;
1121     }
1122 
1123     conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
1124 
1125     if (atom_data_size > mp4->buffer_size) {
1126 
1127         if (atom_data_size > conf->max_buffer_size) {
1128             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1129                           "\"%s\" mp4 moov atom is too large:%uL, "
1130                           "you may want to increase mp4_max_buffer_size",
1131                           mp4->file.name.data, atom_data_size);
1132             return NGX_ERROR;
1133         }
1134 
1135         ngx_pfree(mp4->request->pool, mp4->buffer);
1136         mp4->buffer = NULL;
1137         mp4->buffer_pos = NULL;
1138         mp4->buffer_end = NULL;
1139 
1140         mp4->buffer_size = (size_t) atom_data_size
1141                          + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;
1142     }
1143 
1144     if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {
1145         return NGX_ERROR;
1146     }
1147 
1148     mp4->trak.elts = &mp4->traks;
1149     mp4->trak.size = sizeof(ngx_http_mp4_trak_t);
1150     mp4->trak.nalloc = 2;
1151     mp4->trak.pool = mp4->request->pool;
1152 
1153     atom = &mp4->moov_atom_buf;
1154     atom->temporary = 1;
1155     atom->pos = mp4->moov_atom_header;
1156     atom->last = mp4->moov_atom_header + 8;
1157 
1158     mp4->moov_atom.buf = &mp4->moov_atom_buf;
1159 
1160     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);
1161 
1162     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");
1163 
1164     if (no_mdat) {
1165         mp4->buffer_start = mp4->buffer_pos;
1166         mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;
1167 
1168         if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {
1169             mp4->buffer = NULL;
1170             mp4->buffer_pos = NULL;
1171             mp4->buffer_end = NULL;
1172         }
1173 
1174     } else {
1175         /* skip atoms after moov atom */
1176         mp4->offset = mp4->end;
1177     }
1178 
1179     return rc;
1180 }
1181 
1182 
1183 static ngx_int_t
1184 ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1185 {
1186     ngx_buf_t  *data;
1187 
1188     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");
1189 
1190     data = &mp4->mdat_data_buf;
1191     data->file = &mp4->file;
1192     data->in_file = 1;
1193     data->last_buf = (mp4->request == mp4->request->main) ? 1 : 0;
1194     data->last_in_chain = 1;
1195     data->file_last = mp4->offset + atom_data_size;
1196 
1197     mp4->mdat_atom.buf = &mp4->mdat_atom_buf;
1198     mp4->mdat_atom.next = &mp4->mdat_data;
1199     mp4->mdat_data.buf = data;
1200 
1201     if (mp4->trak.nelts) {
1202         /* skip atoms after mdat atom */
1203         mp4->offset = mp4->end;
1204 
1205     } else {
1206         ngx_mp4_atom_next(mp4, atom_data_size);
1207     }
1208 
1209     return NGX_OK;
1210 }
1211 
1212 
1213 static size_t
1214 ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,
1215     off_t end_offset)
1216 {
1217     off_t       atom_data_size;
1218     u_char     *atom_header;
1219     uint32_t    atom_header_size;
1220     uint64_t    atom_size;
1221     ngx_buf_t  *atom;
1222 
1223     atom_data_size = end_offset - start_offset;
1224     mp4->mdat_data.buf->file_pos = start_offset;
1225     mp4->mdat_data.buf->file_last = end_offset;
1226 
1227     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1228                    "mdat new offset @%O:%O", start_offset, atom_data_size);
1229 
1230     atom_header = mp4->mdat_atom_header;
1231 
1232     if ((uint64_t) atom_data_size
1233         > (uint64_t) 0xffffffff - sizeof(ngx_mp4_atom_header_t))
1234     {
1235         atom_size = 1;
1236         atom_header_size = sizeof(ngx_mp4_atom_header64_t);
1237         ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
1238                             sizeof(ngx_mp4_atom_header64_t) + atom_data_size);
1239     } else {
1240         atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;
1241         atom_header_size = sizeof(ngx_mp4_atom_header_t);
1242     }
1243 
1244     mp4->content_length += atom_header_size + atom_data_size;
1245 
1246     ngx_mp4_set_32value(atom_header, atom_size);
1247     ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');
1248 
1249     atom = &mp4->mdat_atom_buf;
1250     atom->temporary = 1;
1251     atom->pos = atom_header;
1252     atom->last = atom_header + atom_header_size;
1253 
1254     return atom_header_size;
1255 }
1256 
1257 
1258 typedef struct {
1259     u_char    size[4];
1260     u_char    name[4];
1261     u_char    version[1];
1262     u_char    flags[3];
1263     u_char    creation_time[4];
1264     u_char    modification_time[4];
1265     u_char    timescale[4];
1266     u_char    duration[4];
1267     u_char    rate[4];
1268     u_char    volume[2];
1269     u_char    reserved[10];
1270     u_char    matrix[36];
1271     u_char    preview_time[4];
1272     u_char    preview_duration[4];
1273     u_char    poster_time[4];
1274     u_char    selection_time[4];
1275     u_char    selection_duration[4];
1276     u_char    current_time[4];
1277     u_char    next_track_id[4];
1278 } ngx_mp4_mvhd_atom_t;
1279 
1280 typedef struct {
1281     u_char    size[4];
1282     u_char    name[4];
1283     u_char    version[1];
1284     u_char    flags[3];
1285     u_char    creation_time[8];
1286     u_char    modification_time[8];
1287     u_char    timescale[4];
1288     u_char    duration[8];
1289     u_char    rate[4];
1290     u_char    volume[2];
1291     u_char    reserved[10];
1292     u_char    matrix[36];
1293     u_char    preview_time[4];
1294     u_char    preview_duration[4];
1295     u_char    poster_time[4];
1296     u_char    selection_time[4];
1297     u_char    selection_duration[4];
1298     u_char    current_time[4];
1299     u_char    next_track_id[4];
1300 } ngx_mp4_mvhd64_atom_t;
1301 
1302 
1303 static ngx_int_t
1304 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1305 {
1306     u_char                 *atom_header;
1307     size_t                  atom_size;
1308     uint32_t                timescale;
1309     uint64_t                duration, start_time, length_time;
1310     ngx_buf_t              *atom;
1311     ngx_mp4_mvhd_atom_t    *mvhd_atom;
1312     ngx_mp4_mvhd64_atom_t  *mvhd64_atom;
1313 
1314     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
1315 
1316     atom_header = ngx_mp4_atom_header(mp4);
1317     mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;
1318     mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;
1319     ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');
1320 
1321     if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {
1322         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1323                       "\"%s\" mp4 mvhd atom too small", mp4->file.name.data);
1324         return NGX_ERROR;
1325     }
1326 
1327     if (mvhd_atom->version[0] == 0) {
1328         /* version 0: 32-bit duration */
1329         timescale = ngx_mp4_get_32value(mvhd_atom->timescale);
1330         duration = ngx_mp4_get_32value(mvhd_atom->duration);
1331 
1332     } else {
1333         /* version 1: 64-bit duration */
1334 
1335         if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {
1336             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1337                           "\"%s\" mp4 mvhd atom too small",
1338                           mp4->file.name.data);
1339             return NGX_ERROR;
1340         }
1341 
1342         timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);
1343         duration = ngx_mp4_get_64value(mvhd64_atom->duration);
1344     }
1345 
1346     mp4->timescale = timescale;
1347 
1348     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1349                    "mvhd timescale:%uD, duration:%uL, time:%.3fs",
1350                    timescale, duration, (double) duration / timescale);
1351 
1352     start_time = (uint64_t) mp4->start * timescale / 1000;
1353 
1354     if (duration < start_time) {
1355         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1356                       "\"%s\" mp4 start time exceeds file duration",
1357                       mp4->file.name.data);
1358         return NGX_ERROR;
1359     }
1360 
1361     duration -= start_time;
1362 
1363     if (mp4->length) {
1364         length_time = (uint64_t) mp4->length * timescale / 1000;
1365 
1366         if (duration > length_time) {
1367             duration = length_time;
1368         }
1369     }
1370 
1371     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1372                    "mvhd new duration:%uL, time:%.3fs",
1373                    duration, (double) duration / timescale);
1374 
1375     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1376     ngx_mp4_set_32value(mvhd_atom->size, atom_size);
1377 
1378     if (mvhd_atom->version[0] == 0) {
1379         ngx_mp4_set_32value(mvhd_atom->duration, duration);
1380 
1381     } else {
1382         ngx_mp4_set_64value(mvhd64_atom->duration, duration);
1383     }
1384 
1385     atom = &mp4->mvhd_atom_buf;
1386     atom->temporary = 1;
1387     atom->pos = atom_header;
1388     atom->last = atom_header + atom_size;
1389 
1390     mp4->mvhd_atom.buf = atom;
1391 
1392     ngx_mp4_atom_next(mp4, atom_data_size);
1393 
1394     return NGX_OK;
1395 }
1396 
1397 
1398 static ngx_int_t
1399 ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1400 {
1401     u_char               *atom_header, *atom_end;
1402     off_t                 atom_file_end;
1403     ngx_int_t             rc;
1404     ngx_buf_t            *atom;
1405     ngx_http_mp4_trak_t  *trak;
1406 
1407     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");
1408 
1409     trak = ngx_array_push(&mp4->trak);
1410     if (trak == NULL) {
1411         return NGX_ERROR;
1412     }
1413 
1414     ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
1415 
1416     atom_header = ngx_mp4_atom_header(mp4);
1417     ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');
1418 
1419     atom = &trak->trak_atom_buf;
1420     atom->temporary = 1;
1421     atom->pos = atom_header;
1422     atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
1423 
1424     trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
1425 
1426     atom_end = mp4->buffer_pos + (size_t) atom_data_size;
1427     atom_file_end = mp4->offset + atom_data_size;
1428 
1429     rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
1430 
1431     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1432                    "mp4 trak atom: %i", rc);
1433 
1434     if (rc == NGX_DECLINED) {
1435         /* skip this trak */
1436         ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
1437         mp4->trak.nelts--;
1438         mp4->buffer_pos = atom_end;
1439         mp4->offset = atom_file_end;
1440         return NGX_OK;
1441     }
1442 
1443     return rc;
1444 }
1445 
1446 
1447 static void
1448 ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
1449     ngx_http_mp4_trak_t *trak)
1450 {
1451     ngx_buf_t  *atom;
1452 
1453     trak->size += sizeof(ngx_mp4_atom_header_t);
1454     atom = &trak->trak_atom_buf;
1455     ngx_mp4_set_32value(atom->pos, trak->size);
1456 }
1457 
1458 
1459 static ngx_int_t
1460 ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1461 {
1462     ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1463                   "\"%s\" mp4 compressed moov atom (cmov) is not supported",
1464                   mp4->file.name.data);
1465 
1466     return NGX_ERROR;
1467 }
1468 
1469 
1470 typedef struct {
1471     u_char    size[4];
1472     u_char    name[4];
1473     u_char    version[1];
1474     u_char    flags[3];
1475     u_char    creation_time[4];
1476     u_char    modification_time[4];
1477     u_char    track_id[4];
1478     u_char    reserved1[4];
1479     u_char    duration[4];
1480     u_char    reserved2[8];
1481     u_char    layer[2];
1482     u_char    group[2];
1483     u_char    volume[2];
1484     u_char    reserved3[2];
1485     u_char    matrix[36];
1486     u_char    width[4];
1487     u_char    height[4];
1488 } ngx_mp4_tkhd_atom_t;
1489 
1490 typedef struct {
1491     u_char    size[4];
1492     u_char    name[4];
1493     u_char    version[1];
1494     u_char    flags[3];
1495     u_char    creation_time[8];
1496     u_char    modification_time[8];
1497     u_char    track_id[4];
1498     u_char    reserved1[4];
1499     u_char    duration[8];
1500     u_char    reserved2[8];
1501     u_char    layer[2];
1502     u_char    group[2];
1503     u_char    volume[2];
1504     u_char    reserved3[2];
1505     u_char    matrix[36];
1506     u_char    width[4];
1507     u_char    height[4];
1508 } ngx_mp4_tkhd64_atom_t;
1509 
1510 
1511 static ngx_int_t
1512 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1513 {
1514     u_char                 *atom_header;
1515     size_t                  atom_size;
1516     uint64_t                duration, start_time, length_time;
1517     ngx_buf_t              *atom;
1518     ngx_http_mp4_trak_t    *trak;
1519     ngx_mp4_tkhd_atom_t    *tkhd_atom;
1520     ngx_mp4_tkhd64_atom_t  *tkhd64_atom;
1521 
1522     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");
1523 
1524     atom_header = ngx_mp4_atom_header(mp4);
1525     tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;
1526     tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;
1527     ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');
1528 
1529     if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {
1530         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1531                       "\"%s\" mp4 tkhd atom too small", mp4->file.name.data);
1532         return NGX_ERROR;
1533     }
1534 
1535     if (tkhd_atom->version[0] == 0) {
1536         /* version 0: 32-bit duration */
1537         duration = ngx_mp4_get_32value(tkhd_atom->duration);
1538 
1539     } else {
1540         /* version 1: 64-bit duration */
1541 
1542         if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {
1543             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1544                           "\"%s\" mp4 tkhd atom too small",
1545                           mp4->file.name.data);
1546             return NGX_ERROR;
1547         }
1548 
1549         duration = ngx_mp4_get_64value(tkhd64_atom->duration);
1550     }
1551 
1552     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1553                    "tkhd duration:%uL, time:%.3fs",
1554                    duration, (double) duration / mp4->timescale);
1555 
1556     start_time = (uint64_t) mp4->start * mp4->timescale / 1000;
1557 
1558     if (duration <= start_time) {
1559         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1560                        "tkhd duration is less than start time");
1561         return NGX_DECLINED;
1562     }
1563 
1564     duration -= start_time;
1565 
1566     if (mp4->length) {
1567         length_time = (uint64_t) mp4->length * mp4->timescale / 1000;
1568 
1569         if (duration > length_time) {
1570             duration = length_time;
1571         }
1572     }
1573 
1574     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1575                    "tkhd new duration:%uL, time:%.3fs",
1576                    duration, (double) duration / mp4->timescale);
1577 
1578     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1579 
1580     trak = ngx_mp4_last_trak(mp4);
1581     trak->tkhd_size = atom_size;
1582 
1583     ngx_mp4_set_32value(tkhd_atom->size, atom_size);
1584 
1585     if (tkhd_atom->version[0] == 0) {
1586         ngx_mp4_set_32value(tkhd_atom->duration, duration);
1587 
1588     } else {
1589         ngx_mp4_set_64value(tkhd64_atom->duration, duration);
1590     }
1591 
1592     atom = &trak->tkhd_atom_buf;
1593     atom->temporary = 1;
1594     atom->pos = atom_header;
1595     atom->last = atom_header + atom_size;
1596 
1597     trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;
1598 
1599     ngx_mp4_atom_next(mp4, atom_data_size);
1600 
1601     return NGX_OK;
1602 }
1603 
1604 
1605 static ngx_int_t
1606 ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1607 {
1608     u_char               *atom_header;
1609     ngx_buf_t            *atom;
1610     ngx_http_mp4_trak_t  *trak;
1611 
1612     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");
1613 
1614     atom_header = ngx_mp4_atom_header(mp4);
1615     ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');
1616 
1617     trak = ngx_mp4_last_trak(mp4);
1618 
1619     atom = &trak->mdia_atom_buf;
1620     atom->temporary = 1;
1621     atom->pos = atom_header;
1622     atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
1623 
1624     trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;
1625 
1626     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);
1627 }
1628 
1629 
1630 static void
1631 ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
1632     ngx_http_mp4_trak_t *trak)
1633 {
1634     ngx_buf_t  *atom;
1635 
1636     trak->size += sizeof(ngx_mp4_atom_header_t);
1637     atom = &trak->mdia_atom_buf;
1638     ngx_mp4_set_32value(atom->pos, trak->size);
1639 }
1640 
1641 
1642 typedef struct {
1643     u_char    size[4];
1644     u_char    name[4];
1645     u_char    version[1];
1646     u_char    flags[3];
1647     u_char    creation_time[4];
1648     u_char    modification_time[4];
1649     u_char    timescale[4];
1650     u_char    duration[4];
1651     u_char    language[2];
1652     u_char    quality[2];
1653 } ngx_mp4_mdhd_atom_t;
1654 
1655 typedef struct {
1656     u_char    size[4];
1657     u_char    name[4];
1658     u_char    version[1];
1659     u_char    flags[3];
1660     u_char    creation_time[8];
1661     u_char    modification_time[8];
1662     u_char    timescale[4];
1663     u_char    duration[8];
1664     u_char    language[2];
1665     u_char    quality[2];
1666 } ngx_mp4_mdhd64_atom_t;
1667 
1668 
1669 static ngx_int_t
1670 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1671 {
1672     u_char                 *atom_header;
1673     size_t                  atom_size;
1674     uint32_t                timescale;
1675     uint64_t                duration, start_time, length_time;
1676     ngx_buf_t              *atom;
1677     ngx_http_mp4_trak_t    *trak;
1678     ngx_mp4_mdhd_atom_t    *mdhd_atom;
1679     ngx_mp4_mdhd64_atom_t  *mdhd64_atom;
1680 
1681     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");
1682 
1683     atom_header = ngx_mp4_atom_header(mp4);
1684     mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;
1685     mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;
1686     ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');
1687 
1688     if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {
1689         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1690                       "\"%s\" mp4 mdhd atom too small", mp4->file.name.data);
1691         return NGX_ERROR;
1692     }
1693 
1694     if (mdhd_atom->version[0] == 0) {
1695         /* version 0: everything is 32-bit */
1696         timescale = ngx_mp4_get_32value(mdhd_atom->timescale);
1697         duration = ngx_mp4_get_32value(mdhd_atom->duration);
1698 
1699     } else {
1700         /* version 1: 64-bit duration and 32-bit timescale */
1701 
1702         if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {
1703             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1704                           "\"%s\" mp4 mdhd atom too small",
1705                           mp4->file.name.data);
1706             return NGX_ERROR;
1707         }
1708 
1709         timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);
1710         duration = ngx_mp4_get_64value(mdhd64_atom->duration);
1711     }
1712 
1713     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1714                    "mdhd timescale:%uD, duration:%uL, time:%.3fs",
1715                    timescale, duration, (double) duration / timescale);
1716 
1717     start_time = (uint64_t) mp4->start * timescale / 1000;
1718 
1719     if (duration <= start_time) {
1720         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1721                        "mdhd duration is less than start time");
1722         return NGX_DECLINED;
1723     }
1724 
1725     duration -= start_time;
1726 
1727     if (mp4->length) {
1728         length_time = (uint64_t) mp4->length * timescale / 1000;
1729 
1730         if (duration > length_time) {
1731             duration = length_time;
1732         }
1733     }
1734 
1735     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1736                    "mdhd new duration:%uL, time:%.3fs",
1737                    duration, (double) duration / timescale);
1738 
1739     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1740 
1741     trak = ngx_mp4_last_trak(mp4);
1742     trak->mdhd_size = atom_size;
1743     trak->timescale = timescale;
1744 
1745     ngx_mp4_set_32value(mdhd_atom->size, atom_size);
1746 
1747     if (mdhd_atom->version[0] == 0) {
1748         ngx_mp4_set_32value(mdhd_atom->duration, duration);
1749 
1750     } else {
1751         ngx_mp4_set_64value(mdhd64_atom->duration, duration);
1752     }
1753 
1754     atom = &trak->mdhd_atom_buf;
1755     atom->temporary = 1;
1756     atom->pos = atom_header;
1757     atom->last = atom_header + atom_size;
1758 
1759     trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;
1760 
1761     ngx_mp4_atom_next(mp4, atom_data_size);
1762 
1763     return NGX_OK;
1764 }
1765 
1766 
1767 static ngx_int_t
1768 ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1769 {
1770     u_char              *atom_header;
1771     size_t               atom_size;
1772     ngx_buf_t            *atom;
1773     ngx_http_mp4_trak_t  *trak;
1774 
1775     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");
1776 
1777     atom_header = ngx_mp4_atom_header(mp4);
1778     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1779     ngx_mp4_set_32value(atom_header, atom_size);
1780     ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');
1781 
1782     trak = ngx_mp4_last_trak(mp4);
1783 
1784     atom = &trak->hdlr_atom_buf;
1785     atom->temporary = 1;
1786     atom->pos = atom_header;
1787     atom->last = atom_header + atom_size;
1788 
1789     trak->hdlr_size = atom_size;
1790     trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;
1791 
1792     ngx_mp4_atom_next(mp4, atom_data_size);
1793 
1794     return NGX_OK;
1795 }
1796 
1797 
1798 static ngx_int_t
1799 ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1800 {
1801     u_char               *atom_header;
1802     ngx_buf_t            *atom;
1803     ngx_http_mp4_trak_t  *trak;
1804 
1805     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");
1806 
1807     atom_header = ngx_mp4_atom_header(mp4);
1808     ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');
1809 
1810     trak = ngx_mp4_last_trak(mp4);
1811 
1812     atom = &trak->minf_atom_buf;
1813     atom->temporary = 1;
1814     atom->pos = atom_header;
1815     atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
1816 
1817     trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;
1818 
1819     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);
1820 }
1821 
1822 
1823 static void
1824 ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
1825     ngx_http_mp4_trak_t *trak)
1826 {
1827     ngx_buf_t  *atom;
1828 
1829     trak->size += sizeof(ngx_mp4_atom_header_t)
1830                + trak->vmhd_size
1831                + trak->smhd_size
1832                + trak->dinf_size;
1833     atom = &trak->minf_atom_buf;
1834     ngx_mp4_set_32value(atom->pos, trak->size);
1835 }
1836 
1837 
1838 static ngx_int_t
1839 ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1840 {
1841     u_char              *atom_header;
1842     size_t               atom_size;
1843     ngx_buf_t            *atom;
1844     ngx_http_mp4_trak_t  *trak;
1845 
1846     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");
1847 
1848     atom_header = ngx_mp4_atom_header(mp4);
1849     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1850     ngx_mp4_set_32value(atom_header, atom_size);
1851     ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');
1852 
1853     trak = ngx_mp4_last_trak(mp4);
1854 
1855     atom = &trak->vmhd_atom_buf;
1856     atom->temporary = 1;
1857     atom->pos = atom_header;
1858     atom->last = atom_header + atom_size;
1859 
1860     trak->vmhd_size += atom_size;
1861     trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;
1862 
1863     ngx_mp4_atom_next(mp4, atom_data_size);
1864 
1865     return NGX_OK;
1866 }
1867 
1868 
1869 static ngx_int_t
1870 ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1871 {
1872     u_char              *atom_header;
1873     size_t               atom_size;
1874     ngx_buf_t            *atom;
1875     ngx_http_mp4_trak_t  *trak;
1876 
1877     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");
1878 
1879     atom_header = ngx_mp4_atom_header(mp4);
1880     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1881     ngx_mp4_set_32value(atom_header, atom_size);
1882     ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');
1883 
1884     trak = ngx_mp4_last_trak(mp4);
1885 
1886     atom = &trak->smhd_atom_buf;
1887     atom->temporary = 1;
1888     atom->pos = atom_header;
1889     atom->last = atom_header + atom_size;
1890 
1891     trak->smhd_size += atom_size;
1892     trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;
1893 
1894     ngx_mp4_atom_next(mp4, atom_data_size);
1895 
1896     return NGX_OK;
1897 }
1898 
1899 
1900 static ngx_int_t
1901 ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1902 {
1903     u_char              *atom_header;
1904     size_t               atom_size;
1905     ngx_buf_t            *atom;
1906     ngx_http_mp4_trak_t  *trak;
1907 
1908     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");
1909 
1910     atom_header = ngx_mp4_atom_header(mp4);
1911     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1912     ngx_mp4_set_32value(atom_header, atom_size);
1913     ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');
1914 
1915     trak = ngx_mp4_last_trak(mp4);
1916 
1917     atom = &trak->dinf_atom_buf;
1918     atom->temporary = 1;
1919     atom->pos = atom_header;
1920     atom->last = atom_header + atom_size;
1921 
1922     trak->dinf_size += atom_size;
1923     trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;
1924 
1925     ngx_mp4_atom_next(mp4, atom_data_size);
1926 
1927     return NGX_OK;
1928 }
1929 
1930 
1931 static ngx_int_t
1932 ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1933 {
1934     u_char               *atom_header;
1935     ngx_buf_t            *atom;
1936     ngx_http_mp4_trak_t  *trak;
1937 
1938     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");
1939 
1940     atom_header = ngx_mp4_atom_header(mp4);
1941     ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');
1942 
1943     trak = ngx_mp4_last_trak(mp4);
1944 
1945     atom = &trak->stbl_atom_buf;
1946     atom->temporary = 1;
1947     atom->pos = atom_header;
1948     atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
1949 
1950     trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;
1951 
1952     return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);
1953 }
1954 
1955 
1956 static void
1957 ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
1958     ngx_http_mp4_trak_t *trak)
1959 {
1960     ngx_buf_t  *atom;
1961 
1962     trak->size += sizeof(ngx_mp4_atom_header_t);
1963     atom = &trak->stbl_atom_buf;
1964     ngx_mp4_set_32value(atom->pos, trak->size);
1965 }
1966 
1967 
1968 typedef struct {
1969     u_char    size[4];
1970     u_char    name[4];
1971     u_char    version[1];
1972     u_char    flags[3];
1973     u_char    entries[4];
1974 
1975     u_char    media_size[4];
1976     u_char    media_name[4];
1977 } ngx_mp4_stsd_atom_t;
1978 
1979 
1980 static ngx_int_t
1981 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1982 {
1983     u_char               *atom_header, *atom_table;
1984     size_t                atom_size;
1985     ngx_buf_t            *atom;
1986     ngx_mp4_stsd_atom_t  *stsd_atom;
1987     ngx_http_mp4_trak_t  *trak;
1988 
1989     /* sample description atom */
1990 
1991     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");
1992 
1993     atom_header = ngx_mp4_atom_header(mp4);
1994     stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;
1995     atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1996     atom_table = atom_header + atom_size;
1997     ngx_mp4_set_32value(stsd_atom->size, atom_size);
1998     ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
1999 
2000     if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {
2001         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2002                       "\"%s\" mp4 stsd atom too small", mp4->file.name.data);
2003         return NGX_ERROR;
2004     }
2005 
2006     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2007                    "stsd entries:%uD, media:%*s",
2008                    ngx_mp4_get_32value(stsd_atom->entries),
2009                    (size_t) 4, stsd_atom->media_name);
2010 
2011     trak = ngx_mp4_last_trak(mp4);
2012 
2013     atom = &trak->stsd_atom_buf;
2014     atom->temporary = 1;
2015     atom->pos = atom_header;
2016     atom->last = atom_table;
2017 
2018     trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;
2019     trak->size += atom_size;
2020 
2021     ngx_mp4_atom_next(mp4, atom_data_size);
2022 
2023     return NGX_OK;
2024 }
2025 
2026 
2027 typedef struct {
2028     u_char    size[4];
2029     u_char    name[4];
2030     u_char    version[1];
2031     u_char    flags[3];
2032     u_char    entries[4];
2033 } ngx_mp4_stts_atom_t;
2034 
2035 typedef struct {
2036     u_char    count[4];
2037     u_char    duration[4];
2038 } ngx_mp4_stts_entry_t;
2039 
2040 
2041 static ngx_int_t
2042 ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2043 {
2044     u_char               *atom_header, *atom_table, *atom_end;
2045     uint32_t              entries;
2046     ngx_buf_t            *atom, *data;
2047     ngx_mp4_stts_atom_t  *stts_atom;
2048     ngx_http_mp4_trak_t  *trak;
2049 
2050     /* time-to-sample atom */
2051 
2052     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");
2053 
2054     atom_header = ngx_mp4_atom_header(mp4);
2055     stts_atom = (ngx_mp4_stts_atom_t *) atom_header;
2056     ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');
2057 
2058     if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {
2059         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2060                       "\"%s\" mp4 stts atom too small", mp4->file.name.data);
2061         return NGX_ERROR;
2062     }
2063 
2064     entries = ngx_mp4_get_32value(stts_atom->entries);
2065 
2066     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2067                    "mp4 time-to-sample entries:%uD", entries);
2068 
2069     if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)
2070         + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)
2071     {
2072         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2073                       "\"%s\" mp4 stts atom too small", mp4->file.name.data);
2074         return NGX_ERROR;
2075     }
2076 
2077     atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);
2078     atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);
2079 
2080     trak = ngx_mp4_last_trak(mp4);
2081     trak->time_to_sample_entries = entries;
2082 
2083     atom = &trak->stts_atom_buf;
2084     atom->temporary = 1;
2085     atom->pos = atom_header;
2086     atom->last = atom_table;
2087 
2088     data = &trak->stts_data_buf;
2089     data->temporary = 1;
2090     data->pos = atom_table;
2091     data->last = atom_end;
2092 
2093     trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;
2094     trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;
2095 
2096     ngx_mp4_atom_next(mp4, atom_data_size);
2097 
2098     return NGX_OK;
2099 }
2100 
2101 
2102 static ngx_int_t
2103 ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
2104     ngx_http_mp4_trak_t *trak)
2105 {
2106     size_t                atom_size;
2107     ngx_buf_t            *atom, *data;
2108     ngx_mp4_stts_atom_t  *stts_atom;
2109 
2110     /*
2111      * mdia.minf.stbl.stts updating requires trak->timescale
2112      * from mdia.mdhd atom which may reside after mdia.minf
2113      */
2114 
2115     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2116                    "mp4 stts atom update");
2117 
2118     data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
2119 
2120     if (data == NULL) {
2121         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2122                       "no mp4 stts atoms were found in \"%s\"",
2123                       mp4->file.name.data);
2124         return NGX_ERROR;
2125     }
2126 
2127     if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {
2128         return NGX_ERROR;
2129     }
2130 
2131     if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {
2132         return NGX_ERROR;
2133     }
2134 
2135     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2136                    "time-to-sample entries:%uD", trak->time_to_sample_entries);
2137 
2138     atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
2139     trak->size += atom_size;
2140 
2141     atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
2142     stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
2143     ngx_mp4_set_32value(stts_atom->size, atom_size);
2144     ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);
2145 
2146     return NGX_OK;
2147 }
2148 
2149 
2150 static ngx_int_t
2151 ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
2152     ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2153 {
2154     uint32_t               count, duration, rest;
2155     uint64_t               start_time;
2156     ngx_buf_t             *data;
2157     ngx_uint_t             start_sample, entries, start_sec;
2158     ngx_mp4_stts_entry_t  *entry, *end;
2159 
2160     if (start) {
2161         start_sec = mp4->start;
2162 
2163         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2164                        "mp4 stts crop start_time:%ui", start_sec);
2165 
2166     } else if (mp4->length) {
2167         start_sec = mp4->length;
2168 
2169         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2170                        "mp4 stts crop end_time:%ui", start_sec);
2171 
2172     } else {
2173         return NGX_OK;
2174     }
2175 
2176     data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
2177 
2178     start_time = (uint64_t) start_sec * trak->timescale / 1000;
2179 
2180     entries = trak->time_to_sample_entries;
2181     start_sample = 0;
2182     entry = (ngx_mp4_stts_entry_t *) data->pos;
2183     end = (ngx_mp4_stts_entry_t *) data->last;
2184 
2185     while (entry < end) {
2186         count = ngx_mp4_get_32value(entry->count);
2187         duration = ngx_mp4_get_32value(entry->duration);
2188 
2189         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2190                        "time:%uL, count:%uD, duration:%uD",
2191                        start_time, count, duration);
2192 
2193         if (start_time < (uint64_t) count * duration) {
2194             start_sample += (ngx_uint_t) (start_time / duration);
2195             rest = (uint32_t) (start_time / duration);
2196             goto found;
2197         }
2198 
2199         start_sample += count;
2200         start_time -= count * duration;
2201         entries--;
2202         entry++;
2203     }
2204 
2205     if (start) {
2206         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2207                       "start time is out mp4 stts samples in \"%s\"",
2208                       mp4->file.name.data);
2209 
2210         return NGX_ERROR;
2211 
2212     } else {
2213         trak->end_sample = trak->start_sample + start_sample;
2214 
2215         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2216                        "end_sample:%ui", trak->end_sample);
2217 
2218         return NGX_OK;
2219     }
2220 
2221 found:
2222 
2223     if (start) {
2224         ngx_mp4_set_32value(entry->count, count - rest);
2225         data->pos = (u_char *) entry;
2226         trak->time_to_sample_entries = entries;
2227         trak->start_sample = start_sample;
2228 
2229         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2230                        "start_sample:%ui, new count:%uD",
2231                        trak->start_sample, count - rest);
2232 
2233     } else {
2234         ngx_mp4_set_32value(entry->count, rest);
2235         data->last = (u_char *) (entry + 1);
2236         trak->time_to_sample_entries -= entries - 1;
2237         trak->end_sample = trak->start_sample + start_sample;
2238 
2239         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2240                        "end_sample:%ui, new count:%uD",
2241                        trak->end_sample, rest);
2242     }
2243 
2244     return NGX_OK;
2245 }
2246 
2247 
2248 typedef struct {
2249     u_char    size[4];
2250     u_char    name[4];
2251     u_char    version[1];
2252     u_char    flags[3];
2253     u_char    entries[4];
2254 } ngx_http_mp4_stss_atom_t;
2255 
2256 
2257 static ngx_int_t
2258 ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2259 {
2260     u_char                    *atom_header, *atom_table, *atom_end;
2261     uint32_t                   entries;
2262     ngx_buf_t                 *atom, *data;
2263     ngx_http_mp4_trak_t       *trak;
2264     ngx_http_mp4_stss_atom_t  *stss_atom;
2265 
2266     /* sync samples atom */
2267 
2268     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");
2269 
2270     atom_header = ngx_mp4_atom_header(mp4);
2271     stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;
2272     ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');
2273 
2274     if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {
2275         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2276                       "\"%s\" mp4 stss atom too small", mp4->file.name.data);
2277         return NGX_ERROR;
2278     }
2279 
2280     entries = ngx_mp4_get_32value(stss_atom->entries);
2281 
2282     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2283                    "sync sample entries:%uD", entries);
2284 
2285     trak = ngx_mp4_last_trak(mp4);
2286     trak->sync_samples_entries = entries;
2287 
2288     atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);
2289 
2290     atom = &trak->stss_atom_buf;
2291     atom->temporary = 1;
2292     atom->pos = atom_header;
2293     atom->last = atom_table;
2294 
2295     if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)
2296         + entries * sizeof(uint32_t) > atom_data_size)
2297     {
2298         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2299                       "\"%s\" mp4 stss atom too small", mp4->file.name.data);
2300         return NGX_ERROR;
2301     }
2302 
2303     atom_end = atom_table + entries * sizeof(uint32_t);
2304 
2305     data = &trak->stss_data_buf;
2306     data->temporary = 1;
2307     data->pos = atom_table;
2308     data->last = atom_end;
2309 
2310     trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;
2311     trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;
2312 
2313     ngx_mp4_atom_next(mp4, atom_data_size);
2314 
2315     return NGX_OK;
2316 }
2317 
2318 
2319 static ngx_int_t
2320 ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
2321     ngx_http_mp4_trak_t *trak)
2322 {
2323     size_t                     atom_size;
2324     uint32_t                   sample, start_sample, *entry, *end;
2325     ngx_buf_t                 *atom, *data;
2326     ngx_http_mp4_stss_atom_t  *stss_atom;
2327 
2328     /*
2329      * mdia.minf.stbl.stss updating requires trak->start_sample
2330      * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2331      * atom which may reside after mdia.minf
2332      */
2333 
2334     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2335                    "mp4 stss atom update");
2336 
2337     data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
2338 
2339     if (data == NULL) {
2340         return NGX_OK;
2341     }
2342 
2343     ngx_http_mp4_crop_stss_data(mp4, trak, 1);
2344     ngx_http_mp4_crop_stss_data(mp4, trak, 0);
2345 
2346     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2347                    "sync sample entries:%uD", trak->sync_samples_entries);
2348 
2349     if (trak->sync_samples_entries) {
2350         entry = (uint32_t *) data->pos;
2351         end = (uint32_t *) data->last;
2352 
2353         start_sample = trak->start_sample;
2354 
2355         while (entry < end) {
2356             sample = ngx_mp4_get_32value(entry);
2357             sample -= start_sample;
2358             ngx_mp4_set_32value(entry, sample);
2359             entry++;
2360         }
2361 
2362     } else {
2363         trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;
2364     }
2365 
2366     atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
2367     trak->size += atom_size;
2368 
2369     atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
2370     stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
2371 
2372     ngx_mp4_set_32value(stss_atom->size, atom_size);
2373     ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);
2374 
2375     return NGX_OK;
2376 }
2377 
2378 
2379 static void
2380 ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
2381     ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2382 {
2383     uint32_t     sample, start_sample, *entry, *end;
2384     ngx_buf_t   *data;
2385     ngx_uint_t   entries;
2386 
2387     /* sync samples starts from 1 */
2388 
2389     if (start) {
2390         start_sample = trak->start_sample + 1;
2391 
2392         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2393                        "mp4 stss crop start_sample:%uD", start_sample);
2394 
2395     } else if (mp4->length) {
2396         start_sample = trak->end_sample + 1;
2397 
2398         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2399                        "mp4 stss crop end_sample:%uD", start_sample);
2400 
2401     } else {
2402         return;
2403     }
2404 
2405     data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
2406 
2407     entries = trak->sync_samples_entries;
2408     entry = (uint32_t *) data->pos;
2409     end = (uint32_t *) data->last;
2410 
2411     while (entry < end) {
2412         sample = ngx_mp4_get_32value(entry);
2413 
2414         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2415                        "sync:%uD", sample);
2416 
2417         if (sample >= start_sample) {
2418             goto found;
2419         }
2420 
2421         entries--;
2422         entry++;
2423     }
2424 
2425     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2426                    "sample is out of mp4 stss atom");
2427 
2428 found:
2429 
2430     if (start) {
2431         data->pos = (u_char *) entry;
2432         trak->sync_samples_entries = entries;
2433 
2434     } else {
2435         data->last = (u_char *) entry;
2436         trak->sync_samples_entries -= entries;
2437     }
2438 }
2439 
2440 
2441 typedef struct {
2442     u_char    size[4];
2443     u_char    name[4];
2444     u_char    version[1];
2445     u_char    flags[3];
2446     u_char    entries[4];
2447 } ngx_mp4_ctts_atom_t;
2448 
2449 typedef struct {
2450     u_char    count[4];
2451     u_char    offset[4];
2452 } ngx_mp4_ctts_entry_t;
2453 
2454 
2455 static ngx_int_t
2456 ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2457 {
2458     u_char               *atom_header, *atom_table, *atom_end;
2459     uint32_t              entries;
2460     ngx_buf_t            *atom, *data;
2461     ngx_mp4_ctts_atom_t  *ctts_atom;
2462     ngx_http_mp4_trak_t  *trak;
2463 
2464     /* composition offsets atom */
2465 
2466     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");
2467 
2468     atom_header = ngx_mp4_atom_header(mp4);
2469     ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;
2470     ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');
2471 
2472     if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {
2473         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2474                       "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
2475         return NGX_ERROR;
2476     }
2477 
2478     entries = ngx_mp4_get_32value(ctts_atom->entries);
2479 
2480     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2481                    "composition offset entries:%uD", entries);
2482 
2483     trak = ngx_mp4_last_trak(mp4);
2484     trak->composition_offset_entries = entries;
2485 
2486     atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);
2487 
2488     atom = &trak->ctts_atom_buf;
2489     atom->temporary = 1;
2490     atom->pos = atom_header;
2491     atom->last = atom_table;
2492 
2493     if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)
2494         + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)
2495     {
2496         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2497                       "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
2498         return NGX_ERROR;
2499     }
2500 
2501     atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);
2502 
2503     data = &trak->ctts_data_buf;
2504     data->temporary = 1;
2505     data->pos = atom_table;
2506     data->last = atom_end;
2507 
2508     trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;
2509     trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;
2510 
2511     ngx_mp4_atom_next(mp4, atom_data_size);
2512 
2513     return NGX_OK;
2514 }
2515 
2516 
2517 static void
2518 ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2519     ngx_http_mp4_trak_t *trak)
2520 {
2521     size_t                atom_size;
2522     ngx_buf_t            *atom, *data;
2523     ngx_mp4_ctts_atom_t  *ctts_atom;
2524 
2525     /*
2526      * mdia.minf.stbl.ctts updating requires trak->start_sample
2527      * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2528      * atom which may reside after mdia.minf
2529      */
2530 
2531     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2532                    "mp4 ctts atom update");
2533 
2534     data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
2535 
2536     if (data == NULL) {
2537         return;
2538     }
2539 
2540     ngx_http_mp4_crop_ctts_data(mp4, trak, 1);
2541     ngx_http_mp4_crop_ctts_data(mp4, trak, 0);
2542 
2543     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2544                    "composition offset entries:%uD",
2545                    trak->composition_offset_entries);
2546 
2547     if (trak->composition_offset_entries == 0) {
2548         trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
2549         trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
2550         return;
2551     }
2552 
2553     atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
2554     trak->size += atom_size;
2555 
2556     atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
2557     ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
2558 
2559     ngx_mp4_set_32value(ctts_atom->size, atom_size);
2560     ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);
2561 
2562     return;
2563 }
2564 
2565 
2566 static void
2567 ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
2568     ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2569 {
2570     uint32_t               count, start_sample, rest;
2571     ngx_buf_t             *data;
2572     ngx_uint_t             entries;
2573     ngx_mp4_ctts_entry_t  *entry, *end;
2574 
2575     /* sync samples starts from 1 */
2576 
2577     if (start) {
2578         start_sample = trak->start_sample + 1;
2579 
2580         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2581                        "mp4 ctts crop start_sample:%uD", start_sample);
2582 
2583     } else if (mp4->length) {
2584         start_sample = trak->end_sample - trak->start_sample + 1;
2585 
2586         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2587                        "mp4 ctts crop end_sample:%uD", start_sample);
2588 
2589     } else {
2590         return;
2591     }
2592 
2593     data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
2594 
2595     entries = trak->composition_offset_entries;
2596     entry = (ngx_mp4_ctts_entry_t *) data->pos;
2597     end = (ngx_mp4_ctts_entry_t *) data->last;
2598 
2599     while (entry < end) {
2600         count = ngx_mp4_get_32value(entry->count);
2601 
2602         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2603                        "sample:%uD, count:%uD, offset:%uD",
2604                        start_sample, count, ngx_mp4_get_32value(entry->offset));
2605 
2606         if (start_sample <= count) {
2607             rest = start_sample - 1;
2608             goto found;
2609         }
2610 
2611         start_sample -= count;
2612         entries--;
2613         entry++;
2614     }
2615 
2616     if (start) {
2617         data->pos = (u_char *) end;
2618         trak->composition_offset_entries = 0;
2619     }
2620 
2621     return;
2622 
2623 found:
2624 
2625     if (start) {
2626         ngx_mp4_set_32value(entry->count, count - rest);
2627         data->pos = (u_char *) entry;
2628         trak->composition_offset_entries = entries;
2629 
2630     } else {
2631         ngx_mp4_set_32value(entry->count, rest);
2632         data->last = (u_char *) (entry + 1);
2633         trak->composition_offset_entries -= entries - 1;
2634     }
2635 }
2636 
2637 
2638 typedef struct {
2639     u_char    size[4];
2640     u_char    name[4];
2641     u_char    version[1];
2642     u_char    flags[3];
2643     u_char    entries[4];
2644 } ngx_mp4_stsc_atom_t;
2645 
2646 
2647 static ngx_int_t
2648 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2649 {
2650     u_char               *atom_header, *atom_table, *atom_end;
2651     uint32_t              entries;
2652     ngx_buf_t            *atom, *data;
2653     ngx_mp4_stsc_atom_t  *stsc_atom;
2654     ngx_http_mp4_trak_t  *trak;
2655 
2656     /* sample-to-chunk atom */
2657 
2658     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");
2659 
2660     atom_header = ngx_mp4_atom_header(mp4);
2661     stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;
2662     ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');
2663 
2664     if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {
2665         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2666                       "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
2667         return NGX_ERROR;
2668     }
2669 
2670     entries = ngx_mp4_get_32value(stsc_atom->entries);
2671 
2672     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2673                    "sample-to-chunk entries:%uD", entries);
2674 
2675     if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)
2676         + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)
2677     {
2678         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2679                       "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
2680         return NGX_ERROR;
2681     }
2682 
2683     atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);
2684     atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);
2685 
2686     trak = ngx_mp4_last_trak(mp4);
2687     trak->sample_to_chunk_entries = entries;
2688 
2689     atom = &trak->stsc_atom_buf;
2690     atom->temporary = 1;
2691     atom->pos = atom_header;
2692     atom->last = atom_table;
2693 
2694     data = &trak->stsc_data_buf;
2695     data->temporary = 1;
2696     data->pos = atom_table;
2697     data->last = atom_end;
2698 
2699     trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;
2700     trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;
2701 
2702     ngx_mp4_atom_next(mp4, atom_data_size);
2703 
2704     return NGX_OK;
2705 }
2706 
2707 
2708 static ngx_int_t
2709 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2710     ngx_http_mp4_trak_t *trak)
2711 {
2712     size_t                 atom_size;
2713     uint32_t               chunk;
2714     ngx_buf_t             *atom, *data;
2715     ngx_mp4_stsc_atom_t   *stsc_atom;
2716     ngx_mp4_stsc_entry_t  *entry, *end;
2717 
2718     /*
2719      * mdia.minf.stbl.stsc updating requires trak->start_sample
2720      * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
2721      * atom which may reside after mdia.minf
2722      */
2723 
2724     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2725                    "mp4 stsc atom update");
2726 
2727     data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
2728 
2729     if (data == NULL) {
2730         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2731                       "no mp4 stsc atoms were found in \"%s\"",
2732                       mp4->file.name.data);
2733         return NGX_ERROR;
2734     }
2735 
2736     if (trak->sample_to_chunk_entries == 0) {
2737         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2738                       "zero number of entries in stsc atom in \"%s\"",
2739                       mp4->file.name.data);
2740         return NGX_ERROR;
2741     }
2742 
2743     if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {
2744         return NGX_ERROR;
2745     }
2746 
2747     if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {
2748         return NGX_ERROR;
2749     }
2750 
2751     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2752                    "sample-to-chunk entries:%uD",
2753                    trak->sample_to_chunk_entries);
2754 
2755     entry = (ngx_mp4_stsc_entry_t *) data->pos;
2756     end = (ngx_mp4_stsc_entry_t *) data->last;
2757 
2758     while (entry < end) {
2759         chunk = ngx_mp4_get_32value(entry->chunk);
2760         chunk -= trak->start_chunk;
2761         ngx_mp4_set_32value(entry->chunk, chunk);
2762         entry++;
2763     }
2764 
2765     atom_size = sizeof(ngx_mp4_stsc_atom_t)
2766                 + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);
2767 
2768     trak->size += atom_size;
2769 
2770     atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
2771     stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
2772 
2773     ngx_mp4_set_32value(stsc_atom->size, atom_size);
2774     ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);
2775 
2776     return NGX_OK;
2777 }
2778 
2779 
2780 static ngx_int_t
2781 ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
2782     ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2783 {
2784     uint32_t               start_sample, chunk, samples, id, next_chunk, n,
2785                            prev_samples;
2786     ngx_buf_t             *data, *buf;
2787     ngx_uint_t             entries, target_chunk, chunk_samples;
2788     ngx_mp4_stsc_entry_t  *entry, *end, *first;
2789 
2790     entries = trak->sample_to_chunk_entries - 1;
2791 
2792     if (start) {
2793         start_sample = (uint32_t) trak->start_sample;
2794 
2795         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2796                        "mp4 stsc crop start_sample:%uD", start_sample);
2797 
2798     } else if (mp4->length) {
2799         start_sample = (uint32_t) (trak->end_sample - trak->start_sample);
2800         samples = 0;
2801 
2802         data = trak->out[NGX_HTTP_MP4_STSC_START].buf;
2803 
2804         if (data) {
2805             entry = (ngx_mp4_stsc_entry_t *) data->pos;
2806             samples = ngx_mp4_get_32value(entry->samples);
2807             entries--;
2808 
2809             if (samples > start_sample) {
2810                 samples = start_sample;
2811                 ngx_mp4_set_32value(entry->samples, samples);
2812             }
2813 
2814             start_sample -= samples;
2815         }
2816 
2817         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2818                        "mp4 stsc crop end_sample:%uD, ext_samples:%uD",
2819                        start_sample, samples);
2820 
2821     } else {
2822         return NGX_OK;
2823     }
2824 
2825     data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
2826 
2827     entry = (ngx_mp4_stsc_entry_t *) data->pos;
2828     end = (ngx_mp4_stsc_entry_t *) data->last;
2829 
2830     chunk = ngx_mp4_get_32value(entry->chunk);
2831     samples = ngx_mp4_get_32value(entry->samples);
2832     id = ngx_mp4_get_32value(entry->id);
2833     prev_samples = 0;
2834     entry++;
2835 
2836     while (entry < end) {
2837 
2838         next_chunk = ngx_mp4_get_32value(entry->chunk);
2839 
2840         ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2841                        "sample:%uD, chunk:%uD, chunks:%uD, "
2842                        "samples:%uD, id:%uD",
2843                        start_sample, chunk, next_chunk - chunk, samples, id);
2844 
2845         n = (next_chunk - chunk) * samples;
2846 
2847         if (start_sample < n) {
2848             goto found;
2849         }
2850 
2851         start_sample -= n;
2852 
2853         prev_samples = samples;
2854         chunk = next_chunk;
2855         samples = ngx_mp4_get_32value(entry->samples);
2856         id = ngx_mp4_get_32value(entry->id);
2857         entries--;
2858         entry++;
2859     }
2860 
2861     next_chunk = trak->chunks + 1;
2862 
2863     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2864                    "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
2865                    start_sample, chunk, next_chunk - chunk, samples);
2866 
2867     n = (next_chunk - chunk) * samples;
2868 
2869     if (start_sample > n) {
2870         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2871                       "%s time is out mp4 stsc chunks in \"%s\"",
2872                       start ? "start" : "end", mp4->file.name.data);
2873         return NGX_ERROR;
2874     }
2875 
2876 found:
2877 
2878     entries++;
2879     entry--;
2880 
2881     if (samples == 0) {
2882         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2883                       "zero number of samples in \"%s\"",
2884                       mp4->file.name.data);
2885         return NGX_ERROR;
2886     }
2887 
2888     target_chunk = chunk - 1;
2889     target_chunk += start_sample / samples;
2890     chunk_samples = start_sample % samples;
2891 
2892     if (start) {
2893         data->pos = (u_char *) entry;
2894 
2895         trak->sample_to_chunk_entries = entries;
2896         trak->start_chunk = target_chunk;
2897         trak->start_chunk_samples = chunk_samples;
2898 
2899         ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);
2900 
2901         samples -= chunk_samples;
2902 
2903         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2904                        "start_chunk:%ui, start_chunk_samples:%ui",
2905                        trak->start_chunk, trak->start_chunk_samples);
2906 
2907     } else {
2908         if (start_sample) {
2909             data->last = (u_char *) (entry + 1);
2910             trak->sample_to_chunk_entries -= entries - 1;
2911             trak->end_chunk_samples = samples;
2912 
2913         } else {
2914             data->last = (u_char *) entry;
2915             trak->sample_to_chunk_entries -= entries;
2916             trak->end_chunk_samples = prev_samples;
2917         }
2918 
2919         if (chunk_samples) {
2920             trak->end_chunk = target_chunk + 1;
2921             trak->end_chunk_samples = chunk_samples;
2922 
2923         } else {
2924             trak->end_chunk = target_chunk;
2925         }
2926 
2927         samples = chunk_samples;
2928         next_chunk = chunk + 1;
2929 
2930         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2931                        "end_chunk:%ui, end_chunk_samples:%ui",
2932                        trak->end_chunk, trak->end_chunk_samples);
2933     }
2934 
2935     if (chunk_samples && next_chunk - target_chunk == 2) {
2936 
2937         ngx_mp4_set_32value(entry->samples, samples);
2938 
2939     } else if (chunk_samples && start) {
2940 
2941         first = &trak->stsc_start_chunk_entry;
2942         ngx_mp4_set_32value(first->chunk, 1);
2943         ngx_mp4_set_32value(first->samples, samples);
2944         ngx_mp4_set_32value(first->id, id);
2945 
2946         buf = &trak->stsc_start_chunk_buf;
2947         buf->temporary = 1;
2948         buf->pos = (u_char *) first;
2949         buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2950 
2951         trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;
2952 
2953         ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);
2954 
2955         trak->sample_to_chunk_entries++;
2956 
2957     } else if (chunk_samples) {
2958 
2959         first = &trak->stsc_end_chunk_entry;
2960         ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);
2961         ngx_mp4_set_32value(first->samples, samples);
2962         ngx_mp4_set_32value(first->id, id);
2963 
2964         buf = &trak->stsc_end_chunk_buf;
2965         buf->temporary = 1;
2966         buf->pos = (u_char *) first;
2967         buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2968 
2969         trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;
2970 
2971         trak->sample_to_chunk_entries++;
2972     }
2973 
2974     return NGX_OK;
2975 }
2976 
2977 
2978 typedef struct {
2979     u_char    size[4];
2980     u_char    name[4];
2981     u_char    version[1];
2982     u_char    flags[3];
2983     u_char    uniform_size[4];
2984     u_char    entries[4];
2985 } ngx_mp4_stsz_atom_t;
2986 
2987 
2988 static ngx_int_t
2989 ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2990 {
2991     u_char               *atom_header, *atom_table, *atom_end;
2992     size_t                atom_size;
2993     uint32_t              entries, size;
2994     ngx_buf_t            *atom, *data;
2995     ngx_mp4_stsz_atom_t  *stsz_atom;
2996     ngx_http_mp4_trak_t  *trak;
2997 
2998     /* sample sizes atom */
2999 
3000     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");
3001 
3002     atom_header = ngx_mp4_atom_header(mp4);
3003     stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;
3004     ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');
3005 
3006     if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {
3007         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3008                       "\"%s\" mp4 stsz atom too small", mp4->file.name.data);
3009         return NGX_ERROR;
3010     }
3011 
3012     size = ngx_mp4_get_32value(stsz_atom->uniform_size);
3013     entries = ngx_mp4_get_32value(stsz_atom->entries);
3014 
3015     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3016                    "sample uniform size:%uD, entries:%uD", size, entries);
3017 
3018     trak = ngx_mp4_last_trak(mp4);
3019     trak->sample_sizes_entries = entries;
3020 
3021     atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);
3022 
3023     atom = &trak->stsz_atom_buf;
3024     atom->temporary = 1;
3025     atom->pos = atom_header;
3026     atom->last = atom_table;
3027 
3028     trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;
3029 
3030     if (size == 0) {
3031         if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)
3032             + entries * sizeof(uint32_t) > atom_data_size)
3033         {
3034             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3035                           "\"%s\" mp4 stsz atom too small",
3036                           mp4->file.name.data);
3037             return NGX_ERROR;
3038         }
3039 
3040         atom_end = atom_table + entries * sizeof(uint32_t);
3041 
3042         data = &trak->stsz_data_buf;
3043         data->temporary = 1;
3044         data->pos = atom_table;
3045         data->last = atom_end;
3046 
3047         trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
3048 
3049     } else {
3050         /* if size != 0 then all samples are the same size */
3051         /* TODO : chunk samples */
3052         atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
3053         ngx_mp4_set_32value(atom_header, atom_size);
3054         trak->size += atom_size;
3055     }
3056 
3057     ngx_mp4_atom_next(mp4, atom_data_size);
3058 
3059     return NGX_OK;
3060 }
3061 
3062 
3063 static ngx_int_t
3064 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
3065     ngx_http_mp4_trak_t *trak)
3066 {
3067     size_t                atom_size;
3068     uint32_t             *pos, *end, entries;
3069     ngx_buf_t            *atom, *data;
3070     ngx_mp4_stsz_atom_t  *stsz_atom;
3071 
3072     /*
3073      * mdia.minf.stbl.stsz updating requires trak->start_sample
3074      * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
3075      * atom which may reside after mdia.minf
3076      */
3077 
3078     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3079                    "mp4 stsz atom update");
3080 
3081     data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
3082 
3083     if (data) {
3084         entries = trak->sample_sizes_entries;
3085 
3086         if (trak->start_sample > entries) {
3087             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3088                           "start time is out mp4 stsz samples in \"%s\"",
3089                           mp4->file.name.data);
3090             return NGX_ERROR;
3091         }
3092 
3093         entries -= trak->start_sample;
3094         data->pos += trak->start_sample * sizeof(uint32_t);
3095         end = (uint32_t *) data->pos;
3096 
3097         for (pos = end - trak->start_chunk_samples; pos < end; pos++) {
3098             trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);
3099         }
3100 
3101         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3102                        "chunk samples sizes:%uL",
3103                        trak->start_chunk_samples_size);
3104 
3105         if (mp4->length) {
3106             if (trak->end_sample - trak->start_sample > entries) {
3107                 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3108                               "end time is out mp4 stsz samples in \"%s\"",
3109                               mp4->file.name.data);
3110                 return NGX_ERROR;
3111             }
3112 
3113             entries = trak->end_sample - trak->start_sample;
3114             data->last = data->pos + entries * sizeof(uint32_t);
3115             end = (uint32_t *) data->last;
3116 
3117             for (pos = end - trak->end_chunk_samples; pos < end; pos++) {
3118                 trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);
3119             }
3120 
3121             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3122                            "mp4 stsz end_chunk_samples_size:%uL",
3123                            trak->end_chunk_samples_size);
3124         }
3125 
3126         atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
3127         trak->size += atom_size;
3128 
3129         atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
3130         stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
3131 
3132         ngx_mp4_set_32value(stsz_atom->size, atom_size);
3133         ngx_mp4_set_32value(stsz_atom->entries, entries);
3134     }
3135 
3136     return NGX_OK;
3137 }
3138 
3139 
3140 typedef struct {
3141     u_char    size[4];
3142     u_char    name[4];
3143     u_char    version[1];
3144     u_char    flags[3];
3145     u_char    entries[4];
3146 } ngx_mp4_stco_atom_t;
3147 
3148 
3149 static ngx_int_t
3150 ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
3151 {
3152     u_char               *atom_header, *atom_table, *atom_end;
3153     uint32_t              entries;
3154     ngx_buf_t            *atom, *data;
3155     ngx_mp4_stco_atom_t  *stco_atom;
3156     ngx_http_mp4_trak_t  *trak;
3157 
3158     /* chunk offsets atom */
3159 
3160     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");
3161 
3162     atom_header = ngx_mp4_atom_header(mp4);
3163     stco_atom = (ngx_mp4_stco_atom_t *) atom_header;
3164     ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');
3165 
3166     if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {
3167         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3168                       "\"%s\" mp4 stco atom too small", mp4->file.name.data);
3169         return NGX_ERROR;
3170     }
3171 
3172     entries = ngx_mp4_get_32value(stco_atom->entries);
3173 
3174     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
3175 
3176     if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)
3177         + entries * sizeof(uint32_t) > atom_data_size)
3178     {
3179         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3180                       "\"%s\" mp4 stco atom too small", mp4->file.name.data);
3181         return NGX_ERROR;
3182     }
3183 
3184     atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);
3185     atom_end = atom_table + entries * sizeof(uint32_t);
3186 
3187     trak = ngx_mp4_last_trak(mp4);
3188     trak->chunks = entries;
3189 
3190     atom = &trak->stco_atom_buf;
3191     atom->temporary = 1;
3192     atom->pos = atom_header;
3193     atom->last = atom_table;
3194 
3195     data = &trak->stco_data_buf;
3196     data->temporary = 1;
3197     data->pos = atom_table;
3198     data->last = atom_end;
3199 
3200     trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
3201     trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;
3202 
3203     ngx_mp4_atom_next(mp4, atom_data_size);
3204 
3205     return NGX_OK;
3206 }
3207 
3208 
3209 static ngx_int_t
3210 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
3211     ngx_http_mp4_trak_t *trak)
3212 {
3213     size_t                atom_size;
3214     uint32_t              entries;
3215     ngx_buf_t            *atom, *data;
3216     ngx_mp4_stco_atom_t  *stco_atom;
3217 
3218     /*
3219      * mdia.minf.stbl.stco updating requires trak->start_chunk
3220      * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
3221      * atom which may reside after mdia.minf
3222      */
3223 
3224     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3225                    "mp4 stco atom update");
3226 
3227     data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
3228 
3229     if (data == NULL) {
3230         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3231                       "no mp4 stco atoms were found in \"%s\"",
3232                       mp4->file.name.data);
3233         return NGX_ERROR;
3234     }
3235 
3236     if (trak->start_chunk > trak->chunks) {
3237         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3238                       "start time is out mp4 stco chunks in \"%s\"",
3239                       mp4->file.name.data);
3240         return NGX_ERROR;
3241     }
3242 
3243     data->pos += trak->start_chunk * sizeof(uint32_t);
3244 
3245     trak->start_offset = ngx_mp4_get_32value(data->pos);
3246     trak->start_offset += trak->start_chunk_samples_size;
3247     ngx_mp4_set_32value(data->pos, trak->start_offset);
3248 
3249     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3250                    "start chunk offset:%O", trak->start_offset);
3251 
3252     if (mp4->length) {
3253 
3254         if (trak->end_chunk > trak->chunks) {
3255             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3256                           "end time is out mp4 stco chunks in \"%s\"",
3257                           mp4->file.name.data);
3258             return NGX_ERROR;
3259         }
3260 
3261         entries = trak->end_chunk - trak->start_chunk;
3262         data->last = data->pos + entries * sizeof(uint32_t);
3263 
3264         if (entries) {
3265             trak->end_offset =
3266                             ngx_mp4_get_32value(data->last - sizeof(uint32_t));
3267             trak->end_offset += trak->end_chunk_samples_size;
3268 
3269             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3270                            "end chunk offset:%O", trak->end_offset);
3271         }
3272 
3273     } else {
3274         entries = trak->chunks - trak->start_chunk;
3275         trak->end_offset = mp4->mdat_data.buf->file_last;
3276     }
3277 
3278     if (entries == 0) {
3279         trak->start_offset = mp4->end;
3280         trak->end_offset = 0;
3281     }
3282 
3283     atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
3284     trak->size += atom_size;
3285 
3286     atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
3287     stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
3288 
3289     ngx_mp4_set_32value(stco_atom->size, atom_size);
3290     ngx_mp4_set_32value(stco_atom->entries, entries);
3291 
3292     return NGX_OK;
3293 }
3294 
3295 
3296 static void
3297 ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
3298     ngx_http_mp4_trak_t *trak, int32_t adjustment)
3299 {
3300     uint32_t    offset, *entry, *end;
3301     ngx_buf_t  *data;
3302 
3303     /*
3304      * moov.trak.mdia.minf.stbl.stco adjustment requires
3305      * minimal start offset of all traks and new moov atom size
3306      */
3307 
3308     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3309                    "mp4 stco atom adjustment");
3310 
3311     data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
3312     entry = (uint32_t *) data->pos;
3313     end = (uint32_t *) data->last;
3314 
3315     while (entry < end) {
3316         offset = ngx_mp4_get_32value(entry);
3317         offset += adjustment;
3318         ngx_mp4_set_32value(entry, offset);
3319         entry++;
3320     }
3321 }
3322 
3323 
3324 typedef struct {
3325     u_char    size[4];
3326     u_char    name[4];
3327     u_char    version[1];
3328     u_char    flags[3];
3329     u_char    entries[4];
3330 } ngx_mp4_co64_atom_t;
3331 
3332 
3333 static ngx_int_t
3334 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
3335 {
3336     u_char               *atom_header, *atom_table, *atom_end;
3337     uint32_t              entries;
3338     ngx_buf_t            *atom, *data;
3339     ngx_mp4_co64_atom_t  *co64_atom;
3340     ngx_http_mp4_trak_t  *trak;
3341 
3342     /* chunk offsets atom */
3343 
3344     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
3345 
3346     atom_header = ngx_mp4_atom_header(mp4);
3347     co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
3348     ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
3349 
3350     if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {
3351         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3352                       "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
3353         return NGX_ERROR;
3354     }
3355 
3356     entries = ngx_mp4_get_32value(co64_atom->entries);
3357 
3358     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
3359 
3360     if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)
3361         + entries * sizeof(uint64_t) > atom_data_size)
3362     {
3363         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3364                       "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
3365         return NGX_ERROR;
3366     }
3367 
3368     atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
3369     atom_end = atom_table + entries * sizeof(uint64_t);
3370 
3371     trak = ngx_mp4_last_trak(mp4);
3372     trak->chunks = entries;
3373 
3374     atom = &trak->co64_atom_buf;
3375     atom->temporary = 1;
3376     atom->pos = atom_header;
3377     atom->last = atom_table;
3378 
3379     data = &trak->co64_data_buf;
3380     data->temporary = 1;
3381     data->pos = atom_table;
3382     data->last = atom_end;
3383 
3384     trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
3385     trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
3386 
3387     ngx_mp4_atom_next(mp4, atom_data_size);
3388 
3389     return NGX_OK;
3390 }
3391 
3392 
3393 static ngx_int_t
3394 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
3395     ngx_http_mp4_trak_t *trak)
3396 {
3397     size_t                atom_size;
3398     uint64_t              entries;
3399     ngx_buf_t            *atom, *data;
3400     ngx_mp4_co64_atom_t  *co64_atom;
3401 
3402     /*
3403      * mdia.minf.stbl.co64 updating requires trak->start_chunk
3404      * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
3405      * atom which may reside after mdia.minf
3406      */
3407 
3408     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3409                    "mp4 co64 atom update");
3410 
3411     data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
3412 
3413     if (data == NULL) {
3414         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3415                       "no mp4 co64 atoms were found in \"%s\"",
3416                       mp4->file.name.data);
3417         return NGX_ERROR;
3418     }
3419 
3420     if (trak->start_chunk > trak->chunks) {
3421         ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3422                       "start time is out mp4 co64 chunks in \"%s\"",
3423                       mp4->file.name.data);
3424         return NGX_ERROR;
3425     }
3426 
3427     data->pos += trak->start_chunk * sizeof(uint64_t);
3428 
3429     trak->start_offset = ngx_mp4_get_64value(data->pos);
3430     trak->start_offset += trak->start_chunk_samples_size;
3431     ngx_mp4_set_64value(data->pos, trak->start_offset);
3432 
3433     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3434                    "start chunk offset:%O", trak->start_offset);
3435 
3436     if (mp4->length) {
3437 
3438         if (trak->end_chunk > trak->chunks) {
3439             ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3440                           "end time is out mp4 co64 chunks in \"%s\"",
3441                           mp4->file.name.data);
3442             return NGX_ERROR;
3443         }
3444 
3445         entries = trak->end_chunk - trak->start_chunk;
3446         data->last = data->pos + entries * sizeof(uint64_t);
3447 
3448         if (entries) {
3449             trak->end_offset =
3450                             ngx_mp4_get_64value(data->last - sizeof(uint64_t));
3451             trak->end_offset += trak->end_chunk_samples_size;
3452 
3453             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3454                            "end chunk offset:%O", trak->end_offset);
3455         }
3456 
3457     } else {
3458         entries = trak->chunks - trak->start_chunk;
3459         trak->end_offset = mp4->mdat_data.buf->file_last;
3460     }
3461 
3462     if (entries == 0) {
3463         trak->start_offset = mp4->end;
3464         trak->end_offset = 0;
3465     }
3466 
3467     atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
3468     trak->size += atom_size;
3469 
3470     atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
3471     co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
3472 
3473     ngx_mp4_set_32value(co64_atom->size, atom_size);
3474     ngx_mp4_set_32value(co64_atom->entries, entries);
3475 
3476     return NGX_OK;
3477 }
3478 
3479 
3480 static void
3481 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
3482     ngx_http_mp4_trak_t *trak, off_t adjustment)
3483 {
3484     uint64_t    offset, *entry, *end;
3485     ngx_buf_t  *data;
3486 
3487     /*
3488      * moov.trak.mdia.minf.stbl.co64 adjustment requires
3489      * minimal start offset of all traks and new moov atom size
3490      */
3491 
3492     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3493                    "mp4 co64 atom adjustment");
3494 
3495     data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
3496     entry = (uint64_t *) data->pos;
3497     end = (uint64_t *) data->last;
3498 
3499     while (entry < end) {
3500         offset = ngx_mp4_get_64value(entry);
3501         offset += adjustment;
3502         ngx_mp4_set_64value(entry, offset);
3503         entry++;
3504     }
3505 }
3506 
3507 
3508 static char *
3509 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3510 {
3511     ngx_http_core_loc_conf_t  *clcf;
3512 
3513     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
3514     clcf->handler = ngx_http_mp4_handler;
3515 
3516     return NGX_CONF_OK;
3517 }
3518 
3519 
3520 static void *
3521 ngx_http_mp4_create_conf(ngx_conf_t *cf)
3522 {
3523     ngx_http_mp4_conf_t  *conf;
3524 
3525     conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));
3526     if (conf == NULL) {
3527         return NULL;
3528     }
3529 
3530     conf->buffer_size = NGX_CONF_UNSET_SIZE;
3531     conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
3532 
3533     return conf;
3534 }
3535 
3536 
3537 static char *
3538 ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
3539 {
3540     ngx_http_mp4_conf_t *prev = parent;
3541     ngx_http_mp4_conf_t *conf = child;
3542 
3543     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);
3544     ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,
3545                               10 * 1024 * 1024);
3546 
3547     return NGX_CONF_OK;
3548 }