Mercurial > hg > nginx-quic
annotate src/http/modules/ngx_http_mp4_module.c @ 4207:4fc91bae6f83
Better recheck of dead upstream servers.
Previously nginx used to mark backend again as live as soon as fail_timeout
passes (10s by default) since last failure. On the other hand, detecting
dead backend takes up to 60s (proxy_connect_timeout) in typical situation
"backend is down and doesn't respond to any packets". This resulted in
suboptimal behaviour in the above situation (up to 23% of requests were
directed to dead backend with default settings).
More detailed description of the problem may be found here (in Russian):
http://mailman.nginx.org/pipermail/nginx-ru/2011-August/042172.html
Fix is to only allow one request after fail_timeout passes, and
mark backend as "live" only if this request succeeds.
Note that with new code backend will not be marked "live" unless "check"
request is completed, and this may take a while in some specific workloads
(e.g. streaming). This is believed to be acceptable.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 12 Oct 2011 14:22:48 +0000 |
parents | a12c558503f0 |
children | 71cdac0b9ea6 |
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 mp4->content_length += atom_data_size; | |
1070 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
1071 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 1072 "mdat new offset @%O:%O", start_offset, atom_data_size); |
1073 | |
1074 atom_header = mp4->mdat_atom_header; | |
1075 | |
4155
d9636bf3f159
Fix of building on platforms with 32-bit off_t. (closed #23)
Igor Sysoev <igor@sysoev.ru>
parents:
4112
diff
changeset
|
1076 if ((uint64_t) atom_data_size > 0xffffffff) { |
4085 | 1077 atom_size = 1; |
1078 atom_header_size = sizeof(ngx_mp4_atom_header64_t); | |
1079 ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t), | |
1080 sizeof(ngx_mp4_atom_header64_t) + atom_data_size); | |
1081 } else { | |
1082 atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size; | |
1083 atom_header_size = sizeof(ngx_mp4_atom_header_t); | |
1084 } | |
1085 | |
1086 ngx_mp4_set_32value(atom_header, atom_size); | |
1087 ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't'); | |
1088 | |
1089 atom = &mp4->mdat_atom_buf; | |
1090 atom->temporary = 1; | |
1091 atom->pos = atom_header; | |
1092 atom->last = atom_header + atom_header_size; | |
1093 | |
1094 return atom_header_size; | |
1095 } | |
1096 | |
1097 | |
1098 typedef struct { | |
1099 u_char size[4]; | |
1100 u_char name[4]; | |
1101 u_char version[1]; | |
1102 u_char flags[3]; | |
1103 u_char creation_time[4]; | |
1104 u_char modification_time[4]; | |
1105 u_char timescale[4]; | |
1106 u_char duration[4]; | |
1107 u_char rate[4]; | |
1108 u_char volume[2]; | |
1109 u_char reserved[10]; | |
1110 u_char matrix[36]; | |
1111 u_char preview_time[4]; | |
1112 u_char preview_duration[4]; | |
1113 u_char poster_time[4]; | |
1114 u_char selection_time[4]; | |
1115 u_char selection_duration[4]; | |
1116 u_char current_time[4]; | |
1117 u_char next_track_id[4]; | |
1118 } ngx_mp4_mvhd_atom_t; | |
1119 | |
1120 typedef struct { | |
1121 u_char size[4]; | |
1122 u_char name[4]; | |
1123 u_char version[1]; | |
1124 u_char flags[3]; | |
1125 u_char creation_time[8]; | |
1126 u_char modification_time[8]; | |
1127 u_char timescale[4]; | |
1128 u_char duration[8]; | |
1129 u_char rate[4]; | |
1130 u_char volume[2]; | |
1131 u_char reserved[10]; | |
1132 u_char matrix[36]; | |
1133 u_char preview_time[4]; | |
1134 u_char preview_duration[4]; | |
1135 u_char poster_time[4]; | |
1136 u_char selection_time[4]; | |
1137 u_char selection_duration[4]; | |
1138 u_char current_time[4]; | |
1139 u_char next_track_id[4]; | |
1140 } ngx_mp4_mvhd64_atom_t; | |
1141 | |
1142 | |
1143 static ngx_int_t | |
1144 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1145 { | |
1146 u_char *atom_header; | |
1147 size_t atom_size; | |
1148 uint32_t timescale; | |
1149 uint64_t duration; | |
1150 ngx_buf_t *atom; | |
1151 ngx_mp4_mvhd_atom_t *mvhd_atom; | |
1152 ngx_mp4_mvhd64_atom_t *mvhd64_atom; | |
1153 | |
1154 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom"); | |
1155 | |
1156 atom_header = ngx_mp4_atom_header(mp4); | |
1157 mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header; | |
1158 mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header; | |
1159 ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd'); | |
1160 | |
1161 if (mvhd_atom->version[0] == 0) { | |
1162 /* version 0: 32-bit duration */ | |
1163 timescale = ngx_mp4_get_32value(mvhd_atom->timescale); | |
1164 duration = ngx_mp4_get_32value(mvhd_atom->duration); | |
1165 | |
1166 } else { | |
1167 /* version 1: 64-bit duration */ | |
1168 timescale = ngx_mp4_get_32value(mvhd64_atom->timescale); | |
1169 duration = ngx_mp4_get_64value(mvhd64_atom->duration); | |
1170 } | |
1171 | |
1172 mp4->timescale = timescale; | |
1173 | |
1174 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1175 "mvhd timescale:%uD, duration:%uL, time:%.3fs", | |
1176 timescale, duration, (double) duration / timescale); | |
1177 | |
1178 duration -= (uint64_t) mp4->start * timescale / 1000; | |
1179 | |
1180 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1181 "mvhd new duration:%uL, time:%.3fs", | |
1182 duration, (double) duration / timescale); | |
1183 | |
1184 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1185 ngx_mp4_set_32value(mvhd_atom->size, atom_size); | |
1186 | |
1187 if (mvhd_atom->version[0] == 0) { | |
1188 ngx_mp4_set_32value(mvhd_atom->duration, duration); | |
1189 | |
1190 } else { | |
1191 ngx_mp4_set_64value(mvhd64_atom->duration, duration); | |
1192 } | |
1193 | |
1194 atom = &mp4->mvhd_atom_buf; | |
1195 atom->temporary = 1; | |
1196 atom->pos = atom_header; | |
1197 atom->last = atom_header + atom_size; | |
1198 | |
1199 mp4->mvhd_atom.buf = atom; | |
1200 | |
1201 ngx_mp4_atom_next(mp4, atom_data_size); | |
1202 | |
1203 return NGX_OK; | |
1204 } | |
1205 | |
1206 | |
1207 static ngx_int_t | |
1208 ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1209 { | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1210 u_char *atom_header, *atom_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1211 off_t atom_file_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1212 ngx_int_t rc; |
4085 | 1213 ngx_buf_t *atom; |
1214 ngx_http_mp4_trak_t *trak; | |
1215 | |
1216 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom"); | |
1217 | |
1218 trak = ngx_array_push(&mp4->trak); | |
1219 if (trak == NULL) { | |
1220 return NGX_ERROR; | |
1221 } | |
1222 | |
1223 ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t)); | |
1224 | |
1225 atom_header = ngx_mp4_atom_header(mp4); | |
1226 ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k'); | |
1227 | |
1228 atom = &trak->trak_atom_buf; | |
1229 atom->temporary = 1; | |
1230 atom->pos = atom_header; | |
1231 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1232 | |
1233 trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom; | |
1234 | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1235 atom_end = mp4->buffer_pos + atom_data_size; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1236 atom_file_end = mp4->offset + atom_data_size; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1237 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1238 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
|
1239 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1240 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
|
1241 "mp4 trak atom: %i", rc); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1242 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1243 if (rc == NGX_DECLINED) { |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1244 /* skip this trak */ |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1245 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
|
1246 mp4->trak.nelts--; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1247 mp4->buffer_pos = atom_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1248 mp4->offset = atom_file_end; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1249 return NGX_OK; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1250 } |
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 return rc; |
4085 | 1253 } |
1254 | |
1255 | |
1256 static void | |
1257 ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4, | |
1258 ngx_http_mp4_trak_t *trak) | |
1259 { | |
1260 ngx_buf_t *atom; | |
1261 | |
1262 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1263 atom = &trak->trak_atom_buf; | |
1264 ngx_mp4_set_32value(atom->pos, trak->size); | |
1265 } | |
1266 | |
1267 | |
1268 static ngx_int_t | |
1269 ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1270 { | |
1271 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1272 "\"%s\" mp4 compressed moov atom (cmov) is not supported", | |
1273 mp4->file.name.data); | |
1274 | |
1275 return NGX_ERROR; | |
1276 } | |
1277 | |
1278 | |
1279 typedef struct { | |
1280 u_char size[4]; | |
1281 u_char name[4]; | |
1282 u_char version[1]; | |
1283 u_char flags[3]; | |
1284 u_char creation_time[4]; | |
1285 u_char modification_time[4]; | |
1286 u_char track_id[4]; | |
1287 u_char reserved1[4]; | |
1288 u_char duration[4]; | |
1289 u_char reserved2[8]; | |
1290 u_char layer[2]; | |
1291 u_char group[2]; | |
1292 u_char volume[2]; | |
1293 u_char reverved3[2]; | |
1294 u_char matrix[36]; | |
1295 u_char width[4]; | |
1296 u_char heigth[4]; | |
1297 } ngx_mp4_tkhd_atom_t; | |
1298 | |
1299 typedef struct { | |
1300 u_char size[4]; | |
1301 u_char name[4]; | |
1302 u_char version[1]; | |
1303 u_char flags[3]; | |
1304 u_char creation_time[8]; | |
1305 u_char modification_time[8]; | |
1306 u_char track_id[4]; | |
1307 u_char reserved1[4]; | |
1308 u_char duration[8]; | |
1309 u_char reserved2[8]; | |
1310 u_char layer[2]; | |
1311 u_char group[2]; | |
1312 u_char volume[2]; | |
1313 u_char reverved3[2]; | |
1314 u_char matrix[36]; | |
1315 u_char width[4]; | |
1316 u_char heigth[4]; | |
1317 } ngx_mp4_tkhd64_atom_t; | |
1318 | |
1319 | |
1320 static ngx_int_t | |
1321 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1322 { | |
1323 u_char *atom_header; | |
1324 size_t atom_size; | |
1325 uint64_t duration; | |
1326 ngx_buf_t *atom; | |
1327 ngx_http_mp4_trak_t *trak; | |
1328 ngx_mp4_tkhd_atom_t *tkhd_atom; | |
1329 ngx_mp4_tkhd64_atom_t *tkhd64_atom; | |
1330 | |
1331 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom"); | |
1332 | |
1333 atom_header = ngx_mp4_atom_header(mp4); | |
1334 tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header; | |
1335 tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header; | |
1336 ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd'); | |
1337 | |
1338 if (tkhd_atom->version[0] == 0) { | |
1339 /* version 0: 32-bit duration */ | |
1340 duration = ngx_mp4_get_32value(tkhd_atom->duration); | |
1341 | |
1342 } else { | |
1343 /* version 1: 64-bit duration */ | |
1344 duration = ngx_mp4_get_64value(tkhd64_atom->duration); | |
1345 } | |
1346 | |
1347 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1348 "tkhd duration:%uL, time:%.3fs", | |
1349 duration, (double) duration / mp4->timescale); | |
1350 | |
1351 duration -= (uint64_t) mp4->start * mp4->timescale / 1000; | |
1352 | |
1353 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1354 "tkhd new duration:%uL, time:%.3fs", | |
1355 duration, (double) duration / mp4->timescale); | |
1356 | |
1357 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1358 | |
1359 trak = ngx_mp4_last_trak(mp4); | |
1360 trak->tkhd_size = atom_size; | |
1361 | |
1362 ngx_mp4_set_32value(tkhd_atom->size, atom_size); | |
1363 | |
1364 if (tkhd_atom->version[0] == 0) { | |
1365 ngx_mp4_set_32value(tkhd_atom->duration, duration); | |
1366 | |
1367 } else { | |
1368 ngx_mp4_set_64value(tkhd64_atom->duration, duration); | |
1369 } | |
1370 | |
1371 atom = &trak->tkhd_atom_buf; | |
1372 atom->temporary = 1; | |
1373 atom->pos = atom_header; | |
1374 atom->last = atom_header + atom_size; | |
1375 | |
1376 trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom; | |
1377 | |
1378 ngx_mp4_atom_next(mp4, atom_data_size); | |
1379 | |
1380 return NGX_OK; | |
1381 } | |
1382 | |
1383 | |
1384 static ngx_int_t | |
1385 ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1386 { | |
1387 u_char *atom_header; | |
1388 ngx_buf_t *atom; | |
1389 ngx_http_mp4_trak_t *trak; | |
1390 | |
1391 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom"); | |
1392 | |
1393 atom_header = ngx_mp4_atom_header(mp4); | |
1394 ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a'); | |
1395 | |
1396 trak = ngx_mp4_last_trak(mp4); | |
1397 | |
1398 atom = &trak->mdia_atom_buf; | |
1399 atom->temporary = 1; | |
1400 atom->pos = atom_header; | |
1401 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1402 | |
1403 trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom; | |
1404 | |
1405 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size); | |
1406 } | |
1407 | |
1408 | |
1409 static void | |
1410 ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4, | |
1411 ngx_http_mp4_trak_t *trak) | |
1412 { | |
1413 ngx_buf_t *atom; | |
1414 | |
1415 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1416 atom = &trak->mdia_atom_buf; | |
1417 ngx_mp4_set_32value(atom->pos, trak->size); | |
1418 } | |
1419 | |
1420 | |
1421 typedef struct { | |
1422 u_char size[4]; | |
1423 u_char name[4]; | |
1424 u_char version[1]; | |
1425 u_char flags[3]; | |
1426 u_char creation_time[4]; | |
1427 u_char modification_time[4]; | |
1428 u_char timescale[4]; | |
1429 u_char duration[4]; | |
1430 u_char language[2]; | |
1431 u_char quality[2]; | |
1432 } ngx_mp4_mdhd_atom_t; | |
1433 | |
1434 typedef struct { | |
1435 u_char size[4]; | |
1436 u_char name[4]; | |
1437 u_char version[1]; | |
1438 u_char flags[3]; | |
1439 u_char creation_time[8]; | |
1440 u_char modification_time[8]; | |
1441 u_char timescale[4]; | |
1442 u_char duration[8]; | |
1443 u_char language[2]; | |
1444 u_char quality[2]; | |
1445 } ngx_mp4_mdhd64_atom_t; | |
1446 | |
1447 | |
1448 static ngx_int_t | |
1449 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1450 { | |
1451 u_char *atom_header; | |
1452 size_t atom_size; | |
1453 uint32_t timescale; | |
1454 uint64_t duration; | |
1455 ngx_buf_t *atom; | |
1456 ngx_http_mp4_trak_t *trak; | |
1457 ngx_mp4_mdhd_atom_t *mdhd_atom; | |
1458 ngx_mp4_mdhd64_atom_t *mdhd64_atom; | |
1459 | |
1460 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom"); | |
1461 | |
1462 atom_header = ngx_mp4_atom_header(mp4); | |
1463 mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header; | |
1464 mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header; | |
1465 ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd'); | |
1466 | |
1467 if (mdhd_atom->version[0] == 0) { | |
1468 /* version 0: everything is 32-bit */ | |
1469 timescale = ngx_mp4_get_32value(mdhd_atom->timescale); | |
1470 duration = ngx_mp4_get_32value(mdhd_atom->duration); | |
1471 | |
1472 } else { | |
1473 /* version 1: 64-bit duration and 32-bit timescale */ | |
1474 timescale = ngx_mp4_get_32value(mdhd64_atom->timescale); | |
1475 duration = ngx_mp4_get_64value(mdhd64_atom->duration); | |
1476 } | |
1477 | |
1478 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1479 "mdhd timescale:%uD, duration:%uL, time:%.3fs", | |
1480 timescale, duration, (double) duration / timescale); | |
1481 | |
1482 duration -= (uint64_t) mp4->start * timescale / 1000; | |
1483 | |
1484 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1485 "mdhd new duration:%uL, time:%.3fs", | |
1486 duration, (double) duration / timescale); | |
1487 | |
1488 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1489 | |
1490 trak = ngx_mp4_last_trak(mp4); | |
1491 trak->mdhd_size = atom_size; | |
1492 trak->timescale = timescale; | |
1493 | |
1494 ngx_mp4_set_32value(mdhd_atom->size, atom_size); | |
1495 | |
1496 if (mdhd_atom->version[0] == 0) { | |
1497 ngx_mp4_set_32value(mdhd_atom->duration, duration); | |
1498 | |
1499 } else { | |
1500 ngx_mp4_set_64value(mdhd64_atom->duration, duration); | |
1501 } | |
1502 | |
1503 atom = &trak->mdhd_atom_buf; | |
1504 atom->temporary = 1; | |
1505 atom->pos = atom_header; | |
1506 atom->last = atom_header + atom_size; | |
1507 | |
1508 trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom; | |
1509 | |
1510 ngx_mp4_atom_next(mp4, atom_data_size); | |
1511 | |
1512 return NGX_OK; | |
1513 } | |
1514 | |
1515 | |
1516 static ngx_int_t | |
1517 ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1518 { | |
1519 u_char *atom_header; | |
1520 size_t atom_size; | |
1521 ngx_buf_t *atom; | |
1522 ngx_http_mp4_trak_t *trak; | |
1523 | |
1524 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom"); | |
1525 | |
1526 atom_header = ngx_mp4_atom_header(mp4); | |
1527 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1528 ngx_mp4_set_32value(atom_header, atom_size); | |
1529 ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r'); | |
1530 | |
1531 trak = ngx_mp4_last_trak(mp4); | |
1532 | |
1533 atom = &trak->hdlr_atom_buf; | |
1534 atom->temporary = 1; | |
1535 atom->pos = atom_header; | |
1536 atom->last = atom_header + atom_size; | |
1537 | |
1538 trak->hdlr_size = atom_size; | |
1539 trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom; | |
1540 | |
1541 ngx_mp4_atom_next(mp4, atom_data_size); | |
1542 | |
1543 return NGX_OK; | |
1544 } | |
1545 | |
1546 | |
1547 static ngx_int_t | |
1548 ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1549 { | |
1550 u_char *atom_header; | |
1551 ngx_buf_t *atom; | |
1552 ngx_http_mp4_trak_t *trak; | |
1553 | |
1554 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom"); | |
1555 | |
1556 atom_header = ngx_mp4_atom_header(mp4); | |
1557 ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f'); | |
1558 | |
1559 trak = ngx_mp4_last_trak(mp4); | |
1560 | |
1561 atom = &trak->minf_atom_buf; | |
1562 atom->temporary = 1; | |
1563 atom->pos = atom_header; | |
1564 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1565 | |
1566 trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom; | |
1567 | |
1568 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size); | |
1569 } | |
1570 | |
1571 | |
1572 static void | |
1573 ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4, | |
1574 ngx_http_mp4_trak_t *trak) | |
1575 { | |
1576 ngx_buf_t *atom; | |
1577 | |
1578 trak->size += sizeof(ngx_mp4_atom_header_t) | |
1579 + trak->vmhd_size | |
1580 + trak->smhd_size | |
1581 + trak->dinf_size; | |
1582 atom = &trak->minf_atom_buf; | |
1583 ngx_mp4_set_32value(atom->pos, trak->size); | |
1584 } | |
1585 | |
1586 | |
1587 static ngx_int_t | |
1588 ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1589 { | |
1590 u_char *atom_header; | |
1591 size_t atom_size; | |
1592 ngx_buf_t *atom; | |
1593 ngx_http_mp4_trak_t *trak; | |
1594 | |
1595 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom"); | |
1596 | |
1597 atom_header = ngx_mp4_atom_header(mp4); | |
1598 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1599 ngx_mp4_set_32value(atom_header, atom_size); | |
1600 ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd'); | |
1601 | |
1602 trak = ngx_mp4_last_trak(mp4); | |
1603 | |
1604 atom = &trak->vmhd_atom_buf; | |
1605 atom->temporary = 1; | |
1606 atom->pos = atom_header; | |
1607 atom->last = atom_header + atom_size; | |
1608 | |
1609 trak->vmhd_size += atom_size; | |
1610 trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom; | |
1611 | |
1612 ngx_mp4_atom_next(mp4, atom_data_size); | |
1613 | |
1614 return NGX_OK; | |
1615 } | |
1616 | |
1617 | |
1618 static ngx_int_t | |
1619 ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1620 { | |
1621 u_char *atom_header; | |
1622 size_t atom_size; | |
1623 ngx_buf_t *atom; | |
1624 ngx_http_mp4_trak_t *trak; | |
1625 | |
1626 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom"); | |
1627 | |
1628 atom_header = ngx_mp4_atom_header(mp4); | |
1629 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1630 ngx_mp4_set_32value(atom_header, atom_size); | |
1631 ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd'); | |
1632 | |
1633 trak = ngx_mp4_last_trak(mp4); | |
1634 | |
1635 atom = &trak->smhd_atom_buf; | |
1636 atom->temporary = 1; | |
1637 atom->pos = atom_header; | |
1638 atom->last = atom_header + atom_size; | |
1639 | |
1640 trak->vmhd_size += atom_size; | |
1641 trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom; | |
1642 | |
1643 ngx_mp4_atom_next(mp4, atom_data_size); | |
1644 | |
1645 return NGX_OK; | |
1646 } | |
1647 | |
1648 | |
1649 static ngx_int_t | |
1650 ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1651 { | |
1652 u_char *atom_header; | |
1653 size_t atom_size; | |
1654 ngx_buf_t *atom; | |
1655 ngx_http_mp4_trak_t *trak; | |
1656 | |
1657 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom"); | |
1658 | |
1659 atom_header = ngx_mp4_atom_header(mp4); | |
1660 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1661 ngx_mp4_set_32value(atom_header, atom_size); | |
1662 ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f'); | |
1663 | |
1664 trak = ngx_mp4_last_trak(mp4); | |
1665 | |
1666 atom = &trak->dinf_atom_buf; | |
1667 atom->temporary = 1; | |
1668 atom->pos = atom_header; | |
1669 atom->last = atom_header + atom_size; | |
1670 | |
1671 trak->dinf_size += atom_size; | |
1672 trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom; | |
1673 | |
1674 ngx_mp4_atom_next(mp4, atom_data_size); | |
1675 | |
1676 return NGX_OK; | |
1677 } | |
1678 | |
1679 | |
1680 static ngx_int_t | |
1681 ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1682 { | |
1683 u_char *atom_header; | |
1684 ngx_buf_t *atom; | |
1685 ngx_http_mp4_trak_t *trak; | |
1686 | |
1687 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom"); | |
1688 | |
1689 atom_header = ngx_mp4_atom_header(mp4); | |
1690 ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l'); | |
1691 | |
1692 trak = ngx_mp4_last_trak(mp4); | |
1693 | |
1694 atom = &trak->stbl_atom_buf; | |
1695 atom->temporary = 1; | |
1696 atom->pos = atom_header; | |
1697 atom->last = atom_header + sizeof(ngx_mp4_atom_header_t); | |
1698 | |
1699 trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom; | |
1700 | |
1701 return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size); | |
1702 } | |
1703 | |
1704 | |
1705 static void | |
1706 ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4, | |
1707 ngx_http_mp4_trak_t *trak) | |
1708 { | |
1709 ngx_buf_t *atom; | |
1710 | |
1711 trak->size += sizeof(ngx_mp4_atom_header_t); | |
1712 atom = &trak->stbl_atom_buf; | |
1713 ngx_mp4_set_32value(atom->pos, trak->size); | |
1714 } | |
1715 | |
1716 | |
1717 typedef struct { | |
1718 u_char size[4]; | |
1719 u_char name[4]; | |
1720 u_char version[1]; | |
1721 u_char flags[3]; | |
1722 u_char entries[4]; | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1723 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1724 u_char media_size[4]; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1725 u_char media_name[4]; |
4085 | 1726 } ngx_mp4_stsd_atom_t; |
1727 | |
1728 | |
1729 static ngx_int_t | |
1730 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1731 { | |
1732 u_char *atom_header, *atom_table; | |
1733 size_t atom_size; | |
1734 ngx_buf_t *atom; | |
1735 ngx_mp4_stsd_atom_t *stsd_atom; | |
1736 ngx_http_mp4_trak_t *trak; | |
1737 | |
1738 /* sample description atom */ | |
1739 | |
1740 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom"); | |
1741 | |
1742 atom_header = ngx_mp4_atom_header(mp4); | |
1743 stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header; | |
1744 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; | |
1745 atom_table = atom_header + atom_size; | |
1746 ngx_mp4_set_32value(stsd_atom->size, atom_size); | |
1747 ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd'); | |
1748 | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1749 if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t)) |
4106 | 1750 > atom_data_size) |
1751 { | |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1752 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
|
1753 "\"%s\" mp4 stsd atom too large", |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1754 mp4->file.name.data); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1755 return NGX_ERROR; |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1756 } |
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 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
|
1759 "stsd entries:%uD, media:%*s", |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1760 ngx_mp4_get_32value(stsd_atom->entries), |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1761 4, stsd_atom->media_name); |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1762 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1763 /* 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
|
1764 |
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1765 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
|
1766 && ngx_strncmp(stsd_atom->media_name, "mp4a", 4) != 0) |
4085 | 1767 { |
4099
9ee6944590c0
Skipping traks with unsupported media formats.
Igor Sysoev <igor@sysoev.ru>
parents:
4098
diff
changeset
|
1768 return NGX_DECLINED; |
4085 | 1769 } |
1770 | |
1771 trak = ngx_mp4_last_trak(mp4); | |
1772 | |
1773 atom = &trak->stsd_atom_buf; | |
1774 atom->temporary = 1; | |
1775 atom->pos = atom_header; | |
1776 atom->last = atom_table; | |
1777 | |
1778 trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom; | |
1779 trak->size += atom_size; | |
1780 | |
1781 ngx_mp4_atom_next(mp4, atom_data_size); | |
1782 | |
1783 return NGX_OK; | |
1784 } | |
1785 | |
1786 | |
1787 typedef struct { | |
1788 u_char size[4]; | |
1789 u_char name[4]; | |
1790 u_char version[1]; | |
1791 u_char flags[3]; | |
1792 u_char entries[4]; | |
1793 } ngx_mp4_stts_atom_t; | |
1794 | |
1795 typedef struct { | |
1796 u_char count[4]; | |
1797 u_char duration[4]; | |
1798 } ngx_mp4_stts_entry_t; | |
1799 | |
1800 | |
1801 static ngx_int_t | |
1802 ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1803 { | |
1804 u_char *atom_header, *atom_table, *atom_end; | |
1805 uint32_t entries; | |
1806 ngx_buf_t *atom, *data; | |
1807 ngx_mp4_stts_atom_t *stts_atom; | |
1808 ngx_http_mp4_trak_t *trak; | |
1809 | |
1810 /* time-to-sample atom */ | |
1811 | |
1812 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom"); | |
1813 | |
1814 atom_header = ngx_mp4_atom_header(mp4); | |
1815 stts_atom = (ngx_mp4_stts_atom_t *) atom_header; | |
1816 ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's'); | |
1817 | |
1818 entries = ngx_mp4_get_32value(stts_atom->entries); | |
1819 | |
1820 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1821 "mp4 time-to-sample entries:%uD", entries); |
4085 | 1822 |
1823 atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t); | |
1824 atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t); | |
1825 | |
1826 if ((uint64_t) (atom_end - stts_atom->version) > atom_data_size) { | |
1827 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1828 "\"%s\" mp4 stts atom too large", | |
1829 mp4->file.name.data); | |
1830 return NGX_ERROR; | |
1831 } | |
1832 | |
1833 trak = ngx_mp4_last_trak(mp4); | |
1834 trak->time_to_sample_entries = entries; | |
1835 | |
1836 atom = &trak->stts_atom_buf; | |
1837 atom->temporary = 1; | |
1838 atom->pos = atom_header; | |
1839 atom->last = atom_table; | |
1840 | |
1841 data = &trak->stts_data_buf; | |
1842 data->temporary = 1; | |
1843 data->pos = atom_table; | |
1844 data->last = atom_end; | |
1845 | |
1846 trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom; | |
1847 trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data; | |
1848 | |
1849 ngx_mp4_atom_next(mp4, atom_data_size); | |
1850 | |
1851 return NGX_OK; | |
1852 } | |
1853 | |
1854 | |
1855 static ngx_int_t | |
1856 ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, | |
1857 ngx_http_mp4_trak_t *trak) | |
1858 { | |
1859 size_t atom_size; | |
1860 uint32_t entries, count, duration; | |
1861 uint64_t start_time; | |
1862 ngx_buf_t *atom, *data; | |
1863 ngx_uint_t start_sample; | |
1864 ngx_mp4_stts_atom_t *stts_atom; | |
1865 ngx_mp4_stts_entry_t *entry, *end; | |
1866 | |
1867 /* | |
1868 * mdia.minf.stbl.stts updating requires trak->timescale | |
1869 * from mdia.mdhd atom which may reside after mdia.minf | |
1870 */ | |
1871 | |
1872 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1873 "mp4 stts atom update"); | |
1874 | |
1875 data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf; | |
1876 | |
1877 if (data == NULL) { | |
1878 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1879 "no mp4 stts atoms were found in \"%s\"", | |
1880 mp4->file.name.data); | |
1881 return NGX_ERROR; | |
1882 } | |
1883 | |
1884 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
|
1885 start_time = (uint64_t) mp4->start * trak->timescale / 1000; |
4085 | 1886 |
1887 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1888 "time-to-sample start_time:%uL", start_time); |
4085 | 1889 |
1890 start_sample = 0; | |
1891 entry = (ngx_mp4_stts_entry_t *) data->pos; | |
1892 end = (ngx_mp4_stts_entry_t *) data->last; | |
1893 | |
1894 while (entry < end) { | |
1895 count = ngx_mp4_get_32value(entry->count); | |
1896 duration = ngx_mp4_get_32value(entry->duration); | |
1897 | |
1898 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1899 "count:%uD, duration:%uD", count, duration); | |
1900 | |
1901 if (start_time < count * duration) { | |
1902 start_sample += (ngx_uint_t) (start_time / duration); | |
1903 count -= start_sample; | |
1904 ngx_mp4_set_32value(entry->count, count); | |
1905 goto found; | |
1906 } | |
1907 | |
1908 start_sample += count; | |
1909 start_time -= count * duration; | |
1910 entries--; | |
1911 entry++; | |
1912 } | |
1913 | |
1914 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
4094 | 1915 "start time is out mp4 stts samples in \"%s\"", |
4085 | 1916 mp4->file.name.data); |
1917 | |
1918 return NGX_ERROR; | |
1919 | |
1920 found: | |
1921 | |
1922 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
1923 "start_sample:%ui, new count:%uD", start_sample, count); | |
1924 | |
1925 trak->start_sample = start_sample; | |
1926 | |
1927 data->pos = (u_char *) entry; | |
1928 atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos); | |
1929 trak->size += atom_size; | |
1930 | |
1931 atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf; | |
1932 stts_atom = (ngx_mp4_stts_atom_t *) atom->pos; | |
1933 ngx_mp4_set_32value(stts_atom->size, atom_size); | |
1934 ngx_mp4_set_32value(stts_atom->entries, entries); | |
1935 | |
1936 return NGX_OK; | |
1937 } | |
1938 | |
1939 | |
1940 typedef struct { | |
1941 u_char size[4]; | |
1942 u_char name[4]; | |
1943 u_char version[1]; | |
1944 u_char flags[3]; | |
1945 u_char entries[4]; | |
1946 } ngx_http_mp4_stss_atom_t; | |
1947 | |
1948 | |
1949 static ngx_int_t | |
1950 ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
1951 { | |
1952 u_char *atom_header, *atom_table, *atom_end; | |
1953 uint32_t entries; | |
1954 ngx_buf_t *atom, *data; | |
1955 ngx_http_mp4_trak_t *trak; | |
1956 ngx_http_mp4_stss_atom_t *stss_atom; | |
1957 | |
1958 /* sync samples atom */ | |
1959 | |
1960 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom"); | |
1961 | |
1962 atom_header = ngx_mp4_atom_header(mp4); | |
1963 stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header; | |
1964 ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's'); | |
1965 | |
1966 entries = ngx_mp4_get_32value(stss_atom->entries); | |
1967 | |
1968 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 1969 "sync sample entries:%uD", entries); |
4085 | 1970 |
1971 trak = ngx_mp4_last_trak(mp4); | |
1972 trak->sync_samples_entries = entries; | |
1973 | |
1974 atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t); | |
1975 | |
1976 atom = &trak->stss_atom_buf; | |
1977 atom->temporary = 1; | |
1978 atom->pos = atom_header; | |
1979 atom->last = atom_table; | |
1980 | |
1981 atom_end = atom_table + entries * sizeof(uint32_t); | |
1982 | |
1983 if ((uint64_t) (atom_end - stss_atom->version) > atom_data_size) { | |
1984 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
1985 "\"%s\" mp4 stss atom too large", mp4->file.name.data); | |
1986 return NGX_ERROR; | |
1987 } | |
1988 | |
1989 data = &trak->stss_data_buf; | |
1990 data->temporary = 1; | |
1991 data->pos = atom_table; | |
1992 data->last = atom_end; | |
1993 | |
1994 trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom; | |
1995 trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data; | |
1996 | |
1997 ngx_mp4_atom_next(mp4, atom_data_size); | |
1998 | |
1999 return NGX_OK; | |
2000 } | |
2001 | |
2002 | |
2003 static ngx_int_t | |
2004 ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, | |
2005 ngx_http_mp4_trak_t *trak) | |
2006 { | |
2007 size_t atom_size; | |
2008 uint32_t entries, sample, start_sample, *entry, *end; | |
2009 ngx_buf_t *atom, *data; | |
2010 ngx_http_mp4_stss_atom_t *stss_atom; | |
2011 | |
2012 /* | |
2013 * mdia.minf.stbl.stss updating requires trak->start_sample | |
2014 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2015 * atom which may reside after mdia.minf | |
2016 */ | |
2017 | |
2018 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2019 "mp4 stss atom update"); | |
2020 | |
2021 data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf; | |
2022 | |
2023 if (data == NULL) { | |
2024 return NGX_OK; | |
2025 } | |
2026 | |
2027 /* sync samples starts from 1 */ | |
2028 start_sample = trak->start_sample + 1; | |
2029 entries = trak->sync_samples_entries; | |
2030 | |
2031 entry = (uint32_t *) data->pos; | |
2032 end = (uint32_t *) data->last; | |
2033 | |
2034 while (entry < end) { | |
2035 sample = ngx_mp4_get_32value(entry); | |
2036 | |
2037 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2038 "start:%uD, sync:%uD", start_sample, sample); | |
2039 | |
2040 if (sample >= start_sample) { | |
2041 goto found; | |
2042 } | |
2043 | |
2044 entries--; | |
2045 entry++; | |
2046 } | |
2047 | |
2048 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2049 "start sample is out of mp4 stss atom in \"%s\"", | |
2050 mp4->file.name.data); | |
2051 | |
2052 return NGX_ERROR; | |
2053 | |
2054 found: | |
2055 | |
2056 data->pos = (u_char *) entry; | |
2057 | |
2058 start_sample = trak->start_sample; | |
2059 | |
2060 while (entry < end) { | |
2061 sample = ngx_mp4_get_32value(entry); | |
2062 sample -= start_sample; | |
2063 ngx_mp4_set_32value(entry, sample); | |
2064 entry++; | |
2065 } | |
2066 | |
2067 atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos); | |
2068 trak->size += atom_size; | |
2069 | |
2070 atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf; | |
2071 stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos; | |
2072 | |
2073 ngx_mp4_set_32value(stss_atom->size, atom_size); | |
2074 ngx_mp4_set_32value(stss_atom->entries, entries); | |
2075 | |
2076 return NGX_OK; | |
2077 } | |
2078 | |
2079 | |
2080 typedef struct { | |
2081 u_char size[4]; | |
2082 u_char name[4]; | |
2083 u_char version[1]; | |
2084 u_char flags[3]; | |
2085 u_char entries[4]; | |
2086 } ngx_mp4_ctts_atom_t; | |
2087 | |
2088 typedef struct { | |
2089 u_char count[4]; | |
2090 u_char offset[4]; | |
2091 } ngx_mp4_ctts_entry_t; | |
2092 | |
2093 | |
2094 static ngx_int_t | |
2095 ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2096 { | |
2097 u_char *atom_header, *atom_table, *atom_end; | |
2098 uint32_t entries; | |
2099 ngx_buf_t *atom, *data; | |
2100 ngx_mp4_ctts_atom_t *ctts_atom; | |
2101 ngx_http_mp4_trak_t *trak; | |
2102 | |
2103 /* composition offsets atom */ | |
2104 | |
2105 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom"); | |
2106 | |
2107 atom_header = ngx_mp4_atom_header(mp4); | |
2108 ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header; | |
2109 ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's'); | |
2110 | |
2111 entries = ngx_mp4_get_32value(ctts_atom->entries); | |
2112 | |
2113 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 2114 "composition offset entries:%uD", entries); |
4085 | 2115 |
2116 trak = ngx_mp4_last_trak(mp4); | |
2117 trak->composition_offset_entries = entries; | |
2118 | |
2119 atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t); | |
2120 | |
2121 atom = &trak->ctts_atom_buf; | |
2122 atom->temporary = 1; | |
2123 atom->pos = atom_header; | |
2124 atom->last = atom_table; | |
2125 | |
2126 atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t); | |
2127 | |
2128 if ((uint64_t) (atom_end - ctts_atom->version) > atom_data_size) { | |
2129 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2130 "\"%s\" mp4 ctts atom too large", mp4->file.name.data); | |
2131 return NGX_ERROR; | |
2132 } | |
2133 | |
2134 data = &trak->ctts_data_buf; | |
2135 data->temporary = 1; | |
2136 data->pos = atom_table; | |
2137 data->last = atom_end; | |
2138 | |
2139 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom; | |
2140 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data; | |
2141 | |
2142 ngx_mp4_atom_next(mp4, atom_data_size); | |
2143 | |
2144 return NGX_OK; | |
2145 } | |
2146 | |
2147 | |
2148 static void | |
2149 ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, | |
2150 ngx_http_mp4_trak_t *trak) | |
2151 { | |
2152 size_t atom_size; | |
2153 uint32_t entries, count, start_sample; | |
2154 ngx_buf_t *atom, *data; | |
2155 ngx_mp4_ctts_atom_t *ctts_atom; | |
2156 ngx_mp4_ctts_entry_t *entry, *end; | |
2157 | |
2158 /* | |
2159 * mdia.minf.stbl.ctts updating requires trak->start_sample | |
2160 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2161 * atom which may reside after mdia.minf | |
2162 */ | |
2163 | |
2164 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2165 "mp4 ctts atom update"); | |
2166 | |
2167 data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf; | |
2168 | |
2169 if (data == NULL) { | |
2170 return; | |
2171 } | |
2172 | |
2173 /* sync samples starts from 1 */ | |
2174 start_sample = trak->start_sample + 1; | |
2175 entries = trak->composition_offset_entries; | |
2176 entry = (ngx_mp4_ctts_entry_t *) data->pos; | |
2177 end = (ngx_mp4_ctts_entry_t *) data->last; | |
2178 | |
2179 while (entry < end) { | |
2180 count = ngx_mp4_get_32value(entry->count); | |
2181 | |
4088
8fe1da7b8386
bugfix of r4086: nginx could not be built without debug log.
Igor Sysoev <igor@sysoev.ru>
parents:
4087
diff
changeset
|
2182 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4085 | 2183 "start:%uD, count:%uD, offset:%uD", |
2184 start_sample, count, ngx_mp4_get_32value(entry->offset)); | |
2185 | |
2186 if (start_sample <= count) { | |
2187 count -= (start_sample - 1); | |
2188 ngx_mp4_set_32value(entry->count, count); | |
2189 goto found; | |
2190 } | |
2191 | |
2192 start_sample -= count; | |
2193 entries--; | |
2194 entry++; | |
2195 } | |
2196 | |
2197 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL; | |
2198 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL; | |
2199 | |
2200 return; | |
2201 | |
2202 found: | |
2203 | |
2204 data->pos = (u_char *) entry; | |
2205 atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos); | |
2206 trak->size += atom_size; | |
2207 | |
2208 atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf; | |
2209 ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos; | |
2210 | |
2211 ngx_mp4_set_32value(ctts_atom->size, atom_size); | |
2212 ngx_mp4_set_32value(ctts_atom->entries, entries); | |
2213 | |
2214 return; | |
2215 } | |
2216 | |
2217 | |
2218 typedef struct { | |
2219 u_char size[4]; | |
2220 u_char name[4]; | |
2221 u_char version[1]; | |
2222 u_char flags[3]; | |
2223 u_char entries[4]; | |
2224 } ngx_mp4_stsc_atom_t; | |
2225 | |
2226 | |
2227 static ngx_int_t | |
2228 ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2229 { | |
2230 u_char *atom_header, *atom_table, *atom_end; | |
2231 uint32_t entries; | |
2232 ngx_buf_t *atom, *data; | |
2233 ngx_mp4_stsc_atom_t *stsc_atom; | |
2234 ngx_http_mp4_trak_t *trak; | |
2235 | |
2236 /* sample-to-chunk atom */ | |
2237 | |
2238 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom"); | |
2239 | |
2240 atom_header = ngx_mp4_atom_header(mp4); | |
2241 stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header; | |
2242 ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c'); | |
2243 | |
2244 entries = ngx_mp4_get_32value(stsc_atom->entries); | |
2245 | |
2246 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
4096 | 2247 "sample-to-chunk entries:%uD", entries); |
4085 | 2248 |
2249 atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t); | |
2250 atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t); | |
2251 | |
2252 if ((uint64_t) (atom_end - stsc_atom->version) > atom_data_size) { | |
2253 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2254 "\"%s\" mp4 stsc atom too large", | |
2255 mp4->file.name.data); | |
2256 return NGX_ERROR; | |
2257 } | |
2258 | |
2259 trak = ngx_mp4_last_trak(mp4); | |
2260 trak->sample_to_chunk_entries = entries; | |
2261 | |
2262 atom = &trak->stsc_atom_buf; | |
2263 atom->temporary = 1; | |
2264 atom->pos = atom_header; | |
2265 atom->last = atom_table; | |
2266 | |
2267 data = &trak->stsc_data_buf; | |
2268 data->temporary = 1; | |
2269 data->pos = atom_table; | |
2270 data->last = atom_end; | |
2271 | |
2272 trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom; | |
2273 trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data; | |
2274 | |
2275 ngx_mp4_atom_next(mp4, atom_data_size); | |
2276 | |
2277 return NGX_OK; | |
2278 } | |
2279 | |
2280 | |
2281 static ngx_int_t | |
2282 ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, | |
2283 ngx_http_mp4_trak_t *trak) | |
2284 { | |
2285 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
|
2286 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
|
2287 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
|
2288 ngx_buf_t *atom, *data, *buf; |
4085 | 2289 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
|
2290 ngx_mp4_stsc_entry_t *entry, *first, *end; |
4085 | 2291 |
2292 /* | |
2293 * mdia.minf.stbl.stsc updating requires trak->start_sample | |
2294 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2295 * atom which may reside after mdia.minf | |
2296 */ | |
2297 | |
2298 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2299 "mp4 stsc atom update"); | |
2300 | |
2301 data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf; | |
2302 | |
2303 if (data == NULL) { | |
2304 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2305 "no mp4 stsc atoms were found in \"%s\"", | |
2306 mp4->file.name.data); | |
2307 return NGX_ERROR; | |
2308 } | |
2309 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2310 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
|
2311 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
|
2312 |
4085 | 2313 entry = (ngx_mp4_stsc_entry_t *) data->pos; |
2314 end = (ngx_mp4_stsc_entry_t *) data->last; | |
2315 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2316 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
|
2317 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
|
2318 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
|
2319 entry++; |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2320 |
4085 | 2321 while (entry < end) { |
2322 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2323 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
|
2324 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2325 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
|
2326 "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
|
2327 "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
|
2328 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
|
2329 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2330 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
|
2331 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2332 if (start_sample <= n) { |
4085 | 2333 goto found; |
2334 } | |
2335 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2336 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
|
2337 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2338 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
|
2339 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
|
2340 id = ngx_mp4_get_32value(entry->id); |
4085 | 2341 entries--; |
2342 entry++; | |
2343 } | |
2344 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2345 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
|
2346 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2347 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
|
2348 "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
|
2349 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
|
2350 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2351 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
|
2352 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2353 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
|
2354 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
|
2355 "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
|
2356 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
|
2357 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
|
2358 } |
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 found: |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2361 |
4085 | 2362 entries++; |
2363 entry--; | |
2364 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2365 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
|
2366 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
|
2367 "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
|
2368 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
|
2369 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
|
2370 } |
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 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
|
2373 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2374 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
|
2375 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
|
2376 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2377 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
|
2378 "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
|
2379 trak->start_chunk, trak->chunk_samples); |
4085 | 2380 |
2381 data->pos = (u_char *) entry; | |
2382 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
|
2383 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2384 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
|
2385 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2386 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
|
2387 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
|
2388 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
|
2389 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
|
2390 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2391 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
|
2392 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
|
2393 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
|
2394 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
|
2395 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2396 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
|
2397 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2398 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
|
2399 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2400 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
|
2401 } |
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 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
|
2404 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
|
2405 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
|
2406 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
|
2407 } |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2408 |
4085 | 2409 trak->size += atom_size; |
2410 | |
2411 atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf; | |
2412 stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos; | |
2413 | |
2414 ngx_mp4_set_32value(stsc_atom->size, atom_size); | |
2415 ngx_mp4_set_32value(stsc_atom->entries, entries); | |
2416 | |
2417 return NGX_OK; | |
2418 } | |
2419 | |
2420 | |
2421 typedef struct { | |
2422 u_char size[4]; | |
2423 u_char name[4]; | |
2424 u_char version[1]; | |
2425 u_char flags[3]; | |
2426 u_char uniform_size[4]; | |
2427 u_char entries[4]; | |
2428 } ngx_mp4_stsz_atom_t; | |
2429 | |
2430 | |
2431 static ngx_int_t | |
2432 ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2433 { | |
2434 u_char *atom_header, *atom_table, *atom_end; | |
2435 size_t atom_size; | |
2436 uint32_t entries, size; | |
2437 ngx_buf_t *atom, *data; | |
2438 ngx_mp4_stsz_atom_t *stsz_atom; | |
2439 ngx_http_mp4_trak_t *trak; | |
2440 | |
2441 /* sample sizes atom */ | |
2442 | |
2443 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom"); | |
2444 | |
2445 atom_header = ngx_mp4_atom_header(mp4); | |
2446 stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header; | |
2447 ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z'); | |
2448 | |
2449 size = ngx_mp4_get_32value(stsz_atom->uniform_size); | |
2450 entries = ngx_mp4_get_32value(stsz_atom->entries); | |
2451 | |
2452 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2453 "sample uniform size:%uD, entries:%uD", size, entries); | |
2454 | |
2455 trak = ngx_mp4_last_trak(mp4); | |
2456 trak->sample_sizes_entries = entries; | |
2457 | |
2458 atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t); | |
2459 | |
2460 atom = &trak->stsz_atom_buf; | |
2461 atom->temporary = 1; | |
2462 atom->pos = atom_header; | |
2463 atom->last = atom_table; | |
2464 | |
2465 trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom; | |
2466 | |
2467 if (size == 0) { | |
2468 atom_end = atom_table + entries * sizeof(uint32_t); | |
2469 | |
2470 if ((uint64_t) (atom_end - stsz_atom->version) > atom_data_size) { | |
2471 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2472 "\"%s\" mp4 stsz atom too large", | |
2473 mp4->file.name.data); | |
2474 return NGX_ERROR; | |
2475 } | |
2476 | |
2477 data = &trak->stsz_data_buf; | |
2478 data->temporary = 1; | |
2479 data->pos = atom_table; | |
2480 data->last = atom_end; | |
2481 | |
2482 trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data; | |
2483 | |
2484 } else { | |
2485 /* 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
|
2486 /* TODO : chunk samples */ |
4085 | 2487 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; |
2488 ngx_mp4_set_32value(atom_header, atom_size); | |
2489 trak->size += atom_size; | |
2490 } | |
2491 | |
2492 ngx_mp4_atom_next(mp4, atom_data_size); | |
2493 | |
2494 return NGX_OK; | |
2495 } | |
2496 | |
2497 | |
2498 static void | |
2499 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, | |
2500 ngx_http_mp4_trak_t *trak) | |
2501 { | |
2502 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
|
2503 uint32_t *pos, *end; |
4085 | 2504 ngx_buf_t *atom, *data; |
2505 ngx_mp4_stsz_atom_t *stsz_atom; | |
2506 | |
2507 /* | |
2508 * mdia.minf.stbl.stsz updating requires trak->start_sample | |
2509 * from mdia.minf.stbl.stts which depends on value from mdia.mdhd | |
2510 * atom which may reside after mdia.minf | |
2511 */ | |
2512 | |
2513 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2514 "mp4 stsz atom update"); | |
2515 | |
2516 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf; | |
2517 | |
2518 if (data) { | |
2519 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
|
2520 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
|
2521 |
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2522 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
|
2523 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
|
2524 } |
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 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, |
4112 | 2527 "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
|
2528 |
4085 | 2529 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); |
2530 trak->size += atom_size; | |
2531 | |
2532 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf; | |
2533 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos; | |
2534 | |
2535 ngx_mp4_set_32value(stsz_atom->size, atom_size); | |
2536 ngx_mp4_set_32value(stsz_atom->entries, | |
2537 trak->sample_sizes_entries - trak->start_sample); | |
2538 } | |
2539 } | |
2540 | |
2541 | |
2542 typedef struct { | |
2543 u_char size[4]; | |
2544 u_char name[4]; | |
2545 u_char version[1]; | |
2546 u_char flags[3]; | |
2547 u_char entries[4]; | |
2548 } ngx_mp4_stco_atom_t; | |
2549 | |
2550 | |
2551 static ngx_int_t | |
2552 ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2553 { | |
2554 u_char *atom_header, *atom_table, *atom_end; | |
2555 uint32_t entries; | |
2556 ngx_buf_t *atom, *data; | |
2557 ngx_mp4_stco_atom_t *stco_atom; | |
2558 ngx_http_mp4_trak_t *trak; | |
2559 | |
2560 /* chunk offsets atom */ | |
2561 | |
2562 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom"); | |
2563 | |
2564 atom_header = ngx_mp4_atom_header(mp4); | |
2565 stco_atom = (ngx_mp4_stco_atom_t *) atom_header; | |
2566 ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o'); | |
2567 | |
2568 entries = ngx_mp4_get_32value(stco_atom->entries); | |
2569 | |
2570 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries); | |
2571 | |
2572 atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t); | |
2573 atom_end = atom_table + entries * sizeof(uint32_t); | |
2574 | |
2575 if ((uint64_t) (atom_end - stco_atom->version) > atom_data_size) { | |
2576 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2577 "\"%s\" mp4 stco atom too large", mp4->file.name.data); | |
2578 return NGX_ERROR; | |
2579 } | |
2580 | |
2581 trak = ngx_mp4_last_trak(mp4); | |
2582 trak->chunks = entries; | |
2583 | |
4107 | 2584 atom = &trak->stco_atom_buf; |
4085 | 2585 atom->temporary = 1; |
2586 atom->pos = atom_header; | |
2587 atom->last = atom_table; | |
2588 | |
4107 | 2589 data = &trak->stco_data_buf; |
4085 | 2590 data->temporary = 1; |
2591 data->pos = atom_table; | |
2592 data->last = atom_end; | |
2593 | |
2594 trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom; | |
2595 trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data; | |
2596 | |
2597 ngx_mp4_atom_next(mp4, atom_data_size); | |
2598 | |
2599 return NGX_OK; | |
2600 } | |
2601 | |
2602 | |
2603 static ngx_int_t | |
2604 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, | |
2605 ngx_http_mp4_trak_t *trak) | |
2606 { | |
2607 size_t atom_size; | |
2608 ngx_buf_t *atom, *data; | |
2609 ngx_mp4_stco_atom_t *stco_atom; | |
2610 | |
2611 /* | |
2612 * mdia.minf.stbl.stco updating requires trak->start_chunk | |
2613 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd | |
2614 * atom which may reside after mdia.minf | |
2615 */ | |
2616 | |
2617 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2618 "mp4 stco atom update"); | |
2619 | |
2620 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf; | |
2621 | |
2622 if (data == NULL) { | |
2623 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2624 "no mp4 stco atoms were found in \"%s\"", | |
2625 mp4->file.name.data); | |
2626 return NGX_ERROR; | |
2627 } | |
2628 | |
4098
a3e07fab98a3
Fix of case when start sample does not reside on chunk boundary.
Igor Sysoev <igor@sysoev.ru>
parents:
4096
diff
changeset
|
2629 data->pos += trak->start_chunk * sizeof(uint32_t); |
4085 | 2630 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); |
2631 trak->size += atom_size; | |
2632 | |
2633 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
|
2634 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
|
2635 ngx_mp4_set_32value(data->pos, trak->start_offset); |
4085 | 2636 |
2637 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2638 "start chunk offset:%uD", trak->start_offset); | |
2639 | |
2640 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf; | |
2641 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos; | |
2642 | |
2643 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
|
2644 ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk); |
4085 | 2645 |
2646 return NGX_OK; | |
2647 } | |
2648 | |
2649 | |
2650 static void | |
2651 ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4, | |
2652 ngx_http_mp4_trak_t *trak, int32_t adjustment) | |
2653 { | |
2654 uint32_t offset, *entry, *end; | |
2655 ngx_buf_t *data; | |
2656 | |
2657 /* | |
2658 * moov.trak.mdia.minf.stbl.stco adjustment requires | |
2659 * minimal start offset of all traks and new moov atom size | |
2660 */ | |
2661 | |
2662 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2663 "mp4 stco atom adjustment"); | |
2664 | |
2665 data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf; | |
2666 entry = (uint32_t *) data->pos; | |
2667 end = (uint32_t *) data->last; | |
2668 | |
2669 while (entry < end) { | |
2670 offset = ngx_mp4_get_32value(entry); | |
2671 offset += adjustment; | |
2672 ngx_mp4_set_32value(entry, offset); | |
2673 entry++; | |
2674 } | |
2675 } | |
2676 | |
2677 | |
4112 | 2678 typedef struct { |
2679 u_char size[4]; | |
2680 u_char name[4]; | |
2681 u_char version[1]; | |
2682 u_char flags[3]; | |
2683 u_char entries[4]; | |
2684 } ngx_mp4_co64_atom_t; | |
2685 | |
2686 | |
2687 static ngx_int_t | |
2688 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) | |
2689 { | |
2690 u_char *atom_header, *atom_table, *atom_end; | |
2691 uint32_t entries; | |
2692 ngx_buf_t *atom, *data; | |
2693 ngx_mp4_co64_atom_t *co64_atom; | |
2694 ngx_http_mp4_trak_t *trak; | |
2695 | |
2696 /* chunk offsets atom */ | |
2697 | |
2698 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom"); | |
2699 | |
2700 atom_header = ngx_mp4_atom_header(mp4); | |
2701 co64_atom = (ngx_mp4_co64_atom_t *) atom_header; | |
2702 ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4'); | |
2703 | |
2704 entries = ngx_mp4_get_32value(co64_atom->entries); | |
2705 | |
2706 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries); | |
2707 | |
2708 atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t); | |
2709 atom_end = atom_table + entries * sizeof(uint64_t); | |
2710 | |
2711 if ((uint64_t) (atom_end - co64_atom->version) > atom_data_size) { | |
2712 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2713 "\"%s\" mp4 co64 atom too large", mp4->file.name.data); | |
2714 return NGX_ERROR; | |
2715 } | |
2716 | |
2717 trak = ngx_mp4_last_trak(mp4); | |
2718 trak->chunks = entries; | |
2719 | |
2720 atom = &trak->co64_atom_buf; | |
2721 atom->temporary = 1; | |
2722 atom->pos = atom_header; | |
2723 atom->last = atom_table; | |
2724 | |
2725 data = &trak->co64_data_buf; | |
2726 data->temporary = 1; | |
2727 data->pos = atom_table; | |
2728 data->last = atom_end; | |
2729 | |
2730 trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom; | |
2731 trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data; | |
2732 | |
2733 ngx_mp4_atom_next(mp4, atom_data_size); | |
2734 | |
2735 return NGX_OK; | |
2736 } | |
2737 | |
2738 | |
2739 static ngx_int_t | |
2740 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, | |
2741 ngx_http_mp4_trak_t *trak) | |
2742 { | |
2743 size_t atom_size; | |
2744 ngx_buf_t *atom, *data; | |
2745 ngx_mp4_co64_atom_t *co64_atom; | |
2746 | |
2747 /* | |
2748 * mdia.minf.stbl.co64 updating requires trak->start_chunk | |
2749 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd | |
2750 * atom which may reside after mdia.minf | |
2751 */ | |
2752 | |
2753 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2754 "mp4 co64 atom update"); | |
2755 | |
2756 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; | |
2757 | |
2758 if (data == NULL) { | |
2759 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, | |
2760 "no mp4 co64 atoms were found in \"%s\"", | |
2761 mp4->file.name.data); | |
2762 return NGX_ERROR; | |
2763 } | |
2764 | |
2765 data->pos += trak->start_chunk * sizeof(uint64_t); | |
2766 atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); | |
2767 trak->size += atom_size; | |
2768 | |
2769 trak->start_offset = ngx_mp4_get_64value(data->pos); | |
2770 trak->start_offset += trak->chunk_samples_size; | |
2771 ngx_mp4_set_64value(data->pos, trak->start_offset); | |
2772 | |
2773 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2774 "start chunk offset:%uL", trak->start_offset); | |
2775 | |
2776 atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf; | |
2777 co64_atom = (ngx_mp4_co64_atom_t *) atom->pos; | |
2778 | |
2779 ngx_mp4_set_32value(co64_atom->size, atom_size); | |
2780 ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk); | |
2781 | |
2782 return NGX_OK; | |
2783 } | |
2784 | |
2785 | |
2786 static void | |
2787 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4, | |
2788 ngx_http_mp4_trak_t *trak, off_t adjustment) | |
2789 { | |
2790 uint64_t offset, *entry, *end; | |
2791 ngx_buf_t *data; | |
2792 | |
2793 /* | |
2794 * moov.trak.mdia.minf.stbl.co64 adjustment requires | |
2795 * minimal start offset of all traks and new moov atom size | |
2796 */ | |
2797 | |
2798 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, | |
2799 "mp4 co64 atom adjustment"); | |
2800 | |
2801 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf; | |
2802 entry = (uint64_t *) data->pos; | |
2803 end = (uint64_t *) data->last; | |
2804 | |
2805 while (entry < end) { | |
2806 offset = ngx_mp4_get_64value(entry); | |
2807 offset += adjustment; | |
2808 ngx_mp4_set_64value(entry, offset); | |
2809 entry++; | |
2810 } | |
2811 } | |
2812 | |
2813 | |
4085 | 2814 static char * |
2815 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) | |
2816 { | |
2817 ngx_http_core_loc_conf_t *clcf; | |
2818 | |
2819 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); | |
2820 clcf->handler = ngx_http_mp4_handler; | |
2821 | |
2822 return NGX_CONF_OK; | |
2823 } | |
2824 | |
2825 | |
2826 static void * | |
2827 ngx_http_mp4_create_conf(ngx_conf_t *cf) | |
2828 { | |
2829 ngx_http_mp4_conf_t *conf; | |
2830 | |
2831 conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t)); | |
2832 if (conf == NULL) { | |
2833 return NULL; | |
2834 } | |
2835 | |
2836 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
|
2837 conf->max_buffer_size = NGX_CONF_UNSET_SIZE; |
4085 | 2838 |
2839 return conf; | |
2840 } | |
2841 | |
2842 | |
2843 static char * | |
2844 ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child) | |
2845 { | |
2846 ngx_http_mp4_conf_t *prev = parent; | |
2847 ngx_http_mp4_conf_t *conf = child; | |
2848 | |
2849 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
|
2850 ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size, |
4085 | 2851 10 * 1024 * 1024); |
2852 | |
2853 return NGX_CONF_OK; | |
2854 } |