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