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