Mercurial > hg > nginx
annotate src/http/modules/ngx_http_mp4_module.c @ 4377:726a1ca9b52b
Proxy: made proxy_pass with variables more consistent.
If proxy_pass was used with variables and there was no URI component,
nginx always used unparsed URI. This isn't consistent with "no variables"
case, where e.g. rewrites are applied even if there is no URI component.
Fix is to use the same logic in both cases, i.e. only use unparsed URI if
it's valid and request is the main one.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Fri, 23 Dec 2011 16:04:09 +0000 |
parents | 05f3174483de |
children | b4d54fa76853 |
rev | line source |
---|---|
4085 | 1 |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 #include <ngx_config.h> | |
7 #include <ngx_core.h> | |
8 #include <ngx_http.h> | |
9 | |
10 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
11 #define NGX_HTTP_MP4_TRAK_ATOM 0 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
12 #define NGX_HTTP_MP4_TKHD_ATOM 1 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
13 #define NGX_HTTP_MP4_MDIA_ATOM 2 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
14 #define NGX_HTTP_MP4_MDHD_ATOM 3 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
15 #define NGX_HTTP_MP4_HDLR_ATOM 4 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
16 #define NGX_HTTP_MP4_MINF_ATOM 5 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
17 #define NGX_HTTP_MP4_VMHD_ATOM 6 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
18 #define NGX_HTTP_MP4_SMHD_ATOM 7 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
19 #define NGX_HTTP_MP4_DINF_ATOM 8 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
20 #define NGX_HTTP_MP4_STBL_ATOM 9 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
21 #define NGX_HTTP_MP4_STSD_ATOM 10 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
22 #define NGX_HTTP_MP4_STTS_ATOM 11 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
23 #define NGX_HTTP_MP4_STTS_DATA 12 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
24 #define NGX_HTTP_MP4_STSS_ATOM 13 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
25 #define NGX_HTTP_MP4_STSS_DATA 14 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
26 #define NGX_HTTP_MP4_CTTS_ATOM 15 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
27 #define NGX_HTTP_MP4_CTTS_DATA 16 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
28 #define NGX_HTTP_MP4_STSC_ATOM 17 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
29 #define NGX_HTTP_MP4_STSC_CHUNK 18 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
30 #define NGX_HTTP_MP4_STSC_DATA 19 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
31 #define NGX_HTTP_MP4_STSZ_ATOM 20 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
32 #define NGX_HTTP_MP4_STSZ_DATA 21 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
33 #define NGX_HTTP_MP4_STCO_ATOM 22 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
34 #define NGX_HTTP_MP4_STCO_DATA 23 |
4112 | 35 #define NGX_HTTP_MP4_CO64_ATOM 24 |
36 #define NGX_HTTP_MP4_CO64_DATA 25 | |
37 | |
38 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA | |
4085 | 39 |
40 | |
41 typedef struct { | |
42 size_t buffer_size; | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
43 size_t max_buffer_size; |
4085 | 44 } ngx_http_mp4_conf_t; |
45 | |
46 | |
47 typedef struct { | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
48 u_char chunk[4]; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
49 u_char samples[4]; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
50 u_char id[4]; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
51 } ngx_mp4_stsc_entry_t; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
52 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
53 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
54 typedef struct { |
4085 | 55 uint32_t timescale; |
56 uint32_t time_to_sample_entries; | |
57 uint32_t sample_to_chunk_entries; | |
58 uint32_t sync_samples_entries; | |
59 uint32_t composition_offset_entries; | |
60 uint32_t sample_sizes_entries; | |
61 uint32_t chunks; | |
62 | |
63 ngx_uint_t start_sample; | |
64 ngx_uint_t start_chunk; | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
65 ngx_uint_t chunk_samples; |
4112 | 66 uint64_t chunk_samples_size; |
4085 | 67 off_t start_offset; |
68 | |
69 size_t tkhd_size; | |
70 size_t mdhd_size; | |
71 size_t hdlr_size; | |
72 size_t vmhd_size; | |
73 size_t smhd_size; | |
74 size_t dinf_size; | |
75 size_t size; | |
76 | |
77 ngx_chain_t out[NGX_HTTP_MP4_LAST_ATOM + 1]; | |
78 | |
79 ngx_buf_t trak_atom_buf; | |
80 ngx_buf_t tkhd_atom_buf; | |
81 ngx_buf_t mdia_atom_buf; | |
82 ngx_buf_t mdhd_atom_buf; | |
83 ngx_buf_t hdlr_atom_buf; | |
84 ngx_buf_t minf_atom_buf; | |
85 ngx_buf_t vmhd_atom_buf; | |
86 ngx_buf_t smhd_atom_buf; | |
87 ngx_buf_t dinf_atom_buf; | |
88 ngx_buf_t stbl_atom_buf; | |
89 ngx_buf_t stsd_atom_buf; | |
90 ngx_buf_t stts_atom_buf; | |
91 ngx_buf_t stts_data_buf; | |
92 ngx_buf_t stss_atom_buf; | |
93 ngx_buf_t stss_data_buf; | |
94 ngx_buf_t ctts_atom_buf; | |
95 ngx_buf_t ctts_data_buf; | |
96 ngx_buf_t stsc_atom_buf; | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
97 ngx_buf_t stsc_chunk_buf; |
4085 | 98 ngx_buf_t stsc_data_buf; |
99 ngx_buf_t stsz_atom_buf; | |
100 ngx_buf_t stsz_data_buf; | |
4107 | 101 ngx_buf_t stco_atom_buf; |
102 ngx_buf_t stco_data_buf; | |
4112 | 103 ngx_buf_t co64_atom_buf; |
104 ngx_buf_t co64_data_buf; | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
105 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
106 ngx_mp4_stsc_entry_t stsc_chunk_entry; |
4085 | 107 } ngx_http_mp4_trak_t; |
108 | |
109 | |
110 typedef struct { | |
111 ngx_file_t file; | |
112 | |
113 u_char *buffer; | |
114 u_char *buffer_start; | |
115 u_char *buffer_pos; | |
116 u_char *buffer_end; | |
117 size_t buffer_size; | |
118 | |
119 off_t offset; | |
120 off_t end; | |
121 off_t content_length; | |
122 ngx_uint_t start; | |
123 uint32_t timescale; | |
124 ngx_http_request_t *request; | |
125 ngx_array_t trak; | |
126 ngx_http_mp4_trak_t traks[2]; | |
127 | |
128 size_t ftyp_size; | |
129 size_t moov_size; | |
130 | |
131 ngx_chain_t *out; | |
132 ngx_chain_t ftyp_atom; | |
133 ngx_chain_t moov_atom; | |
134 ngx_chain_t mvhd_atom; | |
135 ngx_chain_t mdat_atom; | |
136 ngx_chain_t mdat_data; | |
137 | |
138 ngx_buf_t ftyp_atom_buf; | |
139 ngx_buf_t moov_atom_buf; | |
140 ngx_buf_t mvhd_atom_buf; | |
141 ngx_buf_t mdat_atom_buf; | |
142 ngx_buf_t mdat_data_buf; | |
143 | |
144 u_char moov_atom_header[8]; | |
145 u_char mdat_atom_header[16]; | |
146 } ngx_http_mp4_file_t; | |
147 | |
148 | |
149 typedef struct { | |
150 char *name; | |
151 ngx_int_t (*handler)(ngx_http_mp4_file_t *mp4, | |
152 uint64_t atom_data_size); | |
153 } ngx_http_mp4_atom_handler_t; | |
154 | |
155 | |
156 #define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8) | |
157 #define ngx_mp4_atom_data(mp4) mp4->buffer_pos | |
158 #define ngx_mp4_atom_next(mp4, n) mp4->buffer_pos += n; mp4->offset += n | |
159 | |
160 | |
161 #define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \ | |
162 ((u_char *) (p))[4] = n1; \ | |
163 ((u_char *) (p))[5] = n2; \ | |
164 ((u_char *) (p))[6] = n3; \ | |
165 ((u_char *) (p))[7] = n4 | |
166 | |
167 #define ngx_mp4_get_32value(p) \ | |
168 ( (((u_char *) (p))[0] << 24) \ | |
169 + (((u_char *) (p))[1] << 16) \ | |
170 + (((u_char *) (p))[2] << 8) \ | |
171 + (((u_char *) (p))[3]) ) | |
172 | |
173 #define ngx_mp4_set_32value(p, n) \ | |
174 ((u_char *) (p))[0] = (u_char) ((n) >> 24); \ | |
175 ((u_char *) (p))[1] = (u_char) ((n) >> 16); \ | |
176 ((u_char *) (p))[2] = (u_char) ((n) >> 8); \ | |
177 ((u_char *) (p))[3] = (u_char) (n) | |
178 | |
179 #define ngx_mp4_get_64value(p) \ | |
180 ( ((uint64_t) ((u_char *) (p))[0] << 56) \ | |
181 + ((uint64_t) ((u_char *) (p))[1] << 48) \ | |
182 + ((uint64_t) ((u_char *) (p))[2] << 40) \ | |
183 + ((uint64_t) ((u_char *) (p))[3] << 32) \ | |
184 + ((uint64_t) ((u_char *) (p))[4] << 24) \ | |
185 + ( ((u_char *) (p))[5] << 16) \ | |
186 + ( ((u_char *) (p))[6] << 8) \ | |
187 + ( ((u_char *) (p))[7]) ) | |
188 | |
189 #define ngx_mp4_set_64value(p, n) \ | |
4155
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
190 ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
191 ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
192 ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
193 ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
194 ((u_char *) (p))[4] = (u_char) ( (n) >> 24); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
195 ((u_char *) (p))[5] = (u_char) ( (n) >> 16); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
196 ((u_char *) (p))[6] = (u_char) ( (n) >> 8); \ |
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
197 ((u_char *) (p))[7] = (u_char) (n) |
4085 | 198 |
199 #define ngx_mp4_last_trak(mp4) \ | |
200 &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1] | |
201 | |
202 | |
203 static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4); | |
204 static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4, | |
205 ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size); | |
206 static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4); | |
207 static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, | |
208 uint64_t atom_data_size); | |
209 static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, | |
210 uint64_t atom_data_size); | |
211 static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, | |
212 uint64_t atom_data_size); | |
213 static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, | |
214 off_t start_offset); | |
215 static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, | |
216 uint64_t atom_data_size); | |
217 static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, | |
218 uint64_t atom_data_size); | |
219 static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4, | |
220 ngx_http_mp4_trak_t *trak); | |
221 static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, | |
222 uint64_t atom_data_size); | |
223 static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, | |
224 uint64_t atom_data_size); | |
225 static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, | |
226 uint64_t atom_data_size); | |
227 static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4, | |
228 ngx_http_mp4_trak_t *trak); | |
229 static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, | |
230 uint64_t atom_data_size); | |
231 static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, | |
232 uint64_t atom_data_size); | |
233 static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, | |
234 uint64_t atom_data_size); | |
235 static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4, | |
236 ngx_http_mp4_trak_t *trak); | |
237 static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, | |
238 uint64_t atom_data_size); | |
239 static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, | |
240 uint64_t atom_data_size); | |
241 static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, | |
242 uint64_t atom_data_size); | |
243 static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, | |
244 uint64_t atom_data_size); | |
245 static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4, | |
246 ngx_http_mp4_trak_t *trak); | |
247 static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, | |
248 uint64_t atom_data_size); | |
249 static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, | |
250 uint64_t atom_data_size); | |
251 static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, | |
252 ngx_http_mp4_trak_t *trak); | |
253 static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, | |
254 uint64_t atom_data_size); | |
255 static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, | |
256 ngx_http_mp4_trak_t *trak); | |
257 static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, | |
258 uint64_t atom_data_size); | |
259 static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, | |
260 ngx_http_mp4_trak_t *trak); | |
261 static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, | |
262 uint64_t atom_data_size); | |
263 static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, | |
264 ngx_http_mp4_trak_t *trak); | |
265 static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, | |
266 uint64_t atom_data_size); | |
267 static void ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, | |
268 ngx_http_mp4_trak_t *trak); | |
269 static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, | |
270 uint64_t atom_data_size); | |
271 static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, | |
272 ngx_http_mp4_trak_t *trak); | |
273 static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4, | |
274 ngx_http_mp4_trak_t *trak, int32_t adjustment); | |
4112 | 275 static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, |
276 uint64_t atom_data_size); | |
277 static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, | |
278 ngx_http_mp4_trak_t *trak); | |
279 static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4, | |
280 ngx_http_mp4_trak_t *trak, off_t adjustment); | |
4085 | 281 static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); |
282 static void *ngx_http_mp4_create_conf(ngx_conf_t *cf); | |
283 static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child); | |
284 | |
285 static ngx_command_t ngx_http_mp4_commands[] = { | |
286 | |
287 { ngx_string("mp4"), | |
288 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, | |
289 ngx_http_mp4, | |
290 0, | |
291 0, | |
292 NULL }, | |
293 | |
294 { ngx_string("mp4_buffer_size"), | |
295 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
296 ngx_conf_set_size_slot, | |
297 NGX_HTTP_LOC_CONF_OFFSET, | |
298 offsetof(ngx_http_mp4_conf_t, buffer_size), | |
299 NULL }, | |
300 | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
301 { ngx_string("mp4_max_buffer_size"), |
4085 | 302 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, |
303 ngx_conf_set_size_slot, | |
304 NGX_HTTP_LOC_CONF_OFFSET, | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
305 offsetof(ngx_http_mp4_conf_t, max_buffer_size), |
4085 | 306 NULL }, |
307 | |
308 ngx_null_command | |
309 }; | |
310 | |
311 | |
312 static ngx_http_module_t ngx_http_mp4_module_ctx = { | |
313 NULL, /* preconfiguration */ | |
314 NULL, /* postconfiguration */ | |
315 | |
316 NULL, /* create main configuration */ | |
317 NULL, /* init main configuration */ | |
318 | |
319 NULL, /* create server configuration */ | |
320 NULL, /* merge server configuration */ | |
321 | |
322 ngx_http_mp4_create_conf, /* create location configuration */ | |
323 ngx_http_mp4_merge_conf /* merge location configuration */ | |
324 }; | |
325 | |
326 | |
327 ngx_module_t ngx_http_mp4_module = { | |
328 NGX_MODULE_V1, | |
329 &ngx_http_mp4_module_ctx, /* module context */ | |
330 ngx_http_mp4_commands, /* module directives */ | |
331 NGX_HTTP_MODULE, /* module type */ | |
332 NULL, /* init master */ | |
333 NULL, /* init module */ | |
334 NULL, /* init process */ | |
335 NULL, /* init thread */ | |
336 NULL, /* exit thread */ | |
337 NULL, /* exit process */ | |
338 NULL, /* exit master */ | |
339 NGX_MODULE_V1_PADDING | |
340 }; | |
341 | |
342 | |
343 static ngx_http_mp4_atom_handler_t ngx_http_mp4_atoms[] = { | |
344 { "ftyp", ngx_http_mp4_read_ftyp_atom }, | |
345 { "moov", ngx_http_mp4_read_moov_atom }, | |
346 { "mdat", ngx_http_mp4_read_mdat_atom }, | |
347 { NULL, NULL } | |
348 }; | |
349 | |
350 static ngx_http_mp4_atom_handler_t ngx_http_mp4_moov_atoms[] = { | |
351 { "mvhd", ngx_http_mp4_read_mvhd_atom }, | |
352 { "trak", ngx_http_mp4_read_trak_atom }, | |
353 { "cmov", ngx_http_mp4_read_cmov_atom }, | |
354 { NULL, NULL } | |
355 }; | |
356 | |
357 static ngx_http_mp4_atom_handler_t ngx_http_mp4_trak_atoms[] = { | |
358 { "tkhd", ngx_http_mp4_read_tkhd_atom }, | |
359 { "mdia", ngx_http_mp4_read_mdia_atom }, | |
360 { NULL, NULL } | |
361 }; | |
362 | |
363 static ngx_http_mp4_atom_handler_t ngx_http_mp4_mdia_atoms[] = { | |
364 { "mdhd", ngx_http_mp4_read_mdhd_atom }, | |
365 { "hdlr", ngx_http_mp4_read_hdlr_atom }, | |
366 { "minf", ngx_http_mp4_read_minf_atom }, | |
367 { NULL, NULL } | |
368 }; | |
369 | |
370 static ngx_http_mp4_atom_handler_t ngx_http_mp4_minf_atoms[] = { | |
371 { "vmhd", ngx_http_mp4_read_vmhd_atom }, | |
372 { "smhd", ngx_http_mp4_read_smhd_atom }, | |
373 { "dinf", ngx_http_mp4_read_dinf_atom }, | |
374 { "stbl", ngx_http_mp4_read_stbl_atom }, | |
375 { NULL, NULL } | |
376 }; | |
377 | |
378 static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms[] = { | |
379 { "stsd", ngx_http_mp4_read_stsd_atom }, | |
380 { "stts", ngx_http_mp4_read_stts_atom }, | |
381 { "stss", ngx_http_mp4_read_stss_atom }, | |
382 { "ctts", ngx_http_mp4_read_ctts_atom }, | |
383 { "stsc", ngx_http_mp4_read_stsc_atom }, | |
384 { "stsz", ngx_http_mp4_read_stsz_atom }, | |
385 { "stco", ngx_http_mp4_read_stco_atom }, | |
4112 | 386 { "co64", ngx_http_mp4_read_co64_atom }, |
4085 | 387 { NULL, NULL } |
388 }; | |
389 | |
390 | |
391 static ngx_int_t | |
392 ngx_http_mp4_handler(ngx_http_request_t *r) | |
393 { | |
394 u_char *last; | |
395 size_t root; | |
396 ngx_int_t rc, start; | |
397 ngx_uint_t level; | |
398 ngx_str_t path, value; | |
399 ngx_log_t *log; | |
400 ngx_buf_t *b; | |
401 ngx_chain_t out; | |
402 ngx_http_mp4_file_t *mp4; | |
403 ngx_open_file_info_t of; | |
404 ngx_http_core_loc_conf_t *clcf; | |
405 | |
406 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { | |
407 return NGX_HTTP_NOT_ALLOWED; | |
408 } | |
409 | |
410 if (r->uri.data[r->uri.len - 1] == '/') { | |
411 return NGX_DECLINED; | |
412 } | |
413 | |
414 rc = ngx_http_discard_request_body(r); | |
415 | |
416 if (rc != NGX_OK) { | |
417 return rc; | |
418 } | |
419 | |
420 last = ngx_http_map_uri_to_path(r, &path, &root, 0); | |
421 if (last == NULL) { | |
422 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
423 } | |
424 | |
425 log = r->connection->log; | |
426 | |
427 path.len = last - path.data; | |
428 | |
429 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, | |
430 "http mp4 filename: \"%V\"", &path); | |
431 | |
432 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
433 | |
434 ngx_memzero(&of, sizeof(ngx_open_file_info_t)); | |
435 | |
436 of.read_ahead = clcf->read_ahead; | |
4087
3aa3b7bb9f0d
Bugfix of r4086: directio was always enabled if mp4 file was sent as is.
Igor Sysoev <igor@sysoev.ru>
parents:
4085
diff
changeset
|
437 of.directio = NGX_MAX_OFF_T_VALUE; |
4085 | 438 of.valid = clcf->open_file_cache_valid; |
439 of.min_uses = clcf->open_file_cache_min_uses; | |
440 of.errors = clcf->open_file_cache_errors; | |
441 of.events = clcf->open_file_cache_events; | |
442 | |
443 if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) | |
444 != NGX_OK) | |
445 { | |
446 switch (of.err) { | |
447 | |
448 case 0: | |
449 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
450 | |
451 case NGX_ENOENT: | |
452 case NGX_ENOTDIR: | |
453 case NGX_ENAMETOOLONG: | |
454 | |
455 level = NGX_LOG_ERR; | |
456 rc = NGX_HTTP_NOT_FOUND; | |
457 break; | |
458 | |
459 case NGX_EACCES: | |
460 | |
461 level = NGX_LOG_ERR; | |
462 rc = NGX_HTTP_FORBIDDEN; | |
463 break; | |
464 | |
465 default: | |
466 | |
467 level = NGX_LOG_CRIT; | |
468 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
469 break; | |
470 } | |
471 | |
472 if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) { | |
473 ngx_log_error(level, log, of.err, | |
474 "%s \"%s\" failed", of.failed, path.data); | |
475 } | |
476 | |
477 return rc; | |
478 } | |
479 | |
480 if (!of.is_file) { | |
481 | |
482 if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { | |
483 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
484 ngx_close_file_n " \"%s\" failed", path.data); | |
485 } | |
486 | |
487 return NGX_DECLINED; | |
488 } | |
489 | |
490 r->root_tested = !r->error_page; | |
491 r->allow_ranges = 1; | |
492 | |
493 start = -1; | |
494 r->headers_out.content_length_n = of.size; | |
495 mp4 = NULL; | |
496 b = NULL; | |
497 | |
498 if (r->args.len) { | |
499 | |
500 if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) { | |
501 | |
4156
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
502 /* |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
503 * A Flash player may send start value with a lot of digits |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
504 * after dot so strtod() is used instead of atofp(). NaNs and |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
505 * infinities become negative numbers after (int) conversion. |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
506 */ |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
507 |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
508 ngx_set_errno(0); |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
509 start = (int) (strtod((char *) value.data, NULL) * 1000); |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
510 |
67a4654ba7d9
Using strtod() instead of atofp() to support a lot of digits after dot in
Igor Sysoev <igor@sysoev.ru>
parents:
4155
diff
changeset
|
511 if (ngx_errno == 0 && start >= 0) { |
4085 | 512 r->allow_ranges = 0; |
513 | |
514 mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t)); | |
515 if (mp4 == NULL) { | |
516 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
517 } | |
518 | |
519 mp4->file.fd = of.fd; | |
520 mp4->file.name = path; | |
521 mp4->file.log = r->connection->log;; | |
522 mp4->end = of.size; | |
523 mp4->start = (ngx_uint_t) start; | |
524 mp4->request = r; | |
525 | |
526 switch (ngx_http_mp4_process(mp4)) { | |
527 | |
528 case NGX_DECLINED: | |
529 if (mp4->buffer) { | |
530 ngx_pfree(r->pool, mp4->buffer); | |
531 } | |
532 | |
533 ngx_pfree(r->pool, mp4); | |
534 mp4 = NULL; | |
535 | |
536 break; | |
537 | |
538 case NGX_OK: | |
539 r->headers_out.content_length_n = mp4->content_length; | |
540 break; | |
541 | |
542 default: /* NGX_ERROR */ | |
543 if (mp4->buffer) { | |
544 ngx_pfree(r->pool, mp4->buffer); | |
545 } | |
546 | |
547 ngx_pfree(r->pool, mp4); | |
548 | |
549 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
550 } | |
551 } | |
552 } | |
553 } | |
554 | |
555 log->action = "sending mp4 to client"; | |
556 | |
557 if (clcf->directio <= of.size) { | |
558 | |
559 /* | |
560 * DIRECTIO is set on transfer only | |
561 * to allow kernel to cache "moov" atom | |
562 */ | |
563 | |
564 if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) { | |
565 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, | |
566 ngx_directio_on_n " \"%s\" failed", path.data); | |
567 } | |
568 | |
4087
3aa3b7bb9f0d
Bugfix of r4086: directio was always enabled if mp4 file was sent as is.
Igor Sysoev <igor@sysoev.ru>
parents:
4085
diff
changeset
|
569 of.is_directio = 1; |
3aa3b7bb9f0d
Bugfix of r4086: directio was always enabled if mp4 file was sent as is.
Igor Sysoev <igor@sysoev.ru>
parents:
4085
diff
changeset
|
570 |
4085 | 571 if (mp4) { |
572 mp4->file.directio = 1; | |
573 } | |
574 } | |
575 | |
576 r->headers_out.status = NGX_HTTP_OK; | |
577 r->headers_out.last_modified_time = of.mtime; | |
578 | |
579 if (ngx_http_set_content_type(r) != NGX_OK) { | |
580 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
581 } | |
582 | |
583 if (mp4 == NULL) { | |
584 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); | |
585 if (b == NULL) { | |
586 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
587 } | |
588 | |
589 b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); | |
590 if (b->file == NULL) { | |
591 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
592 } | |
593 } | |
594 | |
595 rc = ngx_http_send_header(r); | |
596 | |
597 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
598 return rc; | |
599 } | |
600 | |
601 if (mp4) { | |
602 return ngx_http_output_filter(r, mp4->out); | |
603 } | |
604 | |
605 b->file_pos = 0; | |
606 b->file_last = of.size; | |
607 | |
608 b->in_file = b->file_last ? 1 : 0; | |
609 b->last_buf = 1; | |
610 b->last_in_chain = 1; | |
611 | |
612 b->file->fd = of.fd; | |
613 b->file->name = path; | |
614 b->file->log = log; | |
615 b->file->directio = of.is_directio; | |
616 | |
617 out.buf = b; | |
618 out.next = NULL; | |
619 | |
620 return ngx_http_output_filter(r, &out); | |
621 } | |
622 | |
623 | |
624 static ngx_int_t | |
625 ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) | |
626 { | |
627 off_t start_offset, adjustment; | |
628 ngx_int_t rc; | |
629 ngx_uint_t i, j; | |
630 ngx_chain_t **prev; | |
631 ngx_http_mp4_trak_t *trak; | |
632 ngx_http_mp4_conf_t *conf; | |
633 | |
634 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
635 "mp4 start:%ui", mp4->start); | |
636 | |
637 conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module); | |
638 | |
639 mp4->buffer_size = conf->buffer_size; | |
640 | |
641 rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end); | |
642 if (rc != NGX_OK) { | |
643 return rc; | |
644 } | |
645 | |
646 if (mp4->trak.nelts == 0) { | |
647 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
648 "no mp4 trak atoms were found in \"%s\"", | |
649 mp4->file.name.data); | |
650 return NGX_ERROR; | |
651 } | |
652 | |
653 if (mp4->mdat_atom.buf == NULL) { | |
654 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
655 "no mp4 mdat atom was found in \"%s\"", | |
656 mp4->file.name.data); | |
657 return NGX_ERROR; | |
658 } | |
659 | |
660 prev = &mp4->out; | |
661 | |
662 if (mp4->ftyp_atom.buf) { | |
663 *prev = &mp4->ftyp_atom; | |
664 prev = &mp4->ftyp_atom.next; | |
665 } | |
666 | |
667 *prev = &mp4->moov_atom; | |
668 prev = &mp4->moov_atom.next; | |
669 | |
670 if (mp4->mvhd_atom.buf) { | |
671 mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos; | |
672 *prev = &mp4->mvhd_atom; | |
673 prev = &mp4->mvhd_atom.next; | |
674 } | |
675 | |
676 start_offset = mp4->end; | |
677 trak = mp4->trak.elts; | |
678 | |
679 for (i = 0; i < mp4->trak.nelts; i++) { | |
680 | |
681 if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) { | |
682 return NGX_ERROR; | |
683 } | |
684 | |
685 if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) { | |
686 return NGX_ERROR; | |
687 } | |
688 | |
689 ngx_http_mp4_update_ctts_atom(mp4, &trak[i]); | |
690 | |
691 if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) { | |
692 return NGX_ERROR; | |
693 } | |
694 | |
695 ngx_http_mp4_update_stsz_atom(mp4, &trak[i]); | |
696 | |
4112 | 697 if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) { |
698 if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) { | |
699 return NGX_ERROR; | |
700 } | |
701 | |
702 } else { | |
703 if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) { | |
704 return NGX_ERROR; | |
705 } | |
4085 | 706 } |
707 | |
708 ngx_http_mp4_update_stbl_atom(mp4, &trak[i]); | |
709 ngx_http_mp4_update_minf_atom(mp4, &trak[i]); | |
710 trak[i].size += trak[i].mdhd_size; | |
711 trak[i].size += trak[i].hdlr_size; | |
712 ngx_http_mp4_update_mdia_atom(mp4, &trak[i]); | |
713 trak[i].size += trak[i].tkhd_size; | |
714 ngx_http_mp4_update_trak_atom(mp4, &trak[i]); | |
715 | |
716 mp4->moov_size += trak[i].size; | |
717 | |
718 if (start_offset > trak[i].start_offset) { | |
719 start_offset = trak[i].start_offset; | |
720 } | |
721 | |
722 *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM]; | |
723 prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next; | |
724 | |
725 for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) { | |
726 if (trak[i].out[j].buf) { | |
727 *prev = &trak[i].out[j]; | |
728 prev = &trak[i].out[j].next; | |
729 } | |
730 } | |
731 } | |
732 | |
733 mp4->moov_size += 8; | |
734 | |
735 ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size); | |
736 ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v'); | |
737 mp4->content_length += mp4->moov_size; | |
738 | |
739 *prev = &mp4->mdat_atom; | |
740 | |
741 adjustment = mp4->ftyp_size + mp4->moov_size | |
742 + ngx_http_mp4_update_mdat_atom(mp4, start_offset) | |
743 - start_offset; | |
744 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
745 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 746 "mp4 adjustment:%D", adjustment); |
747 | |
748 for (i = 0; i < mp4->trak.nelts; i++) { | |
4112 | 749 if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) { |
750 ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment); | |
751 } else { | |
752 ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment); | |
753 } | |
4085 | 754 } |
755 | |
756 return NGX_OK; | |
757 } | |
758 | |
759 | |
760 typedef struct { | |
761 u_char size[4]; | |
762 u_char name[4]; | |
763 } ngx_mp4_atom_header_t; | |
764 | |
765 typedef struct { | |
766 u_char size[4]; | |
767 u_char name[4]; | |
768 u_char size64[8]; | |
769 } ngx_mp4_atom_header64_t; | |
770 | |
771 | |
772 static ngx_int_t | |
773 ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4, | |
774 ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size) | |
775 { | |
776 off_t end; | |
777 size_t atom_header_size; | |
778 u_char *atom_header, *atom_name; | |
779 uint64_t atom_size; | |
780 ngx_int_t rc; | |
781 ngx_uint_t n; | |
782 | |
783 end = mp4->offset + atom_data_size; | |
784 | |
785 while (mp4->offset < end) { | |
786 | |
787 if (mp4->buffer_pos + sizeof(uint32_t) > mp4->buffer_end) { | |
788 if (ngx_http_mp4_read(mp4) != NGX_OK) { | |
789 return NGX_ERROR; | |
790 } | |
791 } | |
792 | |
793 atom_header = mp4->buffer_pos; | |
794 atom_size = ngx_mp4_get_32value(atom_header); | |
795 atom_header_size = sizeof(ngx_mp4_atom_header_t); | |
796 | |
797 if (atom_size == 0) { | |
798 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
799 "mp4 atom end"); | |
800 return NGX_OK; | |
801 } | |
802 | |
803 if (atom_size < sizeof(ngx_mp4_atom_header_t)) { | |
804 | |
805 if (atom_size == 1) { | |
806 | |
807 if (mp4->buffer_pos + sizeof(ngx_mp4_atom_header64_t) | |
808 > mp4->buffer_end) | |
809 { | |
810 if (ngx_http_mp4_read(mp4) != NGX_OK) { | |
811 return NGX_ERROR; | |
812 } | |
813 | |
814 atom_header = mp4->buffer_pos; | |
815 } | |
816 | |
817 /* 64-bit atom size */ | |
818 atom_size = ngx_mp4_get_64value(atom_header + 8); | |
819 atom_header_size = sizeof(ngx_mp4_atom_header64_t); | |
820 | |
821 } else { | |
822 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
823 "\"%s\" mp4 atom is too small:%uL", | |
824 mp4->file.name.data, atom_size); | |
825 return NGX_ERROR; | |
826 } | |
827 } | |
828 | |
829 if (mp4->buffer_pos + sizeof(ngx_mp4_atom_header_t) > mp4->buffer_end) { | |
830 if (ngx_http_mp4_read(mp4) != NGX_OK) { | |
831 return NGX_ERROR; | |
832 } | |
833 | |
834 atom_header = mp4->buffer_pos; | |
835 } | |
836 | |
837 atom_name = atom_header + sizeof(uint32_t); | |
838 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
839 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 840 "mp4 atom: %*s @%O:%uL", |
841 4, atom_name, mp4->offset, atom_size); | |
842 | |
843 for (n = 0; atom[n].name; n++) { | |
844 | |
845 if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) { | |
846 | |
847 ngx_mp4_atom_next(mp4, atom_header_size); | |
848 | |
849 rc = atom[n].handler(mp4, atom_size - atom_header_size); | |
850 if (rc != NGX_OK) { | |
851 return rc; | |
852 } | |
853 | |
854 goto next; | |
855 } | |
856 } | |
857 | |
858 ngx_mp4_atom_next(mp4, atom_size); | |
859 | |
860 next: | |
861 continue; | |
862 } | |
863 | |
864 return NGX_OK; | |
865 } | |
866 | |
867 | |
868 static ngx_int_t | |
869 ngx_http_mp4_read(ngx_http_mp4_file_t *mp4) | |
870 { | |
871 ngx_int_t n; | |
872 | |
873 if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) { | |
874 mp4->buffer_size = (size_t) (mp4->end - mp4->offset); | |
875 } | |
876 | |
877 if (mp4->buffer == NULL) { | |
878 mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size); | |
879 if (mp4->buffer == NULL) { | |
880 return NGX_ERROR; | |
881 } | |
882 | |
883 mp4->buffer_start = mp4->buffer; | |
884 mp4->buffer_end = mp4->buffer + mp4->buffer_size; | |
885 } | |
886 | |
887 n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size, | |
888 mp4->offset); | |
889 | |
890 if (n == NGX_ERROR) { | |
891 return NGX_ERROR; | |
892 } | |
893 | |
894 if (n == 0) { | |
895 return NGX_OK; | |
896 } | |
897 | |
898 mp4->buffer_pos = mp4->buffer_start; | |
899 | |
900 return NGX_OK; | |
901 } | |
902 | |
903 | |
904 static ngx_int_t | |
905 ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
906 { | |
907 u_char *ftyp_atom; | |
908 size_t atom_size; | |
909 ngx_buf_t *atom; | |
910 | |
911 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom"); | |
912 | |
913 if (atom_data_size > 1024) { | |
914 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
915 "\"%s\" mp4 ftyp atom is too large:%uL", | |
916 mp4->file.name.data, atom_data_size); | |
917 return NGX_ERROR; | |
918 } | |
919 | |
920 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
921 | |
922 ftyp_atom = ngx_palloc(mp4->request->pool, atom_size); | |
923 if (ftyp_atom == NULL) { | |
924 return NGX_ERROR; | |
925 } | |
926 | |
927 ngx_mp4_set_32value(ftyp_atom, atom_size); | |
928 ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p'); | |
929 | |
930 /* | |
931 * only moov atom content is guaranteed to be in mp4->buffer | |
932 * during sending response, so ftyp atom content should be copied | |
933 */ | |
934 ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t), | |
935 ngx_mp4_atom_data(mp4), (size_t) atom_data_size); | |
936 | |
937 atom = &mp4->ftyp_atom_buf; | |
938 atom->temporary = 1; | |
939 atom->pos = ftyp_atom; | |
940 atom->last = ftyp_atom + atom_size; | |
941 | |
942 mp4->ftyp_atom.buf = atom; | |
943 mp4->ftyp_size = atom_size; | |
944 mp4->content_length = atom_size; | |
945 | |
946 ngx_mp4_atom_next(mp4, atom_data_size); | |
947 | |
948 return NGX_OK; | |
949 } | |
950 | |
951 | |
952 /* | |
953 * Small excess buffer to process atoms after moov atom, mp4->buffer_start | |
954 * will be set to this buffer part after moov atom processing. | |
955 */ | |
956 #define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS (4 * 1024) | |
957 | |
958 static ngx_int_t | |
959 ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
960 { | |
961 ngx_int_t rc; | |
962 ngx_uint_t no_mdat; | |
963 ngx_buf_t *atom; | |
964 ngx_http_mp4_conf_t *conf; | |
965 | |
966 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom"); | |
967 | |
968 no_mdat = (mp4->mdat_atom.buf == NULL); | |
969 | |
970 if (no_mdat && mp4->start == 0) { | |
971 /* | |
972 * send original file if moov atom resides before | |
973 * mdat atom and client requests integral file | |
974 */ | |
975 return NGX_DECLINED; | |
976 } | |
977 | |
978 conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module); | |
979 | |
980 if (atom_data_size > mp4->buffer_size) { | |
981 | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
982 if (atom_data_size > conf->max_buffer_size) { |
4085 | 983 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, |
984 "\"%s\" mp4 moov atom is too large:%uL, " | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
985 "you may want to increase mp4_max_buffer_size", |
4085 | 986 mp4->file.name.data, atom_data_size); |
987 return NGX_ERROR; | |
988 } | |
989 | |
990 ngx_pfree(mp4->request->pool, mp4->buffer); | |
991 mp4->buffer = NULL; | |
992 mp4->buffer_pos = NULL; | |
993 mp4->buffer_end = NULL; | |
994 | |
995 mp4->buffer_size = (size_t) atom_data_size | |
996 + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat; | |
997 } | |
998 | |
999 mp4->trak.elts = &mp4->traks; | |
1000 mp4->trak.size = sizeof(ngx_http_mp4_trak_t); | |
1001 mp4->trak.nalloc = 2; | |
1002 mp4->trak.pool = mp4->request->pool; | |
1003 | |
1004 atom = &mp4->moov_atom_buf; | |
1005 atom->temporary = 1; | |
1006 atom->pos = mp4->moov_atom_header; | |
1007 atom->last = mp4->moov_atom_header + 8; | |
1008 | |
1009 mp4->moov_atom.buf = &mp4->moov_atom_buf; | |
1010 | |
1011 rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size); | |
1012 | |
1013 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done"); | |
1014 | |
1015 if (no_mdat) { | |
1016 mp4->buffer_start = mp4->buffer_pos; | |
1017 mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS; | |
1018 | |
1019 } else { | |
1020 /* skip atoms after moov atom */ | |
1021 mp4->offset = mp4->end; | |
1022 } | |
1023 | |
1024 return rc; | |
1025 } | |
1026 | |
1027 | |
1028 static ngx_int_t | |
1029 ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1030 { | |
1031 ngx_buf_t *data; | |
1032 | |
1033 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom"); | |
1034 | |
1035 data = &mp4->mdat_data_buf; | |
1036 data->file = &mp4->file; | |
1037 data->in_file = 1; | |
1038 data->last_buf = 1; | |
1039 data->last_in_chain = 1; | |
1040 data->file_last = mp4->offset + atom_data_size; | |
1041 | |
1042 mp4->mdat_atom.buf = &mp4->mdat_atom_buf; | |
1043 mp4->mdat_atom.next = &mp4->mdat_data; | |
1044 mp4->mdat_data.buf = data; | |
1045 | |
1046 if (mp4->trak.nelts) { | |
1047 /* skip atoms after mdat atom */ | |
1048 mp4->offset = mp4->end; | |
1049 | |
1050 } else { | |
1051 ngx_mp4_atom_next(mp4, atom_data_size); | |
1052 } | |
1053 | |
1054 return NGX_OK; | |
1055 } | |
1056 | |
1057 | |
1058 static size_t | |
1059 ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset) | |
1060 { | |
1061 off_t atom_data_size; | |
1062 u_char *atom_header; | |
1063 uint32_t atom_header_size; | |
1064 uint64_t atom_size; | |
1065 ngx_buf_t *atom; | |
1066 | |
1067 atom_data_size = mp4->mdat_data.buf->file_last - start_offset; | |
1068 mp4->mdat_data.buf->file_pos = start_offset; | |
1069 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
1070 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 1071 "mdat new offset @%O:%O", start_offset, atom_data_size); |
1072 | |
1073 atom_header = mp4->mdat_atom_header; | |
1074 | |
4155
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
1075 if ((uint64_t) atom_data_size > 0xffffffff) { |
4085 | 1076 atom_size = 1; |
1077 atom_header_size = sizeof(ngx_mp4_atom_header64_t); | |
1078 ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t), | |
1079 sizeof(ngx_mp4_atom_header64_t) + atom_data_size); | |
1080 } else { | |
1081 atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size; | |
1082 atom_header_size = sizeof(ngx_mp4_atom_header_t); | |
1083 } | |
1084 | |
4282
71cdac0b9ea6
Fix of "Content-Length" header of MP4 response if start argument was used.
Igor Sysoev <igor@sysoev.ru>
parents:
4189
diff
changeset
|
1085 mp4->content_length += atom_header_size + atom_data_size; |
71cdac0b9ea6
Fix of "Content-Length" header of MP4 response if start argument was used.
Igor Sysoev <igor@sysoev.ru>
parents:
4189
diff
changeset
|
1086 |
4085 | 1087 ngx_mp4_set_32value(atom_header, atom_size); |
1088 ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't'); | |
1089 | |
1090 atom = &mp4->mdat_atom_buf; | |
1091 atom->temporary = 1; | |
1092 atom->pos = atom_header; | |
1093 atom->last = atom_header + atom_header_size; | |
1094 | |
1095 return atom_header_size; | |
1096 } | |
1097 | |
1098 | |
1099 typedef struct { | |
1100 u_char size[4]; | |
1101 u_char name[4]; | |
1102 u_char version[1]; | |
1103 u_char flags[3]; | |
1104 u_char creation_time[4]; | |
1105 u_char modification_time[4]; | |
1106 u_char timescale[4]; | |
1107 u_char duration[4]; | |
1108 u_char rate[4]; | |
1109 u_char volume[2]; | |
1110 u_char reserved[10]; | |
1111 u_char matrix[36]; | |
1112 u_char preview_time[4]; | |
1113 u_char preview_duration[4]; | |
1114 u_char poster_time[4]; | |
1115 u_char selection_time[4]; | |
1116 u_char selection_duration[4]; | |
1117 u_char current_time[4]; | |
1118 u_char next_track_id[4]; | |
1119 } ngx_mp4_mvhd_atom_t; | |
1120 | |
1121 typedef struct { | |
1122 u_char size[4]; | |
1123 u_char name[4]; | |
1124 u_char version[1]; | |
1125 u_char flags[3]; | |
1126 u_char creation_time[8]; | |
1127 u_char modification_time[8]; | |
1128 u_char timescale[4]; | |
1129 u_char duration[8]; | |
1130 u_char rate[4]; | |
1131 u_char volume[2]; | |
1132 u_char reserved[10]; | |
1133 u_char matrix[36]; | |
1134 u_char preview_time[4]; | |
1135 u_char preview_duration[4]; | |
1136 u_char poster_time[4]; | |
1137 u_char selection_time[4]; | |
1138 u_char selection_duration[4]; | |
1139 u_char current_time[4]; | |
1140 u_char next_track_id[4]; | |
1141 } ngx_mp4_mvhd64_atom_t; | |
1142 | |
1143 | |
1144 static ngx_int_t | |
1145 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1146 { | |
1147 u_char *atom_header; | |
1148 size_t atom_size; | |
1149 uint32_t timescale; | |
1150 uint64_t duration; | |
1151 ngx_buf_t *atom; | |
1152 ngx_mp4_mvhd_atom_t *mvhd_atom; | |
1153 ngx_mp4_mvhd64_atom_t *mvhd64_atom; | |
1154 | |
1155 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom"); | |
1156 | |
1157 atom_header = ngx_mp4_atom_header(mp4); | |
1158 mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header; | |
1159 mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header; | |
1160 ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd'); | |
1161 | |
1162 if (mvhd_atom->version[0] == 0) { | |
1163 /* version 0: 32-bit duration */ | |
1164 timescale = ngx_mp4_get_32value(mvhd_atom->timescale); | |
1165 duration = ngx_mp4_get_32value(mvhd_atom->duration); | |
1166 | |
1167 } else { | |
1168 /* version 1: 64-bit duration */ | |
1169 timescale = ngx_mp4_get_32value(mvhd64_atom->timescale); | |
1170 duration = ngx_mp4_get_64value(mvhd64_atom->duration); | |
1171 } | |
1172 | |
1173 mp4->timescale = timescale; | |
1174 | |
1175 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1176 "mvhd timescale:%uD, duration:%uL, time:%.3fs", | |
1177 timescale, duration, (double) duration / timescale); | |
1178 | |
1179 duration -= (uint64_t) mp4->start * timescale / 1000; | |
1180 | |
1181 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1182 "mvhd new duration:%uL, time:%.3fs", | |
1183 duration, (double) duration / timescale); | |
1184 | |
1185 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1186 ngx_mp4_set_32value(mvhd_atom->size, atom_size); | |
1187 | |
1188 if (mvhd_atom->version[0] == 0) { | |
1189 ngx_mp4_set_32value(mvhd_atom->duration, duration); | |
1190 | |
1191 } else { | |
1192 ngx_mp4_set_64value(mvhd64_atom->duration, duration); | |
1193 } | |
1194 | |
1195 atom = &mp4->mvhd_atom_buf; | |
1196 atom->temporary = 1; | |
1197 atom->pos = atom_header; | |
1198 atom->last = atom_header + atom_size; | |
1199 | |
1200 mp4->mvhd_atom.buf = atom; | |
1201 | |
1202 ngx_mp4_atom_next(mp4, atom_data_size); | |
1203 | |
1204 return NGX_OK; | |
1205 } | |
1206 | |
1207 | |
1208 static ngx_int_t | |
1209 ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1210 { | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1211 u_char *atom_header, *atom_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1212 off_t atom_file_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1213 ngx_int_t rc; |
4085 | 1214 ngx_buf_t *atom; |
1215 ngx_http_mp4_trak_t *trak; | |
1216 | |
1217 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom"); | |
1218 | |
1219 trak = ngx_array_push(&mp4->trak); | |
1220 if (trak == NULL) { | |
1221 return NGX_ERROR; | |
1222 } | |
1223 | |
1224 ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t)); | |
1225 | |
1226 atom_header = ngx_mp4_atom_header(mp4); | |
1227 ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k'); | |
1228 | |
1229 atom = &trak->trak_atom_buf; | |
1230 atom->temporary = 1; | |
1231 atom->pos = atom_header; | |
1232 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1233 | |
1234 trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom; | |
1235 | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1236 atom_end = mp4->buffer_pos + atom_data_size; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1237 atom_file_end = mp4->offset + atom_data_size; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1238 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1239 rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1240 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1241 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1242 "mp4 trak atom: %i", rc); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1243 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1244 if (rc == NGX_DECLINED) { |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1245 /* skip this trak */ |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1246 ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t)); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1247 mp4->trak.nelts--; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1248 mp4->buffer_pos = atom_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1249 mp4->offset = atom_file_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1250 return NGX_OK; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1251 } |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1252 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1253 return rc; |
4085 | 1254 } |
1255 | |
1256 | |
1257 static void | |
1258 ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4, | |
1259 ngx_http_mp4_trak_t *trak) | |
1260 { | |
1261 ngx_buf_t *atom; | |
1262 | |
1263 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1264 atom = &trak->trak_atom_buf; | |
1265 ngx_mp4_set_32value(atom->pos, trak->size); | |
1266 } | |
1267 | |
1268 | |
1269 static ngx_int_t | |
1270 ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1271 { | |
1272 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1273 "\"%s\" mp4 compressed moov atom (cmov) is not supported", | |
1274 mp4->file.name.data); | |
1275 | |
1276 return NGX_ERROR; | |
1277 } | |
1278 | |
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[4]; | |
1286 u_char modification_time[4]; | |
1287 u_char track_id[4]; | |
1288 u_char reserved1[4]; | |
1289 u_char duration[4]; | |
1290 u_char reserved2[8]; | |
1291 u_char layer[2]; | |
1292 u_char group[2]; | |
1293 u_char volume[2]; | |
1294 u_char reverved3[2]; | |
1295 u_char matrix[36]; | |
1296 u_char width[4]; | |
1297 u_char heigth[4]; | |
1298 } ngx_mp4_tkhd_atom_t; | |
1299 | |
1300 typedef struct { | |
1301 u_char size[4]; | |
1302 u_char name[4]; | |
1303 u_char version[1]; | |
1304 u_char flags[3]; | |
1305 u_char creation_time[8]; | |
1306 u_char modification_time[8]; | |
1307 u_char track_id[4]; | |
1308 u_char reserved1[4]; | |
1309 u_char duration[8]; | |
1310 u_char reserved2[8]; | |
1311 u_char layer[2]; | |
1312 u_char group[2]; | |
1313 u_char volume[2]; | |
1314 u_char reverved3[2]; | |
1315 u_char matrix[36]; | |
1316 u_char width[4]; | |
1317 u_char heigth[4]; | |
1318 } ngx_mp4_tkhd64_atom_t; | |
1319 | |
1320 | |
1321 static ngx_int_t | |
1322 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1323 { | |
1324 u_char *atom_header; | |
1325 size_t atom_size; | |
1326 uint64_t duration; | |
1327 ngx_buf_t *atom; | |
1328 ngx_http_mp4_trak_t *trak; | |
1329 ngx_mp4_tkhd_atom_t *tkhd_atom; | |
1330 ngx_mp4_tkhd64_atom_t *tkhd64_atom; | |
1331 | |
1332 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom"); | |
1333 | |
1334 atom_header = ngx_mp4_atom_header(mp4); | |
1335 tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header; | |
1336 tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header; | |
1337 ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd'); | |
1338 | |
1339 if (tkhd_atom->version[0] == 0) { | |
1340 /* version 0: 32-bit duration */ | |
1341 duration = ngx_mp4_get_32value(tkhd_atom->duration); | |
1342 | |
1343 } else { | |
1344 /* version 1: 64-bit duration */ | |
1345 duration = ngx_mp4_get_64value(tkhd64_atom->duration); | |
1346 } | |
1347 | |
1348 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1349 "tkhd duration:%uL, time:%.3fs", | |
1350 duration, (double) duration / mp4->timescale); | |
1351 | |
1352 duration -= (uint64_t) mp4->start * mp4->timescale / 1000; | |
1353 | |
1354 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1355 "tkhd new duration:%uL, time:%.3fs", | |
1356 duration, (double) duration / mp4->timescale); | |
1357 | |
1358 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1359 | |
1360 trak = ngx_mp4_last_trak(mp4); | |
1361 trak->tkhd_size = atom_size; | |
1362 | |
1363 ngx_mp4_set_32value(tkhd_atom->size, atom_size); | |
1364 | |
1365 if (tkhd_atom->version[0] == 0) { | |
1366 ngx_mp4_set_32value(tkhd_atom->duration, duration); | |
1367 | |
1368 } else { | |
1369 ngx_mp4_set_64value(tkhd64_atom->duration, duration); | |
1370 } | |
1371 | |
1372 atom = &trak->tkhd_atom_buf; | |
1373 atom->temporary = 1; | |
1374 atom->pos = atom_header; | |
1375 atom->last = atom_header + atom_size; | |
1376 | |
1377 trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom; | |
1378 | |
1379 ngx_mp4_atom_next(mp4, atom_data_size); | |
1380 | |
1381 return NGX_OK; | |
1382 } | |
1383 | |
1384 | |
1385 static ngx_int_t | |
1386 ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1387 { | |
1388 u_char *atom_header; | |
1389 ngx_buf_t *atom; | |
1390 ngx_http_mp4_trak_t *trak; | |
1391 | |
1392 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom"); | |
1393 | |
1394 atom_header = ngx_mp4_atom_header(mp4); | |
1395 ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a'); | |
1396 | |
1397 trak = ngx_mp4_last_trak(mp4); | |
1398 | |
1399 atom = &trak->mdia_atom_buf; | |
1400 atom->temporary = 1; | |
1401 atom->pos = atom_header; | |
1402 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1403 | |
1404 trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom; | |
1405 | |
1406 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size); | |
1407 } | |
1408 | |
1409 | |
1410 static void | |
1411 ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4, | |
1412 ngx_http_mp4_trak_t *trak) | |
1413 { | |
1414 ngx_buf_t *atom; | |
1415 | |
1416 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1417 atom = &trak->mdia_atom_buf; | |
1418 ngx_mp4_set_32value(atom->pos, trak->size); | |
1419 } | |
1420 | |
1421 | |
1422 typedef struct { | |
1423 u_char size[4]; | |
1424 u_char name[4]; | |
1425 u_char version[1]; | |
1426 u_char flags[3]; | |
1427 u_char creation_time[4]; | |
1428 u_char modification_time[4]; | |
1429 u_char timescale[4]; | |
1430 u_char duration[4]; | |
1431 u_char language[2]; | |
1432 u_char quality[2]; | |
1433 } ngx_mp4_mdhd_atom_t; | |
1434 | |
1435 typedef struct { | |
1436 u_char size[4]; | |
1437 u_char name[4]; | |
1438 u_char version[1]; | |
1439 u_char flags[3]; | |
1440 u_char creation_time[8]; | |
1441 u_char modification_time[8]; | |
1442 u_char timescale[4]; | |
1443 u_char duration[8]; | |
1444 u_char language[2]; | |
1445 u_char quality[2]; | |
1446 } ngx_mp4_mdhd64_atom_t; | |
1447 | |
1448 | |
1449 static ngx_int_t | |
1450 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1451 { | |
1452 u_char *atom_header; | |
1453 size_t atom_size; | |
1454 uint32_t timescale; | |
1455 uint64_t duration; | |
1456 ngx_buf_t *atom; | |
1457 ngx_http_mp4_trak_t *trak; | |
1458 ngx_mp4_mdhd_atom_t *mdhd_atom; | |
1459 ngx_mp4_mdhd64_atom_t *mdhd64_atom; | |
1460 | |
1461 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom"); | |
1462 | |
1463 atom_header = ngx_mp4_atom_header(mp4); | |
1464 mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header; | |
1465 mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header; | |
1466 ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd'); | |
1467 | |
1468 if (mdhd_atom->version[0] == 0) { | |
1469 /* version 0: everything is 32-bit */ | |
1470 timescale = ngx_mp4_get_32value(mdhd_atom->timescale); | |
1471 duration = ngx_mp4_get_32value(mdhd_atom->duration); | |
1472 | |
1473 } else { | |
1474 /* version 1: 64-bit duration and 32-bit timescale */ | |
1475 timescale = ngx_mp4_get_32value(mdhd64_atom->timescale); | |
1476 duration = ngx_mp4_get_64value(mdhd64_atom->duration); | |
1477 } | |
1478 | |
1479 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1480 "mdhd timescale:%uD, duration:%uL, time:%.3fs", | |
1481 timescale, duration, (double) duration / timescale); | |
1482 | |
1483 duration -= (uint64_t) mp4->start * timescale / 1000; | |
1484 | |
1485 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1486 "mdhd new duration:%uL, time:%.3fs", | |
1487 duration, (double) duration / timescale); | |
1488 | |
1489 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1490 | |
1491 trak = ngx_mp4_last_trak(mp4); | |
1492 trak->mdhd_size = atom_size; | |
1493 trak->timescale = timescale; | |
1494 | |
1495 ngx_mp4_set_32value(mdhd_atom->size, atom_size); | |
1496 | |
1497 if (mdhd_atom->version[0] == 0) { | |
1498 ngx_mp4_set_32value(mdhd_atom->duration, duration); | |
1499 | |
1500 } else { | |
1501 ngx_mp4_set_64value(mdhd64_atom->duration, duration); | |
1502 } | |
1503 | |
1504 atom = &trak->mdhd_atom_buf; | |
1505 atom->temporary = 1; | |
1506 atom->pos = atom_header; | |
1507 atom->last = atom_header + atom_size; | |
1508 | |
1509 trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom; | |
1510 | |
1511 ngx_mp4_atom_next(mp4, atom_data_size); | |
1512 | |
1513 return NGX_OK; | |
1514 } | |
1515 | |
1516 | |
1517 static ngx_int_t | |
1518 ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1519 { | |
1520 u_char *atom_header; | |
1521 size_t atom_size; | |
1522 ngx_buf_t *atom; | |
1523 ngx_http_mp4_trak_t *trak; | |
1524 | |
1525 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom"); | |
1526 | |
1527 atom_header = ngx_mp4_atom_header(mp4); | |
1528 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1529 ngx_mp4_set_32value(atom_header, atom_size); | |
1530 ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r'); | |
1531 | |
1532 trak = ngx_mp4_last_trak(mp4); | |
1533 | |
1534 atom = &trak->hdlr_atom_buf; | |
1535 atom->temporary = 1; | |
1536 atom->pos = atom_header; | |
1537 atom->last = atom_header + atom_size; | |
1538 | |
1539 trak->hdlr_size = atom_size; | |
1540 trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom; | |
1541 | |
1542 ngx_mp4_atom_next(mp4, atom_data_size); | |
1543 | |
1544 return NGX_OK; | |
1545 } | |
1546 | |
1547 | |
1548 static ngx_int_t | |
1549 ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1550 { | |
1551 u_char *atom_header; | |
1552 ngx_buf_t *atom; | |
1553 ngx_http_mp4_trak_t *trak; | |
1554 | |
1555 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom"); | |
1556 | |
1557 atom_header = ngx_mp4_atom_header(mp4); | |
1558 ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f'); | |
1559 | |
1560 trak = ngx_mp4_last_trak(mp4); | |
1561 | |
1562 atom = &trak->minf_atom_buf; | |
1563 atom->temporary = 1; | |
1564 atom->pos = atom_header; | |
1565 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1566 | |
1567 trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom; | |
1568 | |
1569 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size); | |
1570 } | |
1571 | |
1572 | |
1573 static void | |
1574 ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4, | |
1575 ngx_http_mp4_trak_t *trak) | |
1576 { | |
1577 ngx_buf_t *atom; | |
1578 | |
1579 trak->size += sizeof(ngx_mp4_atom_header_t) | |
1580 + trak->vmhd_size | |
1581 + trak->smhd_size | |
1582 + trak->dinf_size; | |
1583 atom = &trak->minf_atom_buf; | |
1584 ngx_mp4_set_32value(atom->pos, trak->size); | |
1585 } | |
1586 | |
1587 | |
1588 static ngx_int_t | |
1589 ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1590 { | |
1591 u_char *atom_header; | |
1592 size_t atom_size; | |
1593 ngx_buf_t *atom; | |
1594 ngx_http_mp4_trak_t *trak; | |
1595 | |
1596 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom"); | |
1597 | |
1598 atom_header = ngx_mp4_atom_header(mp4); | |
1599 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1600 ngx_mp4_set_32value(atom_header, atom_size); | |
1601 ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd'); | |
1602 | |
1603 trak = ngx_mp4_last_trak(mp4); | |
1604 | |
1605 atom = &trak->vmhd_atom_buf; | |
1606 atom->temporary = 1; | |
1607 atom->pos = atom_header; | |
1608 atom->last = atom_header + atom_size; | |
1609 | |
1610 trak->vmhd_size += atom_size; | |
1611 trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom; | |
1612 | |
1613 ngx_mp4_atom_next(mp4, atom_data_size); | |
1614 | |
1615 return NGX_OK; | |
1616 } | |
1617 | |
1618 | |
1619 static ngx_int_t | |
1620 ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1621 { | |
1622 u_char *atom_header; | |
1623 size_t atom_size; | |
1624 ngx_buf_t *atom; | |
1625 ngx_http_mp4_trak_t *trak; | |
1626 | |
1627 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom"); | |
1628 | |
1629 atom_header = ngx_mp4_atom_header(mp4); | |
1630 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1631 ngx_mp4_set_32value(atom_header, atom_size); | |
1632 ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd'); | |
1633 | |
1634 trak = ngx_mp4_last_trak(mp4); | |
1635 | |
1636 atom = &trak->smhd_atom_buf; | |
1637 atom->temporary = 1; | |
1638 atom->pos = atom_header; | |
1639 atom->last = atom_header + atom_size; | |
1640 | |
1641 trak->vmhd_size += atom_size; | |
1642 trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom; | |
1643 | |
1644 ngx_mp4_atom_next(mp4, atom_data_size); | |
1645 | |
1646 return NGX_OK; | |
1647 } | |
1648 | |
1649 | |
1650 static ngx_int_t | |
1651 ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1652 { | |
1653 u_char *atom_header; | |
1654 size_t atom_size; | |
1655 ngx_buf_t *atom; | |
1656 ngx_http_mp4_trak_t *trak; | |
1657 | |
1658 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom"); | |
1659 | |
1660 atom_header = ngx_mp4_atom_header(mp4); | |
1661 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1662 ngx_mp4_set_32value(atom_header, atom_size); | |
1663 ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f'); | |
1664 | |
1665 trak = ngx_mp4_last_trak(mp4); | |
1666 | |
1667 atom = &trak->dinf_atom_buf; | |
1668 atom->temporary = 1; | |
1669 atom->pos = atom_header; | |
1670 atom->last = atom_header + atom_size; | |
1671 | |
1672 trak->dinf_size += atom_size; | |
1673 trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom; | |
1674 | |
1675 ngx_mp4_atom_next(mp4, atom_data_size); | |
1676 | |
1677 return NGX_OK; | |
1678 } | |
1679 | |
1680 | |
1681 static ngx_int_t | |
1682 ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1683 { | |
1684 u_char *atom_header; | |
1685 ngx_buf_t *atom; | |
1686 ngx_http_mp4_trak_t *trak; | |
1687 | |
1688 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom"); | |
1689 | |
1690 atom_header = ngx_mp4_atom_header(mp4); | |
1691 ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l'); | |
1692 | |
1693 trak = ngx_mp4_last_trak(mp4); | |
1694 | |
1695 atom = &trak->stbl_atom_buf; | |
1696 atom->temporary = 1; | |
1697 atom->pos = atom_header; | |
1698 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1699 | |
1700 trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom; | |
1701 | |
1702 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size); | |
1703 } | |
1704 | |
1705 | |
1706 static void | |
1707 ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4, | |
1708 ngx_http_mp4_trak_t *trak) | |
1709 { | |
1710 ngx_buf_t *atom; | |
1711 | |
1712 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1713 atom = &trak->stbl_atom_buf; | |
1714 ngx_mp4_set_32value(atom->pos, trak->size); | |
1715 } | |
1716 | |
1717 | |
1718 typedef struct { | |
1719 u_char size[4]; | |
1720 u_char name[4]; | |
1721 u_char version[1]; | |
1722 u_char flags[3]; | |
1723 u_char entries[4]; | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1724 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1725 u_char media_size[4]; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1726 u_char media_name[4]; |
4085 | 1727 } ngx_mp4_stsd_atom_t; |
1728 | |
1729 | |
1730 static ngx_int_t | |
1731 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1732 { | |
1733 u_char *atom_header, *atom_table; | |
1734 size_t atom_size; | |
1735 ngx_buf_t *atom; | |
1736 ngx_mp4_stsd_atom_t *stsd_atom; | |
1737 ngx_http_mp4_trak_t *trak; | |
1738 | |
1739 /* sample description atom */ | |
1740 | |
1741 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom"); | |
1742 | |
1743 atom_header = ngx_mp4_atom_header(mp4); | |
1744 stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header; | |
1745 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1746 atom_table = atom_header + atom_size; | |
1747 ngx_mp4_set_32value(stsd_atom->size, atom_size); | |
1748 ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd'); | |
1749 | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1750 if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t)) |
4106 | 1751 > atom_data_size) |
1752 { | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1753 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1754 "\"%s\" mp4 stsd atom too large", |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1755 mp4->file.name.data); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1756 return NGX_ERROR; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1757 } |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1758 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1759 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1760 "stsd entries:%uD, media:%*s", |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1761 ngx_mp4_get_32value(stsd_atom->entries), |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1762 4, stsd_atom->media_name); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1763 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1764 /* supported media format: "avc1" (H.264) and "mp4a" (MPEG-4/AAC) */ |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1765 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1766 if (ngx_strncmp(stsd_atom->media_name, "avc1", 4) != 0 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1767 && ngx_strncmp(stsd_atom->media_name, "mp4a", 4) != 0) |
4085 | 1768 { |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1769 return NGX_DECLINED; |
4085 | 1770 } |
1771 | |
1772 trak = ngx_mp4_last_trak(mp4); | |
1773 | |
1774 atom = &trak->stsd_atom_buf; | |
1775 atom->temporary = 1; | |
1776 atom->pos = atom_header; | |
1777 atom->last = atom_table; | |
1778 | |
1779 trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom; | |
1780 trak->size += atom_size; | |
1781 | |
1782 ngx_mp4_atom_next(mp4, atom_data_size); | |
1783 | |
1784 return NGX_OK; | |
1785 } | |
1786 | |
1787 | |
1788 typedef struct { | |
1789 u_char size[4]; | |
1790 u_char name[4]; | |
1791 u_char version[1]; | |
1792 u_char flags[3]; | |
1793 u_char entries[4]; | |
1794 } ngx_mp4_stts_atom_t; | |
1795 | |
1796 typedef struct { | |
1797 u_char count[4]; | |
1798 u_char duration[4]; | |
1799 } ngx_mp4_stts_entry_t; | |
1800 | |
1801 | |
1802 static ngx_int_t | |
1803 ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1804 { | |
1805 u_char *atom_header, *atom_table, *atom_end; | |
1806 uint32_t entries; | |
1807 ngx_buf_t *atom, *data; | |
1808 ngx_mp4_stts_atom_t *stts_atom; | |
1809 ngx_http_mp4_trak_t *trak; | |
1810 | |
1811 /* time-to-sample atom */ | |
1812 | |
1813 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom"); | |
1814 | |
1815 atom_header = ngx_mp4_atom_header(mp4); | |
1816 stts_atom = (ngx_mp4_stts_atom_t *) atom_header; | |
1817 ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's'); | |
1818 | |
1819 entries = ngx_mp4_get_32value(stts_atom->entries); | |
1820 | |
1821 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1822 "mp4 time-to-sample entries:%uD", entries); |
4085 | 1823 |
1824 atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t); | |
1825 atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t); | |
1826 | |
1827 if ((uint64_t) (atom_end - stts_atom->version) > atom_data_size) { | |
1828 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1829 "\"%s\" mp4 stts atom too large", | |
1830 mp4->file.name.data); | |
1831 return NGX_ERROR; | |
1832 } | |
1833 | |
1834 trak = ngx_mp4_last_trak(mp4); | |
1835 trak->time_to_sample_entries = entries; | |
1836 | |
1837 atom = &trak->stts_atom_buf; | |
1838 atom->temporary = 1; | |
1839 atom->pos = atom_header; | |
1840 atom->last = atom_table; | |
1841 | |
1842 data = &trak->stts_data_buf; | |
1843 data->temporary = 1; | |
1844 data->pos = atom_table; | |
1845 data->last = atom_end; | |
1846 | |
1847 trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom; | |
1848 trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data; | |
1849 | |
1850 ngx_mp4_atom_next(mp4, atom_data_size); | |
1851 | |
1852 return NGX_OK; | |
1853 } | |
1854 | |
1855 | |
1856 static ngx_int_t | |
1857 ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, | |
1858 ngx_http_mp4_trak_t *trak) | |
1859 { | |
1860 size_t atom_size; | |
1861 uint32_t entries, count, duration; | |
1862 uint64_t start_time; | |
1863 ngx_buf_t *atom, *data; | |
1864 ngx_uint_t start_sample; | |
1865 ngx_mp4_stts_atom_t *stts_atom; | |
1866 ngx_mp4_stts_entry_t *entry, *end; | |
1867 | |
1868 /* | |
1869 * mdia.minf.stbl.stts updating requires trak->timescale | |
1870 * from mdia.mdhd atom which may reside after mdia.minf | |
1871 */ | |
1872 | |
1873 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1874 "mp4 stts atom update"); | |
1875 | |
1876 data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf; | |
1877 | |
1878 if (data == NULL) { | |
1879 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1880 "no mp4 stts atoms were found in \"%s\"", | |
1881 mp4->file.name.data); | |
1882 return NGX_ERROR; | |
1883 } | |
1884 | |
1885 entries = trak->time_to_sample_entries; | |
4189
a12c558503f0
Fixing mp4 module seeking on 32-bit platforms.
Igor Sysoev <igor@sysoev.ru>
parents:
4156
diff
changeset
|
1886 start_time = (uint64_t) mp4->start * trak->timescale / 1000; |
4085 | 1887 |
1888 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1889 "time-to-sample start_time:%uL", start_time); |
4085 | 1890 |
1891 start_sample = 0; | |
1892 entry = (ngx_mp4_stts_entry_t *) data->pos; | |
1893 end = (ngx_mp4_stts_entry_t *) data->last; | |
1894 | |
1895 while (entry < end) { | |
1896 count = ngx_mp4_get_32value(entry->count); | |
1897 duration = ngx_mp4_get_32value(entry->duration); | |
1898 | |
1899 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1900 "count:%uD, duration:%uD", count, duration); | |
1901 | |
4306 | 1902 if (start_time < (uint64_t) count * duration) { |
4085 | 1903 start_sample += (ngx_uint_t) (start_time / duration); |
1904 count -= start_sample; | |
1905 ngx_mp4_set_32value(entry->count, count); | |
1906 goto found; | |
1907 } | |
1908 | |
1909 start_sample += count; | |
1910 start_time -= count * duration; | |
1911 entries--; | |
1912 entry++; | |
1913 } | |
1914 | |
1915 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
4094 | 1916 "start time is out mp4 stts samples in \"%s\"", |
4085 | 1917 mp4->file.name.data); |
1918 | |
1919 return NGX_ERROR; | |
1920 | |
1921 found: | |
1922 | |
1923 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1924 "start_sample:%ui, new count:%uD", start_sample, count); | |
1925 | |
1926 trak->start_sample = start_sample; | |
1927 | |
1928 data->pos = (u_char *) entry; | |
1929 atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos); | |
1930 trak->size += atom_size; | |
1931 | |
1932 atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf; | |
1933 stts_atom = (ngx_mp4_stts_atom_t *) atom->pos; | |
1934 ngx_mp4_set_32value(stts_atom->size, atom_size); | |
1935 ngx_mp4_set_32value(stts_atom->entries, entries); | |
1936 | |
1937 return NGX_OK; | |
1938 } | |
1939 | |
1940 | |
1941 typedef struct { | |
1942 u_char size[4]; | |
1943 u_char name[4]; | |
1944 u_char version[1]; | |
1945 u_char flags[3]; | |
1946 u_char entries[4]; | |
1947 } ngx_http_mp4_stss_atom_t; | |
1948 | |
1949 | |
1950 static ngx_int_t | |
1951 ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1952 { | |
1953 u_char *atom_header, *atom_table, *atom_end; | |
1954 uint32_t entries; | |
1955 ngx_buf_t *atom, *data; | |
1956 ngx_http_mp4_trak_t *trak; | |
1957 ngx_http_mp4_stss_atom_t *stss_atom; | |
1958 | |
1959 /* sync samples atom */ | |
1960 | |
1961 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom"); | |
1962 | |
1963 atom_header = ngx_mp4_atom_header(mp4); | |
1964 stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header; | |
1965 ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's'); | |
1966 | |
1967 entries = ngx_mp4_get_32value(stss_atom->entries); | |
1968 | |
1969 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1970 "sync sample entries:%uD", entries); |
4085 | 1971 |
1972 trak = ngx_mp4_last_trak(mp4); | |
1973 trak->sync_samples_entries = entries; | |
1974 | |
1975 atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t); | |
1976 | |
1977 atom = &trak->stss_atom_buf; | |
1978 atom->temporary = 1; | |
1979 atom->pos = atom_header; | |
1980 atom->last = atom_table; | |
1981 | |
1982 atom_end = atom_table + entries * sizeof(uint32_t); | |
1983 | |
1984 if ((uint64_t) (atom_end - stss_atom->version) > atom_data_size) { | |
1985 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1986 "\"%s\" mp4 stss atom too large", mp4->file.name.data); | |
1987 return NGX_ERROR; | |
1988 } | |
1989 | |
1990 data = &trak->stss_data_buf; | |
1991 data->temporary = 1; | |
1992 data->pos = atom_table; | |
1993 data->last = atom_end; | |
1994 | |
1995 trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom; | |
1996 trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data; | |
1997 | |
1998 ngx_mp4_atom_next(mp4, atom_data_size); | |
1999 | |
2000 return NGX_OK; | |
2001 } | |
2002 | |
2003 | |
2004 static ngx_int_t | |
2005 ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, | |
2006 ngx_http_mp4_trak_t *trak) | |
2007 { | |
2008 size_t atom_size; | |
2009 uint32_t entries, sample, start_sample, *entry, *end; | |
2010 ngx_buf_t *atom, *data; | |
2011 ngx_http_mp4_stss_atom_t *stss_atom; | |
2012 | |
2013 /* | |
2014 * mdia.minf.stbl.stss updating requires trak->start_sample | |
2015 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2016 * atom which may reside after mdia.minf | |
2017 */ | |
2018 | |
2019 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2020 "mp4 stss atom update"); | |
2021 | |
2022 data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf; | |
2023 | |
2024 if (data == NULL) { | |
2025 return NGX_OK; | |
2026 } | |
2027 | |
2028 /* sync samples starts from 1 */ | |
2029 start_sample = trak->start_sample + 1; | |
2030 entries = trak->sync_samples_entries; | |
2031 | |
2032 entry = (uint32_t *) data->pos; | |
2033 end = (uint32_t *) data->last; | |
2034 | |
2035 while (entry < end) { | |
2036 sample = ngx_mp4_get_32value(entry); | |
2037 | |
2038 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2039 "start:%uD, sync:%uD", start_sample, sample); | |
2040 | |
2041 if (sample >= start_sample) { | |
2042 goto found; | |
2043 } | |
2044 | |
2045 entries--; | |
2046 entry++; | |
2047 } | |
2048 | |
2049 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2050 "start sample is out of mp4 stss atom in \"%s\"", | |
2051 mp4->file.name.data); | |
2052 | |
2053 return NGX_ERROR; | |
2054 | |
2055 found: | |
2056 | |
2057 data->pos = (u_char *) entry; | |
2058 | |
2059 start_sample = trak->start_sample; | |
2060 | |
2061 while (entry < end) { | |
2062 sample = ngx_mp4_get_32value(entry); | |
2063 sample -= start_sample; | |
2064 ngx_mp4_set_32value(entry, sample); | |
2065 entry++; | |
2066 } | |
2067 | |
2068 atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos); | |
2069 trak->size += atom_size; | |
2070 | |
2071 atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf; | |
2072 stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos; | |
2073 | |
2074 ngx_mp4_set_32value(stss_atom->size, atom_size); | |
2075 ngx_mp4_set_32value(stss_atom->entries, entries); | |
2076 | |
2077 return NGX_OK; | |
2078 } | |
2079 | |
2080 | |
2081 typedef struct { | |
2082 u_char size[4]; | |
2083 u_char name[4]; | |
2084 u_char version[1]; | |
2085 u_char flags[3]; | |
2086 u_char entries[4]; | |
2087 } ngx_mp4_ctts_atom_t; | |
2088 | |
2089 typedef struct { | |
2090 u_char count[4]; | |
2091 u_char offset[4]; | |
2092 } ngx_mp4_ctts_entry_t; | |
2093 | |
2094 | |
2095 static ngx_int_t | |
2096 ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2097 { | |
2098 u_char *atom_header, *atom_table, *atom_end; | |
2099 uint32_t entries; | |
2100 ngx_buf_t *atom, *data; | |
2101 ngx_mp4_ctts_atom_t *ctts_atom; | |
2102 ngx_http_mp4_trak_t *trak; | |
2103 | |
2104 /* composition offsets atom */ | |
2105 | |
2106 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom"); | |
2107 | |
2108 atom_header = ngx_mp4_atom_header(mp4); | |
2109 ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header; | |
2110 ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's'); | |
2111 | |
2112 entries = ngx_mp4_get_32value(ctts_atom->entries); | |
2113 | |
2114 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 2115 "composition offset entries:%uD", entries); |
4085 | 2116 |
2117 trak = ngx_mp4_last_trak(mp4); | |
2118 trak->composition_offset_entries = entries; | |
2119 | |
2120 atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t); | |
2121 | |
2122 atom = &trak->ctts_atom_buf; | |
2123 atom->temporary = 1; | |
2124 atom->pos = atom_header; | |
2125 atom->last = atom_table; | |
2126 | |
2127 atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t); | |
2128 | |
2129 if ((uint64_t) (atom_end - ctts_atom->version) > atom_data_size) { | |
2130 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2131 "\"%s\" mp4 ctts atom too large", mp4->file.name.data); | |
2132 return NGX_ERROR; | |
2133 } | |
2134 | |
2135 data = &trak->ctts_data_buf; | |
2136 data->temporary = 1; | |
2137 data->pos = atom_table; | |
2138 data->last = atom_end; | |
2139 | |
2140 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom; | |
2141 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data; | |
2142 | |
2143 ngx_mp4_atom_next(mp4, atom_data_size); | |
2144 | |
2145 return NGX_OK; | |
2146 } | |
2147 | |
2148 | |
2149 static void | |
2150 ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, | |
2151 ngx_http_mp4_trak_t *trak) | |
2152 { | |
2153 size_t atom_size; | |
2154 uint32_t entries, count, start_sample; | |
2155 ngx_buf_t *atom, *data; | |
2156 ngx_mp4_ctts_atom_t *ctts_atom; | |
2157 ngx_mp4_ctts_entry_t *entry, *end; | |
2158 | |
2159 /* | |
2160 * mdia.minf.stbl.ctts updating requires trak->start_sample | |
2161 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2162 * atom which may reside after mdia.minf | |
2163 */ | |
2164 | |
2165 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2166 "mp4 ctts atom update"); | |
2167 | |
2168 data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf; | |
2169 | |
2170 if (data == NULL) { | |
2171 return; | |
2172 } | |
2173 | |
2174 /* sync samples starts from 1 */ | |
2175 start_sample = trak->start_sample + 1; | |
2176 entries = trak->composition_offset_entries; | |
2177 entry = (ngx_mp4_ctts_entry_t *) data->pos; | |
2178 end = (ngx_mp4_ctts_entry_t *) data->last; | |
2179 | |
2180 while (entry < end) { | |
2181 count = ngx_mp4_get_32value(entry->count); | |
2182 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
2183 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 2184 "start:%uD, count:%uD, offset:%uD", |
2185 start_sample, count, ngx_mp4_get_32value(entry->offset)); | |
2186 | |
2187 if (start_sample <= count) { | |
2188 count -= (start_sample - 1); | |
2189 ngx_mp4_set_32value(entry->count, count); | |
2190 goto found; | |
2191 } | |
2192 | |
2193 start_sample -= count; | |
2194 entries--; | |
2195 entry++; | |
2196 } | |
2197 | |
2198 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL; | |
2199 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL; | |
2200 | |
2201 return; | |
2202 | |
2203 found: | |
2204 | |
2205 data->pos = (u_char *) entry; | |
2206 atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos); | |
2207 trak->size += atom_size; | |
2208 | |
2209 atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf; | |
2210 ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos; | |
2211 | |
2212 ngx_mp4_set_32value(ctts_atom->size, atom_size); | |
2213 ngx_mp4_set_32value(ctts_atom->entries, entries); | |
2214 | |
2215 return; | |
2216 } | |
2217 | |
2218 | |
2219 typedef struct { | |
2220 u_char size[4]; | |
2221 u_char name[4]; | |
2222 u_char version[1]; | |
2223 u_char flags[3]; | |
2224 u_char entries[4]; | |
2225 } ngx_mp4_stsc_atom_t; | |
2226 | |
2227 | |
2228 static ngx_int_t | |
2229 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2230 { | |
2231 u_char *atom_header, *atom_table, *atom_end; | |
2232 uint32_t entries; | |
2233 ngx_buf_t *atom, *data; | |
2234 ngx_mp4_stsc_atom_t *stsc_atom; | |
2235 ngx_http_mp4_trak_t *trak; | |
2236 | |
2237 /* sample-to-chunk atom */ | |
2238 | |
2239 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom"); | |
2240 | |
2241 atom_header = ngx_mp4_atom_header(mp4); | |
2242 stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header; | |
2243 ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c'); | |
2244 | |
2245 entries = ngx_mp4_get_32value(stsc_atom->entries); | |
2246 | |
2247 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 2248 "sample-to-chunk entries:%uD", entries); |
4085 | 2249 |
2250 atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t); | |
2251 atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t); | |
2252 | |
2253 if ((uint64_t) (atom_end - stsc_atom->version) > atom_data_size) { | |
2254 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2255 "\"%s\" mp4 stsc atom too large", | |
2256 mp4->file.name.data); | |
2257 return NGX_ERROR; | |
2258 } | |
2259 | |
2260 trak = ngx_mp4_last_trak(mp4); | |
2261 trak->sample_to_chunk_entries = entries; | |
2262 | |
2263 atom = &trak->stsc_atom_buf; | |
2264 atom->temporary = 1; | |
2265 atom->pos = atom_header; | |
2266 atom->last = atom_table; | |
2267 | |
2268 data = &trak->stsc_data_buf; | |
2269 data->temporary = 1; | |
2270 data->pos = atom_table; | |
2271 data->last = atom_end; | |
2272 | |
2273 trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom; | |
2274 trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data; | |
2275 | |
2276 ngx_mp4_atom_next(mp4, atom_data_size); | |
2277 | |
2278 return NGX_OK; | |
2279 } | |
2280 | |
2281 | |
2282 static ngx_int_t | |
2283 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, | |
2284 ngx_http_mp4_trak_t *trak) | |
2285 { | |
2286 size_t atom_size; | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2287 uint32_t start_sample, entries, chunk, samples, id, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2288 next_chunk, n; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2289 ngx_buf_t *atom, *data, *buf; |
4085 | 2290 ngx_mp4_stsc_atom_t *stsc_atom; |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2291 ngx_mp4_stsc_entry_t *entry, *first, *end; |
4085 | 2292 |
2293 /* | |
2294 * mdia.minf.stbl.stsc updating requires trak->start_sample | |
2295 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2296 * atom which may reside after mdia.minf | |
2297 */ | |
2298 | |
2299 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2300 "mp4 stsc atom update"); | |
2301 | |
2302 data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf; | |
2303 | |
2304 if (data == NULL) { | |
2305 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2306 "no mp4 stsc atoms were found in \"%s\"", | |
2307 mp4->file.name.data); | |
2308 return NGX_ERROR; | |
2309 } | |
2310 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2311 start_sample = (uint32_t) trak->start_sample; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2312 entries = trak->sample_to_chunk_entries - 1; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2313 |
4085 | 2314 entry = (ngx_mp4_stsc_entry_t *) data->pos; |
2315 end = (ngx_mp4_stsc_entry_t *) data->last; | |
2316 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2317 chunk = ngx_mp4_get_32value(entry->chunk); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2318 samples = ngx_mp4_get_32value(entry->samples); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2319 id = ngx_mp4_get_32value(entry->id); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2320 entry++; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2321 |
4085 | 2322 while (entry < end) { |
2323 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2324 next_chunk = ngx_mp4_get_32value(entry->chunk); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2325 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2326 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2327 "start_sample:%uD, chunk:%uD, chunks:%uD, " |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2328 "samples:%uD, id:%uD", |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2329 start_sample, chunk, next_chunk - chunk, samples, id); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2330 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2331 n = (next_chunk - chunk) * samples; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2332 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2333 if (start_sample <= n) { |
4085 | 2334 goto found; |
2335 } | |
2336 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2337 start_sample -= n; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2338 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2339 chunk = next_chunk; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2340 samples = ngx_mp4_get_32value(entry->samples); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2341 id = ngx_mp4_get_32value(entry->id); |
4085 | 2342 entries--; |
2343 entry++; | |
2344 } | |
2345 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2346 next_chunk = trak->chunks; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2347 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2348 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2349 "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD", |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2350 start_sample, chunk, next_chunk - chunk, samples); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2351 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2352 n = (next_chunk - chunk) * samples; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2353 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2354 if (start_sample > n) { |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2355 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2356 "start time is out mp4 stsc chunks in \"%s\"", |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2357 mp4->file.name.data); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2358 return NGX_ERROR; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2359 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2360 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2361 found: |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2362 |
4085 | 2363 entries++; |
2364 entry--; | |
2365 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2366 if (samples == 0) { |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2367 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2368 "zero number of samples in \"%s\"", |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2369 mp4->file.name.data); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2370 return NGX_ERROR; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2371 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2372 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2373 trak->start_chunk = chunk - 1; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2374 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2375 trak->start_chunk += start_sample / samples; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2376 trak->chunk_samples = start_sample % samples; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2377 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2378 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2379 "start chunk:%ui, samples:%uD", |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2380 trak->start_chunk, trak->chunk_samples); |
4085 | 2381 |
2382 data->pos = (u_char *) entry; | |
2383 atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos); | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2384 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2385 if (trak->chunk_samples) { |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2386 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2387 first = &trak->stsc_chunk_entry; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2388 ngx_mp4_set_32value(first->chunk, 1); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2389 ngx_mp4_set_32value(first->samples, samples - trak->chunk_samples); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2390 ngx_mp4_set_32value(first->id, id); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2391 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2392 buf = &trak->stsc_chunk_buf; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2393 buf->temporary = 1; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2394 buf->pos = (u_char *) first; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2395 buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2396 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2397 trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2398 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2399 ngx_mp4_set_32value(entry->chunk, 2); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2400 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2401 atom_size += sizeof(ngx_mp4_stsc_entry_t); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2402 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2403 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2404 while (++entry < end) { |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2405 chunk = ngx_mp4_get_32value(entry->chunk); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2406 chunk -= trak->start_chunk; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2407 ngx_mp4_set_32value(entry->chunk, chunk); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2408 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2409 |
4085 | 2410 trak->size += atom_size; |
2411 | |
2412 atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf; | |
2413 stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos; | |
2414 | |
2415 ngx_mp4_set_32value(stsc_atom->size, atom_size); | |
2416 ngx_mp4_set_32value(stsc_atom->entries, entries); | |
2417 | |
2418 return NGX_OK; | |
2419 } | |
2420 | |
2421 | |
2422 typedef struct { | |
2423 u_char size[4]; | |
2424 u_char name[4]; | |
2425 u_char version[1]; | |
2426 u_char flags[3]; | |
2427 u_char uniform_size[4]; | |
2428 u_char entries[4]; | |
2429 } ngx_mp4_stsz_atom_t; | |
2430 | |
2431 | |
2432 static ngx_int_t | |
2433 ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2434 { | |
2435 u_char *atom_header, *atom_table, *atom_end; | |
2436 size_t atom_size; | |
2437 uint32_t entries, size; | |
2438 ngx_buf_t *atom, *data; | |
2439 ngx_mp4_stsz_atom_t *stsz_atom; | |
2440 ngx_http_mp4_trak_t *trak; | |
2441 | |
2442 /* sample sizes atom */ | |
2443 | |
2444 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom"); | |
2445 | |
2446 atom_header = ngx_mp4_atom_header(mp4); | |
2447 stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header; | |
2448 ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z'); | |
2449 | |
2450 size = ngx_mp4_get_32value(stsz_atom->uniform_size); | |
2451 entries = ngx_mp4_get_32value(stsz_atom->entries); | |
2452 | |
2453 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2454 "sample uniform size:%uD, entries:%uD", size, entries); | |
2455 | |
2456 trak = ngx_mp4_last_trak(mp4); | |
2457 trak->sample_sizes_entries = entries; | |
2458 | |
2459 atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t); | |
2460 | |
2461 atom = &trak->stsz_atom_buf; | |
2462 atom->temporary = 1; | |
2463 atom->pos = atom_header; | |
2464 atom->last = atom_table; | |
2465 | |
2466 trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom; | |
2467 | |
2468 if (size == 0) { | |
2469 atom_end = atom_table + entries * sizeof(uint32_t); | |
2470 | |
2471 if ((uint64_t) (atom_end - stsz_atom->version) > atom_data_size) { | |
2472 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2473 "\"%s\" mp4 stsz atom too large", | |
2474 mp4->file.name.data); | |
2475 return NGX_ERROR; | |
2476 } | |
2477 | |
2478 data = &trak->stsz_data_buf; | |
2479 data->temporary = 1; | |
2480 data->pos = atom_table; | |
2481 data->last = atom_end; | |
2482 | |
2483 trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data; | |
2484 | |
2485 } else { | |
2486 /* if size != 0 then all samples are the same size */ | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2487 /* TODO : chunk samples */ |
4085 | 2488 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; |
2489 ngx_mp4_set_32value(atom_header, atom_size); | |
2490 trak->size += atom_size; | |
2491 } | |
2492 | |
2493 ngx_mp4_atom_next(mp4, atom_data_size); | |
2494 | |
2495 return NGX_OK; | |
2496 } | |
2497 | |
2498 | |
2499 static void | |
2500 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, | |
2501 ngx_http_mp4_trak_t *trak) | |
2502 { | |
2503 size_t atom_size; | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2504 uint32_t *pos, *end; |
4085 | 2505 ngx_buf_t *atom, *data; |
2506 ngx_mp4_stsz_atom_t *stsz_atom; | |
2507 | |
2508 /* | |
2509 * mdia.minf.stbl.stsz updating requires trak->start_sample | |
2510 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2511 * atom which may reside after mdia.minf | |
2512 */ | |
2513 | |
2514 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2515 "mp4 stsz atom update"); | |
2516 | |
2517 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf; | |
2518 | |
2519 if (data) { | |
2520 data->pos += trak->start_sample * sizeof(uint32_t); | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2521 end = (uint32_t *) data->pos; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2522 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2523 for (pos = end - trak->chunk_samples; pos < end; pos++) { |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2524 trak->chunk_samples_size += ngx_mp4_get_32value(pos); |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2525 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2526 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2527 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4112 | 2528 "chunk samples sizes:%uL", trak->chunk_samples_size); |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2529 |
4085 | 2530 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); |
2531 trak->size += atom_size; | |
2532 | |
2533 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf; | |
2534 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos; | |
2535 | |
2536 ngx_mp4_set_32value(stsz_atom->size, atom_size); | |
2537 ngx_mp4_set_32value(stsz_atom->entries, | |
2538 trak->sample_sizes_entries - trak->start_sample); | |
2539 } | |
2540 } | |
2541 | |
2542 | |
2543 typedef struct { | |
2544 u_char size[4]; | |
2545 u_char name[4]; | |
2546 u_char version[1]; | |
2547 u_char flags[3]; | |
2548 u_char entries[4]; | |
2549 } ngx_mp4_stco_atom_t; | |
2550 | |
2551 | |
2552 static ngx_int_t | |
2553 ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2554 { | |
2555 u_char *atom_header, *atom_table, *atom_end; | |
2556 uint32_t entries; | |
2557 ngx_buf_t *atom, *data; | |
2558 ngx_mp4_stco_atom_t *stco_atom; | |
2559 ngx_http_mp4_trak_t *trak; | |
2560 | |
2561 /* chunk offsets atom */ | |
2562 | |
2563 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom"); | |
2564 | |
2565 atom_header = ngx_mp4_atom_header(mp4); | |
2566 stco_atom = (ngx_mp4_stco_atom_t *) atom_header; | |
2567 ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o'); | |
2568 | |
2569 entries = ngx_mp4_get_32value(stco_atom->entries); | |
2570 | |
2571 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries); | |
2572 | |
2573 atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t); | |
2574 atom_end = atom_table + entries * sizeof(uint32_t); | |
2575 | |
2576 if ((uint64_t) (atom_end - stco_atom->version) > atom_data_size) { | |
2577 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2578 "\"%s\" mp4 stco atom too large", mp4->file.name.data); | |
2579 return NGX_ERROR; | |
2580 } | |
2581 | |
2582 trak = ngx_mp4_last_trak(mp4); | |
2583 trak->chunks = entries; | |
2584 | |
4107 | 2585 atom = &trak->stco_atom_buf; |
4085 | 2586 atom->temporary = 1; |
2587 atom->pos = atom_header; | |
2588 atom->last = atom_table; | |
2589 | |
4107 | 2590 data = &trak->stco_data_buf; |
4085 | 2591 data->temporary = 1; |
2592 data->pos = atom_table; | |
2593 data->last = atom_end; | |
2594 | |
2595 trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom; | |
2596 trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data; | |
2597 | |
2598 ngx_mp4_atom_next(mp4, atom_data_size); | |
2599 | |
2600 return NGX_OK; | |
2601 } | |
2602 | |
2603 | |
2604 static ngx_int_t | |
2605 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, | |
2606 ngx_http_mp4_trak_t *trak) | |
2607 { | |
2608 size_t atom_size; | |
2609 ngx_buf_t *atom, *data; | |
2610 ngx_mp4_stco_atom_t *stco_atom; | |
2611 | |
2612 /* | |
2613 * mdia.minf.stbl.stco updating requires trak->start_chunk | |
2614 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd | |
2615 * atom which may reside after mdia.minf | |
2616 */ | |
2617 | |
2618 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2619 "mp4 stco atom update"); | |
2620 | |
2621 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf; | |
2622 | |
2623 if (data == NULL) { | |
2624 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2625 "no mp4 stco atoms were found in \"%s\"", | |
2626 mp4->file.name.data); | |
2627 return NGX_ERROR; | |
2628 } | |
2629 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2630 data->pos += trak->start_chunk * sizeof(uint32_t); |
4085 | 2631 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); |
2632 trak->size += atom_size; | |
2633 | |
2634 trak->start_offset = ngx_mp4_get_32value(data->pos); | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2635 trak->start_offset += trak->chunk_samples_size; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2636 ngx_mp4_set_32value(data->pos, trak->start_offset); |
4085 | 2637 |
2638 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2639 "start chunk offset:%uD", trak->start_offset); | |
2640 | |
2641 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf; | |
2642 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos; | |
2643 | |
2644 ngx_mp4_set_32value(stco_atom->size, atom_size); | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2645 ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk); |
4085 | 2646 |
2647 return NGX_OK; | |
2648 } | |
2649 | |
2650 | |
2651 static void | |
2652 ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4, | |
2653 ngx_http_mp4_trak_t *trak, int32_t adjustment) | |
2654 { | |
2655 uint32_t offset, *entry, *end; | |
2656 ngx_buf_t *data; | |
2657 | |
2658 /* | |
2659 * moov.trak.mdia.minf.stbl.stco adjustment requires | |
2660 * minimal start offset of all traks and new moov atom size | |
2661 */ | |
2662 | |
2663 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2664 "mp4 stco atom adjustment"); | |
2665 | |
2666 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf; | |
2667 entry = (uint32_t *) data->pos; | |
2668 end = (uint32_t *) data->last; | |
2669 | |
2670 while (entry < end) { | |
2671 offset = ngx_mp4_get_32value(entry); | |
2672 offset += adjustment; | |
2673 ngx_mp4_set_32value(entry, offset); | |
2674 entry++; | |
2675 } | |
2676 } | |
2677 | |
2678 | |
4112 | 2679 typedef struct { |
2680 u_char size[4]; | |
2681 u_char name[4]; | |
2682 u_char version[1]; | |
2683 u_char flags[3]; | |
2684 u_char entries[4]; | |
2685 } ngx_mp4_co64_atom_t; | |
2686 | |
2687 | |
2688 static ngx_int_t | |
2689 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2690 { | |
2691 u_char *atom_header, *atom_table, *atom_end; | |
2692 uint32_t entries; | |
2693 ngx_buf_t *atom, *data; | |
2694 ngx_mp4_co64_atom_t *co64_atom; | |
2695 ngx_http_mp4_trak_t *trak; | |
2696 | |
2697 /* chunk offsets atom */ | |
2698 | |
2699 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom"); | |
2700 | |
2701 atom_header = ngx_mp4_atom_header(mp4); | |
2702 co64_atom = (ngx_mp4_co64_atom_t *) atom_header; | |
2703 ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4'); | |
2704 | |
2705 entries = ngx_mp4_get_32value(co64_atom->entries); | |
2706 | |
2707 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries); | |
2708 | |
2709 atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t); | |
2710 atom_end = atom_table + entries * sizeof(uint64_t); | |
2711 | |
2712 if ((uint64_t) (atom_end - co64_atom->version) > atom_data_size) { | |
2713 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2714 "\"%s\" mp4 co64 atom too large", mp4->file.name.data); | |
2715 return NGX_ERROR; | |
2716 } | |
2717 | |
2718 trak = ngx_mp4_last_trak(mp4); | |
2719 trak->chunks = entries; | |
2720 | |
2721 atom = &trak->co64_atom_buf; | |
2722 atom->temporary = 1; | |
2723 atom->pos = atom_header; | |
2724 atom->last = atom_table; | |
2725 | |
2726 data = &trak->co64_data_buf; | |
2727 data->temporary = 1; | |
2728 data->pos = atom_table; | |
2729 data->last = atom_end; | |
2730 | |
2731 trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom; | |
2732 trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data; | |
2733 | |
2734 ngx_mp4_atom_next(mp4, atom_data_size); | |
2735 | |
2736 return NGX_OK; | |
2737 } | |
2738 | |
2739 | |
2740 static ngx_int_t | |
2741 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, | |
2742 ngx_http_mp4_trak_t *trak) | |
2743 { | |
2744 size_t atom_size; | |
2745 ngx_buf_t *atom, *data; | |
2746 ngx_mp4_co64_atom_t *co64_atom; | |
2747 | |
2748 /* | |
2749 * mdia.minf.stbl.co64 updating requires trak->start_chunk | |
2750 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd | |
2751 * atom which may reside after mdia.minf | |
2752 */ | |
2753 | |
2754 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2755 "mp4 co64 atom update"); | |
2756 | |
2757 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; | |
2758 | |
2759 if (data == NULL) { | |
2760 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2761 "no mp4 co64 atoms were found in \"%s\"", | |
2762 mp4->file.name.data); | |
2763 return NGX_ERROR; | |
2764 } | |
2765 | |
2766 data->pos += trak->start_chunk * sizeof(uint64_t); | |
2767 atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); | |
2768 trak->size += atom_size; | |
2769 | |
2770 trak->start_offset = ngx_mp4_get_64value(data->pos); | |
2771 trak->start_offset += trak->chunk_samples_size; | |
2772 ngx_mp4_set_64value(data->pos, trak->start_offset); | |
2773 | |
2774 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2775 "start chunk offset:%uL", trak->start_offset); | |
2776 | |
2777 atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf; | |
2778 co64_atom = (ngx_mp4_co64_atom_t *) atom->pos; | |
2779 | |
2780 ngx_mp4_set_32value(co64_atom->size, atom_size); | |
2781 ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk); | |
2782 | |
2783 return NGX_OK; | |
2784 } | |
2785 | |
2786 | |
2787 static void | |
2788 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4, | |
2789 ngx_http_mp4_trak_t *trak, off_t adjustment) | |
2790 { | |
2791 uint64_t offset, *entry, *end; | |
2792 ngx_buf_t *data; | |
2793 | |
2794 /* | |
2795 * moov.trak.mdia.minf.stbl.co64 adjustment requires | |
2796 * minimal start offset of all traks and new moov atom size | |
2797 */ | |
2798 | |
2799 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2800 "mp4 co64 atom adjustment"); | |
2801 | |
2802 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; | |
2803 entry = (uint64_t *) data->pos; | |
2804 end = (uint64_t *) data->last; | |
2805 | |
2806 while (entry < end) { | |
2807 offset = ngx_mp4_get_64value(entry); | |
2808 offset += adjustment; | |
2809 ngx_mp4_set_64value(entry, offset); | |
2810 entry++; | |
2811 } | |
2812 } | |
2813 | |
2814 | |
4085 | 2815 static char * |
2816 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
2817 { | |
2818 ngx_http_core_loc_conf_t *clcf; | |
2819 | |
2820 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); | |
2821 clcf->handler = ngx_http_mp4_handler; | |
2822 | |
2823 return NGX_CONF_OK; | |
2824 } | |
2825 | |
2826 | |
2827 static void * | |
2828 ngx_http_mp4_create_conf(ngx_conf_t *cf) | |
2829 { | |
2830 ngx_http_mp4_conf_t *conf; | |
2831 | |
2832 conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t)); | |
2833 if (conf == NULL) { | |
2834 return NULL; | |
2835 } | |
2836 | |
2837 conf->buffer_size = NGX_CONF_UNSET_SIZE; | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
2838 conf->max_buffer_size = NGX_CONF_UNSET_SIZE; |
4085 | 2839 |
2840 return conf; | |
2841 } | |
2842 | |
2843 | |
2844 static char * | |
2845 ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
2846 { | |
2847 ngx_http_mp4_conf_t *prev = parent; | |
2848 ngx_http_mp4_conf_t *conf = child; | |
2849 | |
2850 ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024); | |
4089
e27670e1ab70
mp4_max_moov_size directive has been renamed to mp4_max_buffer_size.
Igor Sysoev <igor@sysoev.ru>
parents:
4088
diff
changeset
|
2851 ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size, |
4085 | 2852 10 * 1024 * 1024); |
2853 | |
2854 return NGX_CONF_OK; | |
2855 } |