Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * ASF muxer | ||
3 | * Copyright (c) 2000, 2001 Fabrice Bellard | ||
4 | * | ||
5 | * This file is part of FFmpeg. | ||
6 | * | ||
7 | * FFmpeg is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * FFmpeg is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with FFmpeg; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include "config_components.h" | ||
23 | |||
24 | #include "libavutil/avassert.h" | ||
25 | #include "libavutil/dict.h" | ||
26 | #include "libavutil/mathematics.h" | ||
27 | #include "libavutil/opt.h" | ||
28 | #include "libavcodec/codec_desc.h" | ||
29 | #include "avformat.h" | ||
30 | #include "avlanguage.h" | ||
31 | #include "avio_internal.h" | ||
32 | #include "internal.h" | ||
33 | #include "mux.h" | ||
34 | #include "riff.h" | ||
35 | #include "asf.h" | ||
36 | |||
37 | #define ASF_INDEXED_INTERVAL 10000000 | ||
38 | #define ASF_INDEX_BLOCK (1<<9) | ||
39 | #define ASF_PAYLOADS_PER_PACKET 63 | ||
40 | |||
41 | #define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 | ||
42 | #define ASF_PACKET_ERROR_CORRECTION_FLAGS \ | ||
43 | (ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT | \ | ||
44 | ASF_PACKET_ERROR_CORRECTION_DATA_SIZE) | ||
45 | |||
46 | #if (ASF_PACKET_ERROR_CORRECTION_FLAGS != 0) | ||
47 | # define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 1 | ||
48 | #else | ||
49 | # define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 0 | ||
50 | #endif | ||
51 | |||
52 | #define ASF_PPI_PROPERTY_FLAGS \ | ||
53 | (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE | \ | ||
54 | ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD | \ | ||
55 | ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE | \ | ||
56 | ASF_PL_FLAG_STREAM_NUMBER_LENGTH_FIELD_IS_BYTE) | ||
57 | |||
58 | #define ASF_PPI_LENGTH_TYPE_FLAGS 0 | ||
59 | |||
60 | #define ASF_PAYLOAD_FLAGS ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD | ||
61 | |||
62 | #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) | ||
63 | # define ASF_PPI_SEQUENCE_FIELD_SIZE 1 | ||
64 | #endif | ||
65 | #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) | ||
66 | # define ASF_PPI_SEQUENCE_FIELD_SIZE 2 | ||
67 | #endif | ||
68 | #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) | ||
69 | # define ASF_PPI_SEQUENCE_FIELD_SIZE 4 | ||
70 | #endif | ||
71 | #ifndef ASF_PPI_SEQUENCE_FIELD_SIZE | ||
72 | # define ASF_PPI_SEQUENCE_FIELD_SIZE 0 | ||
73 | #endif | ||
74 | |||
75 | #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) | ||
76 | # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 1 | ||
77 | #endif | ||
78 | #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) | ||
79 | # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 2 | ||
80 | #endif | ||
81 | #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) | ||
82 | # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 4 | ||
83 | #endif | ||
84 | #ifndef ASF_PPI_PACKET_LENGTH_FIELD_SIZE | ||
85 | # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 0 | ||
86 | #endif | ||
87 | |||
88 | #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) | ||
89 | # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 1 | ||
90 | #endif | ||
91 | #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) | ||
92 | # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 2 | ||
93 | #endif | ||
94 | #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) | ||
95 | # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 4 | ||
96 | #endif | ||
97 | #ifndef ASF_PPI_PADDING_LENGTH_FIELD_SIZE | ||
98 | # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 0 | ||
99 | #endif | ||
100 | |||
101 | #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) | ||
102 | # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 1 | ||
103 | #endif | ||
104 | #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) | ||
105 | # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 2 | ||
106 | #endif | ||
107 | #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) | ||
108 | # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 4 | ||
109 | #endif | ||
110 | #ifndef ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE | ||
111 | # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 0 | ||
112 | #endif | ||
113 | |||
114 | #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) | ||
115 | # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 1 | ||
116 | #endif | ||
117 | #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) | ||
118 | # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 2 | ||
119 | #endif | ||
120 | #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) | ||
121 | # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 4 | ||
122 | #endif | ||
123 | #ifndef ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE | ||
124 | # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 0 | ||
125 | #endif | ||
126 | |||
127 | #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) | ||
128 | # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 1 | ||
129 | #endif | ||
130 | #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) | ||
131 | # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 2 | ||
132 | #endif | ||
133 | #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) | ||
134 | # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 4 | ||
135 | #endif | ||
136 | #ifndef ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE | ||
137 | # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 0 | ||
138 | #endif | ||
139 | |||
140 | #if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_BYTE == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) | ||
141 | # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 1 | ||
142 | #endif | ||
143 | #if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) | ||
144 | # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 2 | ||
145 | #endif | ||
146 | #ifndef ASF_PAYLOAD_LENGTH_FIELD_SIZE | ||
147 | # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 0 | ||
148 | #endif | ||
149 | |||
150 | #define PACKET_HEADER_MIN_SIZE \ | ||
151 | (ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE + \ | ||
152 | ASF_PACKET_ERROR_CORRECTION_DATA_SIZE + \ | ||
153 | 1 + /* Length Type Flags */ \ | ||
154 | 1 + /* Property Flags */ \ | ||
155 | ASF_PPI_PACKET_LENGTH_FIELD_SIZE + \ | ||
156 | ASF_PPI_SEQUENCE_FIELD_SIZE + \ | ||
157 | ASF_PPI_PADDING_LENGTH_FIELD_SIZE + \ | ||
158 | 4 + /* Send Time Field */ \ | ||
159 | 2) /* Duration Field */ | ||
160 | |||
161 | // Replicated Data shall be at least 8 bytes long. | ||
162 | #define ASF_PAYLOAD_REPLICATED_DATA_LENGTH 0x08 | ||
163 | |||
164 | #define PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD \ | ||
165 | (1 + /* Stream Number */ \ | ||
166 | ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ | ||
167 | ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ | ||
168 | ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ | ||
169 | ASF_PAYLOAD_REPLICATED_DATA_LENGTH) | ||
170 | |||
171 | #define PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS \ | ||
172 | (1 + /* Stream Number */ \ | ||
173 | ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ | ||
174 | ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ | ||
175 | ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ | ||
176 | ASF_PAYLOAD_REPLICATED_DATA_LENGTH + \ | ||
177 | ASF_PAYLOAD_LENGTH_FIELD_SIZE) | ||
178 | |||
179 | #define SINGLE_PAYLOAD_HEADERS \ | ||
180 | (PACKET_HEADER_MIN_SIZE + \ | ||
181 | PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD) | ||
182 | |||
183 | #define MULTI_PAYLOAD_HEADERS \ | ||
184 | (PACKET_HEADER_MIN_SIZE + \ | ||
185 | 1 + /* Payload Flags */ \ | ||
186 | 2 * PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS) | ||
187 | |||
188 | #define DATA_HEADER_SIZE 50 | ||
189 | |||
190 | #define PACKET_SIZE_MAX 65536 | ||
191 | #define PACKET_SIZE_MIN 100 | ||
192 | |||
193 | typedef struct ASFStream { | ||
194 | int num; | ||
195 | unsigned char seq; | ||
196 | |||
197 | uint16_t stream_language_index; | ||
198 | } ASFStream; | ||
199 | |||
200 | typedef struct ASFContext { | ||
201 | AVClass *av_class; | ||
202 | uint32_t seqno; | ||
203 | int is_streamed; | ||
204 | ASFStream streams[128]; ///< it's max number and it's not that big | ||
205 | const char *languages[128]; | ||
206 | int nb_languages; | ||
207 | int64_t creation_time; | ||
208 | /* non-streamed additional info */ | ||
209 | uint64_t nb_packets; ///< how many packets are there in the file, invalid if broadcasting | ||
210 | int64_t duration; ///< in 100ns units | ||
211 | /* packet filling */ | ||
212 | unsigned char multi_payloads_present; | ||
213 | int packet_size_left; | ||
214 | int64_t packet_timestamp_start; | ||
215 | int64_t packet_timestamp_end; | ||
216 | unsigned int packet_nb_payloads; | ||
217 | uint8_t packet_buf[PACKET_SIZE_MAX]; | ||
218 | FFIOContext pb; | ||
219 | /* only for reading */ | ||
220 | uint64_t data_offset; ///< beginning of the first data packet | ||
221 | |||
222 | ASFIndex *index_ptr; | ||
223 | uint32_t nb_index_memory_alloc; | ||
224 | uint16_t maximum_packet; | ||
225 | uint32_t next_packet_number; | ||
226 | uint16_t next_packet_count; | ||
227 | uint64_t next_packet_offset; | ||
228 | int next_start_sec; | ||
229 | int end_sec; | ||
230 | int packet_size; | ||
231 | } ASFContext; | ||
232 | |||
233 | static const AVCodecTag codec_asf_bmp_tags[] = { | ||
234 | { AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') }, | ||
235 | { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, | ||
236 | { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, | ||
237 | { AV_CODEC_ID_NONE, 0 }, | ||
238 | }; | ||
239 | |||
240 | static const AVCodecTag *const asf_codec_tags[] = { | ||
241 | codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, NULL | ||
242 | }; | ||
243 | |||
244 | #define PREROLL_TIME 3100 | ||
245 | |||
246 | 12 | static void put_str16(AVIOContext *s, AVIOContext *dyn_buf, const char *tag) | |
247 | { | ||
248 | uint8_t *buf; | ||
249 | int len; | ||
250 | |||
251 | 12 | avio_put_str16le(dyn_buf, tag); | |
252 | 12 | len = avio_get_dyn_buf(dyn_buf, &buf); | |
253 | 12 | avio_wl16(s, len); | |
254 | 12 | avio_write(s, buf, len); | |
255 | 12 | ffio_reset_dyn_buf(dyn_buf); | |
256 | 12 | } | |
257 | |||
258 | 34 | static int64_t put_header(AVIOContext *pb, const ff_asf_guid *g) | |
259 | { | ||
260 | int64_t pos; | ||
261 | |||
262 | 34 | pos = avio_tell(pb); | |
263 | 34 | ff_put_guid(pb, g); | |
264 | 34 | avio_wl64(pb, 24); | |
265 | 34 | return pos; | |
266 | } | ||
267 | |||
268 | /* update header size */ | ||
269 | 34 | static void end_header(AVIOContext *pb, int64_t pos) | |
270 | { | ||
271 | int64_t pos1; | ||
272 | |||
273 | 34 | pos1 = avio_tell(pb); | |
274 | 34 | avio_seek(pb, pos + 16, SEEK_SET); | |
275 | 34 | avio_wl64(pb, pos1 - pos); | |
276 | 34 | avio_seek(pb, pos1, SEEK_SET); | |
277 | 34 | } | |
278 | |||
279 | /* write an asf chunk (only used in streaming case) */ | ||
280 | ✗ | static void put_chunk(AVFormatContext *s, int type, | |
281 | int payload_length, int flags) | ||
282 | { | ||
283 | ✗ | ASFContext *asf = s->priv_data; | |
284 | ✗ | AVIOContext *pb = s->pb; | |
285 | int length; | ||
286 | |||
287 | ✗ | length = payload_length + 8; | |
288 | ✗ | avio_wl16(pb, type); | |
289 | ✗ | avio_wl16(pb, length); // size | |
290 | ✗ | avio_wl32(pb, asf->seqno); // sequence number | |
291 | ✗ | avio_wl16(pb, flags); // unknown bytes | |
292 | ✗ | avio_wl16(pb, length); // size_confirm | |
293 | ✗ | asf->seqno++; | |
294 | ✗ | } | |
295 | |||
296 | /* convert from av time to windows time */ | ||
297 | 6 | static int64_t unix_to_file_time(int64_t ti) | |
298 | { | ||
299 | int64_t t; | ||
300 | |||
301 | 6 | t = ti * INT64_C(10); | |
302 | 6 | t += INT64_C(116444736000000000); | |
303 | 6 | return t; | |
304 | } | ||
305 | |||
306 | ✗ | static int32_t get_send_time(ASFContext *asf, int64_t pres_time, uint64_t *offset) | |
307 | { | ||
308 | ✗ | int32_t send_time = 0; | |
309 | ✗ | *offset = asf->data_offset + DATA_HEADER_SIZE; | |
310 | ✗ | for (int i = 0; i < asf->next_start_sec; i++) { | |
311 | ✗ | if (pres_time <= asf->index_ptr[i].send_time) | |
312 | ✗ | break; | |
313 | ✗ | send_time = asf->index_ptr[i].send_time; | |
314 | ✗ | *offset = asf->index_ptr[i].offset; | |
315 | } | ||
316 | |||
317 | ✗ | return send_time / 10000; | |
318 | } | ||
319 | |||
320 | ✗ | static void asf_write_markers(AVFormatContext *s, AVIOContext *dyn_buf) | |
321 | { | ||
322 | ✗ | ASFContext *asf = s->priv_data; | |
323 | ✗ | AVIOContext *pb = s->pb; | |
324 | ✗ | AVRational scale = {1, 10000000}; | |
325 | ✗ | int64_t hpos = put_header(pb, &ff_asf_marker_header); | |
326 | |||
327 | ✗ | ff_put_guid(pb, &ff_asf_reserved_4);// ASF spec mandates this reserved value | |
328 | ✗ | avio_wl32(pb, s->nb_chapters); // markers count | |
329 | ✗ | avio_wl16(pb, 0); // ASF spec mandates 0 for this | |
330 | ✗ | avio_wl16(pb, 0); // name length 0, no name given | |
331 | |||
332 | ✗ | for (unsigned i = 0; i < s->nb_chapters; i++) { | |
333 | ✗ | AVChapter *c = s->chapters[i]; | |
334 | ✗ | AVDictionaryEntry *t = av_dict_get(c->metadata, "title", NULL, 0); | |
335 | ✗ | int64_t pres_time = av_rescale_q(c->start, c->time_base, scale); | |
336 | uint64_t offset; | ||
337 | ✗ | int32_t send_time = get_send_time(asf, pres_time, &offset); | |
338 | ✗ | int len = 0; | |
339 | uint8_t *buf; | ||
340 | ✗ | if (t) { | |
341 | ✗ | avio_put_str16le(dyn_buf, t->value); | |
342 | ✗ | len = avio_get_dyn_buf(dyn_buf, &buf); | |
343 | } | ||
344 | ✗ | avio_wl64(pb, offset); // offset of the packet with send_time | |
345 | ✗ | avio_wl64(pb, pres_time + PREROLL_TIME * 10000); // presentation time | |
346 | ✗ | avio_wl16(pb, 12 + len); // entry length | |
347 | ✗ | avio_wl32(pb, send_time); // send time | |
348 | ✗ | avio_wl32(pb, 0); // flags, should be 0 | |
349 | ✗ | avio_wl32(pb, len / 2); // marker desc length in WCHARS! | |
350 | ✗ | if (t) { | |
351 | ✗ | avio_write(pb, buf, len); // marker desc | |
352 | ✗ | ffio_reset_dyn_buf(dyn_buf); | |
353 | } | ||
354 | } | ||
355 | ✗ | end_header(pb, hpos); | |
356 | ✗ | } | |
357 | |||
358 | /* write the header (used two times if non streamed) */ | ||
359 | 6 | static int asf_write_header1(AVFormatContext *s, int64_t file_size, | |
360 | int64_t data_chunk_size) | ||
361 | { | ||
362 | 6 | ASFContext *asf = s->priv_data; | |
363 | 6 | AVIOContext *pb = s->pb, *dyn_buf; | |
364 | AVDictionaryEntry *tags[5]; | ||
365 | int header_size, extra_size, extra_size2, wav_extra_size; | ||
366 | 6 | int has_title, has_aspect_ratio = 0; | |
367 | int metadata_count; | ||
368 | int64_t header_offset, cur_pos, hpos; | ||
369 | int bit_rate, ret; | ||
370 | int64_t duration; | ||
371 | 6 | int audio_language_counts[128] = { 0 }; | |
372 | |||
373 | 6 | ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL); | |
374 | |||
375 | 6 | tags[0] = av_dict_get(s->metadata, "title", NULL, 0); | |
376 | 6 | tags[1] = av_dict_get(s->metadata, "author", NULL, 0); | |
377 | 6 | tags[2] = av_dict_get(s->metadata, "copyright", NULL, 0); | |
378 | 6 | tags[3] = av_dict_get(s->metadata, "comment", NULL, 0); | |
379 | 6 | tags[4] = av_dict_get(s->metadata, "rating", NULL, 0); | |
380 | |||
381 | 6 | duration = asf->duration + PREROLL_TIME * 10000; | |
382 |
6/10✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 4 times.
|
6 | has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4]; |
383 | |||
384 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | if (!file_size) { |
385 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if (ff_parse_creation_time_metadata(s, &asf->creation_time, 0) != 0) |
386 | ✗ | av_dict_set(&s->metadata, "creation_time", NULL, 0); | |
387 | } | ||
388 | |||
389 | 6 | metadata_count = av_dict_count(s->metadata); | |
390 | |||
391 | 6 | bit_rate = 0; | |
392 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
|
14 | for (unsigned n = 0; n < s->nb_streams; n++) { |
393 | 8 | AVStream *const st = s->streams[n]; | |
394 | 8 | AVCodecParameters *const par = st->codecpar; | |
395 | AVDictionaryEntry *entry; | ||
396 | |||
397 | 8 | avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */ | |
398 | |||
399 | 8 | bit_rate += par->bit_rate; | |
400 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | if ( par->codec_type == AVMEDIA_TYPE_VIDEO |
401 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | && par->sample_aspect_ratio.num > 0 |
402 | ✗ | && par->sample_aspect_ratio.den > 0) | |
403 | ✗ | has_aspect_ratio++; | |
404 | |||
405 | 8 | entry = av_dict_get(s->streams[n]->metadata, "language", NULL, 0); | |
406 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (entry) { |
407 | ✗ | const char *iso6391lang = ff_convert_lang_to(entry->value, AV_LANG_ISO639_1); | |
408 | ✗ | if (iso6391lang) { | |
409 | int i; | ||
410 | ✗ | for (i = 0; i < asf->nb_languages; i++) { | |
411 | ✗ | if (!strcmp(asf->languages[i], iso6391lang)) { | |
412 | ✗ | asf->streams[n].stream_language_index = i; | |
413 | ✗ | break; | |
414 | } | ||
415 | } | ||
416 | ✗ | if (i >= asf->nb_languages) { | |
417 | ✗ | asf->languages[asf->nb_languages] = iso6391lang; | |
418 | ✗ | asf->streams[n].stream_language_index = asf->nb_languages; | |
419 | ✗ | asf->nb_languages++; | |
420 | } | ||
421 | ✗ | if (par->codec_type == AVMEDIA_TYPE_AUDIO) | |
422 | ✗ | audio_language_counts[asf->streams[n].stream_language_index]++; | |
423 | } | ||
424 | } else { | ||
425 | 8 | asf->streams[n].stream_language_index = 128; | |
426 | } | ||
427 | } | ||
428 | |||
429 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (asf->is_streamed) { |
430 | ✗ | put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ | |
431 | } | ||
432 | |||
433 | 6 | ff_put_guid(pb, &ff_asf_header); | |
434 | 6 | avio_wl64(pb, -1); /* header length, will be patched after */ | |
435 | 6 | avio_wl32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */ | |
436 | 6 | avio_w8(pb, 1); /* ??? */ | |
437 | 6 | avio_w8(pb, 2); /* ??? */ | |
438 | |||
439 | /* file header */ | ||
440 | 6 | header_offset = avio_tell(pb); | |
441 | 6 | hpos = put_header(pb, &ff_asf_file_header); | |
442 | 6 | ff_put_guid(pb, &ff_asf_my_guid); | |
443 | 6 | avio_wl64(pb, file_size); | |
444 | 6 | avio_wl64(pb, unix_to_file_time(asf->creation_time)); | |
445 | 6 | avio_wl64(pb, asf->nb_packets); /* number of packets */ | |
446 | 6 | avio_wl64(pb, duration); /* end time stamp (in 100ns units) */ | |
447 | 6 | avio_wl64(pb, asf->duration); /* duration (in 100ns units) */ | |
448 | 6 | avio_wl64(pb, PREROLL_TIME); /* start time stamp */ | |
449 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | avio_wl32(pb, (asf->is_streamed || !(pb->seekable & AVIO_SEEKABLE_NORMAL)) ? 3 : 2); /* ??? */ |
450 | 6 | avio_wl32(pb, s->packet_size); /* packet size */ | |
451 | 6 | avio_wl32(pb, s->packet_size); /* packet size */ | |
452 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | avio_wl32(pb, bit_rate ? bit_rate : -1); /* Maximum data rate in bps */ |
453 | 6 | end_header(pb, hpos); | |
454 | |||
455 | /* header_extension */ | ||
456 | 6 | hpos = put_header(pb, &ff_asf_head1_guid); | |
457 | 6 | ff_put_guid(pb, &ff_asf_head2_guid); | |
458 | 6 | avio_wl16(pb, 6); | |
459 | 6 | avio_wl32(pb, 0); /* length, to be filled later */ | |
460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (asf->nb_languages) { |
461 | int64_t hpos2; | ||
462 | ✗ | int nb_audio_languages = 0; | |
463 | |||
464 | ✗ | hpos2 = put_header(pb, &ff_asf_language_guid); | |
465 | ✗ | avio_wl16(pb, asf->nb_languages); | |
466 | ✗ | for (int i = 0; i < asf->nb_languages; i++) { | |
467 | ✗ | avio_w8(pb, 6); | |
468 | ✗ | avio_put_str16le(pb, asf->languages[i]); | |
469 | } | ||
470 | ✗ | end_header(pb, hpos2); | |
471 | |||
472 | ✗ | for (int i = 0; i < asf->nb_languages; i++) | |
473 | ✗ | if (audio_language_counts[i]) | |
474 | ✗ | nb_audio_languages++; | |
475 | |||
476 | ✗ | if (nb_audio_languages > 1) { | |
477 | ✗ | hpos2 = put_header(pb, &ff_asf_group_mutual_exclusion_object); | |
478 | ✗ | ff_put_guid(pb, &ff_asf_mutex_language); | |
479 | ✗ | avio_wl16(pb, nb_audio_languages); | |
480 | ✗ | for (int i = 0; i < asf->nb_languages; i++) { | |
481 | ✗ | if (audio_language_counts[i]) { | |
482 | ✗ | avio_wl16(pb, audio_language_counts[i]); | |
483 | ✗ | for (unsigned n = 0; n < s->nb_streams; n++) | |
484 | ✗ | if (asf->streams[n].stream_language_index == i && s->streams[n]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) | |
485 | ✗ | avio_wl16(pb, n + 1); | |
486 | } | ||
487 | } | ||
488 | ✗ | end_header(pb, hpos2); | |
489 | } | ||
490 | |||
491 | ✗ | for (unsigned n = 0; n < s->nb_streams; n++) { | |
492 | int64_t es_pos; | ||
493 | ✗ | if (asf->streams[n].stream_language_index > 127) | |
494 | ✗ | continue; | |
495 | ✗ | es_pos = put_header(pb, &ff_asf_extended_stream_properties_object); | |
496 | ✗ | avio_wl64(pb, 0); /* start time */ | |
497 | ✗ | avio_wl64(pb, 0); /* end time */ | |
498 | ✗ | avio_wl32(pb, s->streams[n]->codecpar->bit_rate); /* data bitrate bps */ | |
499 | ✗ | avio_wl32(pb, 5000); /* buffer size ms */ | |
500 | ✗ | avio_wl32(pb, 0); /* initial buffer fullness */ | |
501 | ✗ | avio_wl32(pb, s->streams[n]->codecpar->bit_rate); /* peak data bitrate */ | |
502 | ✗ | avio_wl32(pb, 5000); /* maximum buffer size ms */ | |
503 | ✗ | avio_wl32(pb, 0); /* max initial buffer fullness */ | |
504 | ✗ | avio_wl32(pb, 0); /* max object size */ | |
505 | ✗ | avio_wl32(pb, (!asf->is_streamed && (pb->seekable & AVIO_SEEKABLE_NORMAL)) << 1); /* flags - seekable */ | |
506 | ✗ | avio_wl16(pb, n + 1); /* stream number */ | |
507 | ✗ | avio_wl16(pb, asf->streams[n].stream_language_index); /* language id index */ | |
508 | ✗ | avio_wl64(pb, 0); /* avg time per frame */ | |
509 | ✗ | avio_wl16(pb, 0); /* stream name count */ | |
510 | ✗ | avio_wl16(pb, 0); /* payload extension system count */ | |
511 | ✗ | end_header(pb, es_pos); | |
512 | } | ||
513 | } | ||
514 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (has_aspect_ratio) { |
515 | int64_t hpos2; | ||
516 | ✗ | hpos2 = put_header(pb, &ff_asf_metadata_header); | |
517 | ✗ | avio_wl16(pb, 2 * has_aspect_ratio); | |
518 | ✗ | for (unsigned n = 0; n < s->nb_streams; n++) { | |
519 | ✗ | AVCodecParameters *const par = s->streams[n]->codecpar; | |
520 | ✗ | if ( par->codec_type == AVMEDIA_TYPE_VIDEO | |
521 | ✗ | && par->sample_aspect_ratio.num > 0 | |
522 | ✗ | && par->sample_aspect_ratio.den > 0) { | |
523 | ✗ | AVRational sar = par->sample_aspect_ratio; | |
524 | ✗ | avio_wl16(pb, 0); | |
525 | // the stream number is set like this below | ||
526 | ✗ | avio_wl16(pb, n + 1); | |
527 | ✗ | avio_wl16(pb, 26); // name_len | |
528 | ✗ | avio_wl16(pb, 3); // value_type | |
529 | ✗ | avio_wl32(pb, 4); // value_len | |
530 | ✗ | avio_put_str16le(pb, "AspectRatioX"); | |
531 | ✗ | avio_wl32(pb, sar.num); | |
532 | ✗ | avio_wl16(pb, 0); | |
533 | // the stream number is set like this below | ||
534 | ✗ | avio_wl16(pb, n + 1); | |
535 | ✗ | avio_wl16(pb, 26); // name_len | |
536 | ✗ | avio_wl16(pb, 3); // value_type | |
537 | ✗ | avio_wl32(pb, 4); // value_len | |
538 | ✗ | avio_put_str16le(pb, "AspectRatioY"); | |
539 | ✗ | avio_wl32(pb, sar.den); | |
540 | } | ||
541 | } | ||
542 | ✗ | end_header(pb, hpos2); | |
543 | } | ||
544 | { | ||
545 | int64_t pos1; | ||
546 | 6 | pos1 = avio_tell(pb); | |
547 | 6 | avio_seek(pb, hpos + 42, SEEK_SET); | |
548 | 6 | avio_wl32(pb, pos1 - hpos - 46); | |
549 | 6 | avio_seek(pb, pos1, SEEK_SET); | |
550 | } | ||
551 | 6 | end_header(pb, hpos); | |
552 | |||
553 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | if ((ret = avio_open_dyn_buf(&dyn_buf)) < 0) |
554 | ✗ | return ret; | |
555 | |||
556 | /* title and other info */ | ||
557 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
|
6 | if (has_title) { |
558 | uint8_t *buf; | ||
559 | int len; | ||
560 | |||
561 | 2 | hpos = put_header(pb, &ff_asf_comment_header); | |
562 | |||
563 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
|
12 | for (size_t n = 0; n < FF_ARRAY_ELEMS(tags); n++) { |
564 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8 times.
|
10 | len = tags[n] ? avio_put_str16le(dyn_buf, tags[n]->value) : 0; |
565 | 10 | avio_wl16(pb, len); | |
566 | } | ||
567 | 2 | len = avio_get_dyn_buf(dyn_buf, &buf); | |
568 | 2 | avio_write(pb, buf, len); | |
569 | 2 | ffio_reset_dyn_buf(dyn_buf); | |
570 | 2 | end_header(pb, hpos); | |
571 | } | ||
572 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (metadata_count) { |
573 | 6 | const AVDictionaryEntry *tag = NULL; | |
574 | 6 | hpos = put_header(pb, &ff_asf_extended_content_header); | |
575 | 6 | avio_wl16(pb, metadata_count); | |
576 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
|
12 | while ((tag = av_dict_iterate(s->metadata, tag))) { |
577 | 6 | put_str16(pb, dyn_buf, tag->key); | |
578 | 6 | avio_wl16(pb, 0); | |
579 | 6 | put_str16(pb, dyn_buf, tag->value); | |
580 | } | ||
581 | 6 | end_header(pb, hpos); | |
582 | } | ||
583 | /* chapters using ASF markers */ | ||
584 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (!asf->is_streamed && s->nb_chapters) { |
585 | ✗ | asf_write_markers(s, dyn_buf); | |
586 | } | ||
587 | /* stream headers */ | ||
588 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
|
14 | for (unsigned n = 0; n < s->nb_streams; n++) { |
589 | 8 | AVCodecParameters *const par = s->streams[n]->codecpar; | |
590 | int64_t es_pos; | ||
591 | // ASFStream *stream = &asf->streams[n]; | ||
592 | |||
593 | 8 | asf->streams[n].num = n + 1; | |
594 | 8 | asf->streams[n].seq = 1; | |
595 | |||
596 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | switch (par->codec_type) { |
597 | 6 | case AVMEDIA_TYPE_AUDIO: | |
598 | 6 | wav_extra_size = 0; | |
599 | 6 | extra_size = 18 + wav_extra_size; | |
600 | 6 | extra_size2 = 8; | |
601 | 6 | break; | |
602 | 2 | default: | |
603 | case AVMEDIA_TYPE_VIDEO: | ||
604 | 2 | wav_extra_size = par->extradata_size; | |
605 | 2 | extra_size = 0x33 + wav_extra_size; | |
606 | 2 | extra_size2 = 0; | |
607 | 2 | break; | |
608 | } | ||
609 | |||
610 | 8 | hpos = put_header(pb, &ff_asf_stream_header); | |
611 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | if (par->codec_type == AVMEDIA_TYPE_AUDIO) { |
612 | 6 | ff_put_guid(pb, &ff_asf_audio_stream); | |
613 | 6 | ff_put_guid(pb, &ff_asf_audio_conceal_spread); | |
614 | } else { | ||
615 | 2 | ff_put_guid(pb, &ff_asf_video_stream); | |
616 | 2 | ff_put_guid(pb, &ff_asf_video_conceal_none); | |
617 | } | ||
618 | 8 | avio_wl64(pb, 0); /* ??? */ | |
619 | 8 | es_pos = avio_tell(pb); | |
620 | 8 | avio_wl32(pb, extra_size); /* wav header len */ | |
621 | 8 | avio_wl32(pb, extra_size2); /* additional data len */ | |
622 | 8 | avio_wl16(pb, n + 1); /* stream number */ | |
623 | 8 | avio_wl32(pb, 0); /* ??? */ | |
624 | |||
625 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | if (par->codec_type == AVMEDIA_TYPE_AUDIO) { |
626 | /* WAVEFORMATEX header */ | ||
627 | 6 | int wavsize = ff_put_wav_header(s, pb, par, FF_PUT_WAV_HEADER_FORCE_WAVEFORMATEX); | |
628 | |||
629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (wavsize < 0) { |
630 | ✗ | ret = wavsize; | |
631 | ✗ | goto fail; | |
632 | } | ||
633 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (wavsize != extra_size) { |
634 | 6 | cur_pos = avio_tell(pb); | |
635 | 6 | avio_seek(pb, es_pos, SEEK_SET); | |
636 | 6 | avio_wl32(pb, wavsize); /* wav header len */ | |
637 | 6 | avio_seek(pb, cur_pos, SEEK_SET); | |
638 | } | ||
639 | /* ERROR Correction */ | ||
640 | 6 | avio_w8(pb, 0x01); | |
641 |
3/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
|
6 | if (par->codec_id == AV_CODEC_ID_ADPCM_G726 || !par->block_align) { |
642 | 2 | avio_wl16(pb, 0x0190); | |
643 | 2 | avio_wl16(pb, 0x0190); | |
644 | } else { | ||
645 | 4 | avio_wl16(pb, par->block_align); | |
646 | 4 | avio_wl16(pb, par->block_align); | |
647 | } | ||
648 | 6 | avio_wl16(pb, 0x01); | |
649 | 6 | avio_w8(pb, 0x00); | |
650 | } else { | ||
651 | 2 | avio_wl32(pb, par->width); | |
652 | 2 | avio_wl32(pb, par->height); | |
653 | 2 | avio_w8(pb, 2); /* ??? */ | |
654 | 2 | avio_wl16(pb, 40 + par->extradata_size); /* size */ | |
655 | |||
656 | /* BITMAPINFOHEADER header */ | ||
657 | 2 | ff_put_bmp_header(pb, par, 1, 0, 0); | |
658 | } | ||
659 | 8 | end_header(pb, hpos); | |
660 | } | ||
661 | |||
662 | /* media comments */ | ||
663 | |||
664 | 6 | hpos = put_header(pb, &ff_asf_codec_comment_header); | |
665 | 6 | ff_put_guid(pb, &ff_asf_codec_comment1_header); | |
666 | 6 | avio_wl32(pb, s->nb_streams); | |
667 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
|
14 | for (unsigned n = 0; n < s->nb_streams; n++) { |
668 | 8 | AVCodecParameters *const par = s->streams[n]->codecpar; | |
669 | 8 | const AVCodecDescriptor *const codec_desc = avcodec_descriptor_get(par->codec_id); | |
670 | const char *desc; | ||
671 | |||
672 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | if (par->codec_type == AVMEDIA_TYPE_AUDIO) |
673 | 6 | avio_wl16(pb, 2); | |
674 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | else if (par->codec_type == AVMEDIA_TYPE_VIDEO) |
675 | 2 | avio_wl16(pb, 1); | |
676 | else | ||
677 | ✗ | avio_wl16(pb, -1); | |
678 | |||
679 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | if (par->codec_id == AV_CODEC_ID_WMAV2) |
680 | 2 | desc = "Windows Media Audio V8"; | |
681 | else | ||
682 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | desc = codec_desc ? codec_desc->name : NULL; |
683 | |||
684 |
1/2✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
|
8 | if (desc) { |
685 | uint8_t *buf; | ||
686 | int len; | ||
687 | |||
688 | 8 | avio_put_str16le(dyn_buf, desc); | |
689 | 8 | len = avio_get_dyn_buf(dyn_buf, &buf); | |
690 | 8 | avio_wl16(pb, len / 2); // "number of characters" = length in bytes / 2 | |
691 | |||
692 | 8 | avio_write(pb, buf, len); | |
693 | 8 | ffio_reset_dyn_buf(dyn_buf); | |
694 | } else | ||
695 | ✗ | avio_wl16(pb, 0); | |
696 | |||
697 | 8 | avio_wl16(pb, 0); /* no parameters */ | |
698 | |||
699 | /* id */ | ||
700 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
|
8 | if (par->codec_type == AVMEDIA_TYPE_AUDIO) { |
701 | 6 | avio_wl16(pb, 2); | |
702 | 6 | avio_wl16(pb, par->codec_tag); | |
703 | } else { | ||
704 | 2 | avio_wl16(pb, 4); | |
705 | 2 | avio_wl32(pb, par->codec_tag); | |
706 | } | ||
707 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!par->codec_tag) { |
708 | ✗ | ret = AVERROR(EINVAL); | |
709 | ✗ | goto fail; | |
710 | } | ||
711 | } | ||
712 | 6 | end_header(pb, hpos); | |
713 | |||
714 | /* patch the header size fields */ | ||
715 | |||
716 | 6 | cur_pos = avio_tell(pb); | |
717 | 6 | header_size = cur_pos - header_offset; | |
718 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (asf->is_streamed) { |
719 | ✗ | header_size += 8 + 30 + DATA_HEADER_SIZE; | |
720 | |||
721 | ✗ | avio_seek(pb, header_offset - 10 - 30, SEEK_SET); | |
722 | ✗ | avio_wl16(pb, header_size); | |
723 | ✗ | avio_seek(pb, header_offset - 2 - 30, SEEK_SET); | |
724 | ✗ | avio_wl16(pb, header_size); | |
725 | |||
726 | ✗ | header_size -= 8 + 30 + DATA_HEADER_SIZE; | |
727 | } | ||
728 | 6 | header_size += 24 + 6; | |
729 | 6 | avio_seek(pb, header_offset - 14, SEEK_SET); | |
730 | 6 | avio_wl64(pb, header_size); | |
731 | 6 | avio_seek(pb, cur_pos, SEEK_SET); | |
732 | |||
733 | /* movie chunk, followed by packets of packet_size */ | ||
734 | 6 | asf->data_offset = cur_pos; | |
735 | 6 | ff_put_guid(pb, &ff_asf_data_header); | |
736 | 6 | avio_wl64(pb, data_chunk_size); | |
737 | 6 | ff_put_guid(pb, &ff_asf_my_guid); | |
738 | 6 | avio_wl64(pb, asf->nb_packets); /* nb packets */ | |
739 | 6 | avio_w8(pb, 1); /* ??? */ | |
740 | 6 | avio_w8(pb, 1); /* ??? */ | |
741 | 6 | ret = 0; | |
742 | 6 | fail: | |
743 | 6 | ffio_free_dyn_buf(&dyn_buf); | |
744 | 6 | return ret; | |
745 | } | ||
746 | |||
747 | 3 | static int asf_write_header(AVFormatContext *s) | |
748 | { | ||
749 | 3 | ASFContext *asf = s->priv_data; | |
750 | int ret; | ||
751 | |||
752 | 3 | s->packet_size = asf->packet_size; | |
753 | 3 | s->max_interleave_delta = 0; | |
754 | 3 | asf->nb_packets = 0; | |
755 | |||
756 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (s->nb_streams > 127) { |
757 | ✗ | av_log(s, AV_LOG_ERROR, "ASF can only handle 127 streams\n"); | |
758 | ✗ | return AVERROR(EINVAL); | |
759 | } | ||
760 | |||
761 | 3 | asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK); | |
762 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (!asf->index_ptr) |
763 | ✗ | return AVERROR(ENOMEM); | |
764 | 3 | asf->nb_index_memory_alloc = ASF_INDEX_BLOCK; | |
765 | 3 | asf->maximum_packet = 0; | |
766 | |||
767 | /* the data-chunk-size has to be 50 (DATA_HEADER_SIZE), which is | ||
768 | * data_size - asf->data_offset at the moment this function is done. | ||
769 | * It is needed to use asf as a streamable format. */ | ||
770 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if ((ret = asf_write_header1(s, 0, DATA_HEADER_SIZE)) < 0) |
771 | ✗ | return ret; | |
772 | |||
773 | 3 | asf->packet_nb_payloads = 0; | |
774 | 3 | asf->packet_timestamp_start = -1; | |
775 | 3 | asf->packet_timestamp_end = -1; | |
776 | 3 | ffio_init_write_context(&asf->pb, asf->packet_buf, s->packet_size); | |
777 | |||
778 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (s->avoid_negative_ts < 0) |
779 | 3 | s->avoid_negative_ts = 1; | |
780 | |||
781 | 3 | return 0; | |
782 | } | ||
783 | |||
784 | ✗ | static int asf_write_stream_header(AVFormatContext *s) | |
785 | { | ||
786 | ✗ | ASFContext *asf = s->priv_data; | |
787 | |||
788 | ✗ | asf->is_streamed = 1; | |
789 | |||
790 | ✗ | return asf_write_header(s); | |
791 | } | ||
792 | |||
793 | 208 | static int put_payload_parsing_info(AVFormatContext *s, | |
794 | unsigned sendtime, unsigned duration, | ||
795 | int nb_payloads, int padsize) | ||
796 | { | ||
797 | 208 | ASFContext *asf = s->priv_data; | |
798 | 208 | AVIOContext *pb = s->pb; | |
799 | int ppi_size; | ||
800 | 208 | int64_t start = avio_tell(pb); | |
801 | |||
802 | 208 | int iLengthTypeFlags = ASF_PPI_LENGTH_TYPE_FLAGS; | |
803 | |||
804 | 208 | padsize -= PACKET_HEADER_MIN_SIZE; | |
805 |
2/2✓ Branch 0 taken 135 times.
✓ Branch 1 taken 73 times.
|
208 | if (asf->multi_payloads_present) |
806 | 135 | padsize--; | |
807 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
|
208 | av_assert0(padsize >= 0); |
808 | |||
809 | 208 | avio_w8(pb, ASF_PACKET_ERROR_CORRECTION_FLAGS); | |
810 | 208 | ffio_fill(pb, 0x0, ASF_PACKET_ERROR_CORRECTION_DATA_SIZE); | |
811 | |||
812 |
2/2✓ Branch 0 taken 135 times.
✓ Branch 1 taken 73 times.
|
208 | if (asf->multi_payloads_present) |
813 | 135 | iLengthTypeFlags |= ASF_PPI_FLAG_MULTIPLE_PAYLOADS_PRESENT; | |
814 | |||
815 |
2/2✓ Branch 0 taken 110 times.
✓ Branch 1 taken 98 times.
|
208 | if (padsize > 0) { |
816 |
2/2✓ Branch 0 taken 107 times.
✓ Branch 1 taken 3 times.
|
110 | if (padsize < 256) |
817 | 107 | iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE; | |
818 | else | ||
819 | 3 | iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD; | |
820 | } | ||
821 | 208 | avio_w8(pb, iLengthTypeFlags); | |
822 | |||
823 | 208 | avio_w8(pb, ASF_PPI_PROPERTY_FLAGS); | |
824 | |||
825 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 205 times.
|
208 | if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD) |
826 | 3 | avio_wl16(pb, padsize - 2); | |
827 |
2/2✓ Branch 0 taken 107 times.
✓ Branch 1 taken 101 times.
|
208 | if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE) |
828 | 107 | avio_w8(pb, padsize - 1); | |
829 | |||
830 | 208 | avio_wl32(pb, sendtime); | |
831 | 208 | avio_wl16(pb, duration); | |
832 |
2/2✓ Branch 0 taken 135 times.
✓ Branch 1 taken 73 times.
|
208 | if (asf->multi_payloads_present) |
833 | 135 | avio_w8(pb, nb_payloads | ASF_PAYLOAD_FLAGS); | |
834 | |||
835 | 208 | ppi_size = avio_tell(pb) - start; | |
836 | |||
837 | 208 | return ppi_size; | |
838 | } | ||
839 | |||
840 | 208 | static void flush_packet(AVFormatContext *s) | |
841 | { | ||
842 | 208 | ASFContext *asf = s->priv_data; | |
843 | int packet_hdr_size, packet_filled_size; | ||
844 | |||
845 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
|
208 | av_assert0(asf->packet_timestamp_end >= asf->packet_timestamp_start); |
846 | |||
847 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
|
208 | if (asf->is_streamed) |
848 | ✗ | put_chunk(s, 0x4424, s->packet_size, 0); | |
849 | |||
850 | 208 | packet_hdr_size = put_payload_parsing_info(s, | |
851 | 208 | asf->packet_timestamp_start, | |
852 | 208 | asf->packet_timestamp_end - asf->packet_timestamp_start, | |
853 | 208 | asf->packet_nb_payloads, | |
854 | asf->packet_size_left); | ||
855 | |||
856 | 208 | packet_filled_size = asf->packet_size - asf->packet_size_left; | |
857 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 208 times.
|
208 | av_assert0(packet_hdr_size <= asf->packet_size_left); |
858 | 208 | memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left); | |
859 | |||
860 | 208 | avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size); | |
861 | |||
862 | 208 | avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_FLUSH_POINT); | |
863 | |||
864 | 208 | asf->nb_packets++; | |
865 | 208 | asf->packet_nb_payloads = 0; | |
866 | 208 | asf->packet_timestamp_start = -1; | |
867 | 208 | asf->packet_timestamp_end = -1; | |
868 | 208 | ffio_init_write_context(&asf->pb, asf->packet_buf, s->packet_size); | |
869 | 208 | } | |
870 | |||
871 | 572 | static void put_payload_header(AVFormatContext *s, ASFStream *stream, | |
872 | int64_t presentation_time, int m_obj_size, | ||
873 | int m_obj_offset, int payload_len, int flags) | ||
874 | { | ||
875 | 572 | ASFContext *asf = s->priv_data; | |
876 | 572 | AVIOContext *const pb = &asf->pb.pub; | |
877 | int val; | ||
878 | |||
879 | 572 | val = stream->num; | |
880 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 542 times.
|
572 | if (flags & AV_PKT_FLAG_KEY) |
881 | 30 | val |= ASF_PL_FLAG_KEY_FRAME; | |
882 | 572 | avio_w8(pb, val); | |
883 | |||
884 | 572 | avio_w8(pb, stream->seq); // Media object number | |
885 | 572 | avio_wl32(pb, m_obj_offset); // Offset Into Media Object | |
886 | |||
887 | // Replicated Data shall be at least 8 bytes long. | ||
888 | // The first 4 bytes of data shall contain the | ||
889 | // Size of the Media Object that the payload belongs to. | ||
890 | // The next 4 bytes of data shall contain the | ||
891 | // Presentation Time for the media object that the payload belongs to. | ||
892 | 572 | avio_w8(pb, ASF_PAYLOAD_REPLICATED_DATA_LENGTH); | |
893 | |||
894 | 572 | avio_wl32(pb, m_obj_size); // Replicated Data - Media Object Size | |
895 | 572 | avio_wl32(pb, (uint32_t) presentation_time); // Replicated Data - Presentation Time | |
896 | |||
897 |
2/2✓ Branch 0 taken 499 times.
✓ Branch 1 taken 73 times.
|
572 | if (asf->multi_payloads_present) { |
898 | 499 | avio_wl16(pb, payload_len); // payload length | |
899 | } | ||
900 | 572 | } | |
901 | |||
902 | 474 | static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, | |
903 | int64_t timestamp, const uint8_t *buf, | ||
904 | int m_obj_size, int flags) | ||
905 | { | ||
906 | 474 | ASFContext *asf = s->priv_data; | |
907 | int m_obj_offset, payload_len, frag_len1; | ||
908 | |||
909 | 474 | m_obj_offset = 0; | |
910 |
2/2✓ Branch 0 taken 679 times.
✓ Branch 1 taken 474 times.
|
1153 | while (m_obj_offset < m_obj_size) { |
911 | 679 | payload_len = m_obj_size - m_obj_offset; | |
912 |
2/2✓ Branch 0 taken 208 times.
✓ Branch 1 taken 471 times.
|
679 | if (asf->packet_timestamp_start == -1) { |
913 | 208 | const int multi_payload_constant = (asf->packet_size - MULTI_PAYLOAD_HEADERS); | |
914 | 208 | asf->multi_payloads_present = (payload_len < multi_payload_constant); | |
915 | |||
916 | 208 | asf->packet_size_left = asf->packet_size; | |
917 |
2/2✓ Branch 0 taken 135 times.
✓ Branch 1 taken 73 times.
|
208 | if (asf->multi_payloads_present) { |
918 | 135 | frag_len1 = multi_payload_constant - 1; | |
919 | } else { | ||
920 | 73 | frag_len1 = asf->packet_size - SINGLE_PAYLOAD_HEADERS; | |
921 | } | ||
922 | 208 | asf->packet_timestamp_start = timestamp; | |
923 | } else { | ||
924 | // multi payloads | ||
925 | 471 | frag_len1 = asf->packet_size_left - | |
926 | PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS - | ||
927 | PACKET_HEADER_MIN_SIZE - 1; | ||
928 | |||
929 |
2/2✓ Branch 0 taken 132 times.
✓ Branch 1 taken 339 times.
|
471 | if (frag_len1 < payload_len && |
930 |
2/2✓ Branch 0 taken 107 times.
✓ Branch 1 taken 25 times.
|
132 | avst->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { |
931 | 107 | flush_packet(s); | |
932 | 107 | continue; | |
933 | } | ||
934 |
1/2✓ Branch 0 taken 364 times.
✗ Branch 1 not taken.
|
364 | if (asf->packet_timestamp_start > INT64_MAX - UINT16_MAX || |
935 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 364 times.
|
364 | timestamp > asf->packet_timestamp_start + UINT16_MAX) { |
936 | ✗ | flush_packet(s); | |
937 | ✗ | continue; | |
938 | } | ||
939 | } | ||
940 |
1/2✓ Branch 0 taken 572 times.
✗ Branch 1 not taken.
|
572 | if (frag_len1 > 0) { |
941 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 474 times.
|
572 | if (payload_len > frag_len1) |
942 | 98 | payload_len = frag_len1; | |
943 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 474 times.
|
474 | else if (payload_len == (frag_len1 - 1)) |
944 | ✗ | payload_len = frag_len1 - 2; // additional byte need to put padding length | |
945 | |||
946 | 572 | put_payload_header(s, stream, timestamp + PREROLL_TIME, | |
947 | m_obj_size, m_obj_offset, payload_len, flags); | ||
948 | 572 | avio_write(&asf->pb.pub, buf, payload_len); | |
949 | |||
950 |
2/2✓ Branch 0 taken 499 times.
✓ Branch 1 taken 73 times.
|
572 | if (asf->multi_payloads_present) |
951 | 499 | asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS); | |
952 | else | ||
953 | 73 | asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD); | |
954 | 572 | asf->packet_timestamp_end = timestamp; | |
955 | |||
956 | 572 | asf->packet_nb_payloads++; | |
957 | } else { | ||
958 | ✗ | payload_len = 0; | |
959 | } | ||
960 | 572 | m_obj_offset += payload_len; | |
961 | 572 | buf += payload_len; | |
962 | |||
963 |
2/2✓ Branch 0 taken 73 times.
✓ Branch 1 taken 499 times.
|
572 | if (!asf->multi_payloads_present) |
964 | 73 | flush_packet(s); | |
965 |
2/2✓ Branch 0 taken 25 times.
✓ Branch 1 taken 474 times.
|
499 | else if (asf->packet_size_left <= (PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS + PACKET_HEADER_MIN_SIZE + 1)) |
966 | 25 | flush_packet(s); | |
967 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 474 times.
|
474 | else if (asf->packet_nb_payloads == ASF_PAYLOADS_PER_PACKET) |
968 | ✗ | flush_packet(s); | |
969 | } | ||
970 | 474 | stream->seq++; | |
971 | 474 | } | |
972 | |||
973 | 4 | static int update_index(AVFormatContext *s, int start_sec, | |
974 | uint32_t packet_number, uint16_t packet_count, | ||
975 | uint64_t packet_offset) | ||
976 | { | ||
977 | 4 | ASFContext *asf = s->priv_data; | |
978 | |||
979 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | if (start_sec > asf->next_start_sec) { |
980 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (!asf->next_start_sec) { |
981 | 1 | asf->next_packet_number = packet_number; | |
982 | 1 | asf->next_packet_count = packet_count; | |
983 | 1 | asf->next_packet_offset = packet_offset; | |
984 | } | ||
985 | |||
986 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (start_sec > asf->nb_index_memory_alloc) { |
987 | int err; | ||
988 | ✗ | asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1); | |
989 | ✗ | if ((err = av_reallocp_array(&asf->index_ptr, | |
990 | ✗ | asf->nb_index_memory_alloc, | |
991 | sizeof(*asf->index_ptr))) < 0) { | ||
992 | ✗ | asf->nb_index_memory_alloc = 0; | |
993 | ✗ | return err; | |
994 | } | ||
995 | } | ||
996 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
|
9 | for (int i = asf->next_start_sec; i < start_sec; i++) { |
997 | 6 | asf->index_ptr[i].packet_number = asf->next_packet_number; | |
998 | 6 | asf->index_ptr[i].packet_count = asf->next_packet_count; | |
999 | 6 | asf->index_ptr[i].send_time = asf->next_start_sec * INT64_C(10000000); | |
1000 | 6 | asf->index_ptr[i].offset = asf->next_packet_offset; | |
1001 | |||
1002 | } | ||
1003 | } | ||
1004 | 4 | asf->maximum_packet = FFMAX(asf->maximum_packet, packet_count); | |
1005 | 4 | asf->next_packet_number = packet_number; | |
1006 | 4 | asf->next_packet_count = packet_count; | |
1007 | 4 | asf->next_packet_offset = packet_offset; | |
1008 | 4 | asf->next_start_sec = start_sec; | |
1009 | |||
1010 | 4 | return 0; | |
1011 | } | ||
1012 | |||
1013 | 474 | static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) | |
1014 | { | ||
1015 | 474 | ASFContext *asf = s->priv_data; | |
1016 | 474 | AVIOContext *pb = s->pb; | |
1017 | ASFStream *stream; | ||
1018 | AVCodecParameters *par; | ||
1019 | uint32_t packet_number; | ||
1020 | int64_t pts; | ||
1021 | int start_sec; | ||
1022 | 474 | int flags = pkt->flags; | |
1023 | int ret; | ||
1024 | 474 | uint64_t offset = avio_tell(pb); | |
1025 | |||
1026 | 474 | par = s->streams[pkt->stream_index]->codecpar; | |
1027 | 474 | stream = &asf->streams[pkt->stream_index]; | |
1028 | |||
1029 |
2/2✓ Branch 0 taken 449 times.
✓ Branch 1 taken 25 times.
|
474 | if (par->codec_type == AVMEDIA_TYPE_AUDIO) |
1030 | 449 | flags &= ~AV_PKT_FLAG_KEY; | |
1031 | |||
1032 |
1/2✓ Branch 0 taken 474 times.
✗ Branch 1 not taken.
|
474 | pts = (pkt->pts != AV_NOPTS_VALUE) ? pkt->pts : pkt->dts; |
1033 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 474 times.
|
474 | av_assert0(pts != AV_NOPTS_VALUE); |
1034 |
1/2✓ Branch 0 taken 474 times.
✗ Branch 1 not taken.
|
474 | if ( pts < - PREROLL_TIME |
1035 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 474 times.
|
474 | || pts > (INT_MAX-3)/10000LL * ASF_INDEXED_INTERVAL - PREROLL_TIME) { |
1036 | ✗ | av_log(s, AV_LOG_ERROR, "input pts %"PRId64" is invalid\n", pts); | |
1037 | ✗ | return AVERROR(EINVAL); | |
1038 | } | ||
1039 | 474 | pts *= 10000; | |
1040 | 474 | asf->duration = FFMAX(asf->duration, pts + pkt->duration * 10000); | |
1041 | |||
1042 | 474 | packet_number = asf->nb_packets; | |
1043 | 474 | put_frame(s, stream, s->streams[pkt->stream_index], | |
1044 | 474 | pkt->dts, pkt->data, pkt->size, flags); | |
1045 | |||
1046 | 474 | start_sec = (int)((PREROLL_TIME * 10000 + pts + ASF_INDEXED_INTERVAL - 1) | |
1047 | 474 | / ASF_INDEXED_INTERVAL); | |
1048 | |||
1049 | /* check index */ | ||
1050 |
3/4✓ Branch 0 taken 474 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 471 times.
|
474 | if ((!asf->is_streamed) && (flags & AV_PKT_FLAG_KEY)) { |
1051 | 3 | uint16_t packet_count = asf->nb_packets - packet_number; | |
1052 | 3 | ret = update_index(s, start_sec, packet_number, packet_count, offset); | |
1053 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret < 0) |
1054 | ✗ | return ret; | |
1055 | } | ||
1056 | 474 | asf->end_sec = start_sec; | |
1057 | |||
1058 | 474 | return 0; | |
1059 | } | ||
1060 | |||
1061 | 1 | static int asf_write_index(AVFormatContext *s, const ASFIndex *index, | |
1062 | uint16_t max, uint32_t count) | ||
1063 | { | ||
1064 | 1 | AVIOContext *pb = s->pb; | |
1065 | |||
1066 | 1 | ff_put_guid(pb, &ff_asf_simple_index_header); | |
1067 | 1 | avio_wl64(pb, 24 + 16 + 8 + 4 + 4 + (4 + 2) * count); | |
1068 | 1 | ff_put_guid(pb, &ff_asf_my_guid); | |
1069 | 1 | avio_wl64(pb, ASF_INDEXED_INTERVAL); | |
1070 | 1 | avio_wl32(pb, max); | |
1071 | 1 | avio_wl32(pb, count); | |
1072 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
|
7 | for (uint32_t i = 0; i < count; i++) { |
1073 | 6 | avio_wl32(pb, index[i].packet_number); | |
1074 | 6 | avio_wl16(pb, index[i].packet_count); | |
1075 | } | ||
1076 | |||
1077 | 1 | return 0; | |
1078 | } | ||
1079 | |||
1080 | 3 | static int asf_write_trailer(AVFormatContext *s) | |
1081 | { | ||
1082 | 3 | ASFContext *asf = s->priv_data; | |
1083 | int64_t file_size, data_size; | ||
1084 | int ret; | ||
1085 | |||
1086 | /* flush the current packet */ | ||
1087 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if (asf->pb.pub.buf_ptr > asf->pb.pub.buffer) |
1088 | 3 | flush_packet(s); | |
1089 | |||
1090 | /* write index */ | ||
1091 | 3 | data_size = avio_tell(s->pb); | |
1092 |
3/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
3 | if (!asf->is_streamed && asf->next_start_sec) { |
1093 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if ((ret = update_index(s, asf->end_sec + 1, 0, 0, 0)) < 0) |
1094 | ✗ | return ret; | |
1095 | 1 | asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); | |
1096 | } | ||
1097 | |||
1098 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | if (asf->is_streamed || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { |
1099 | ✗ | put_chunk(s, 0x4524, 0, 0); /* end of stream */ | |
1100 | } else { | ||
1101 | /* rewrite an updated header */ | ||
1102 | 3 | file_size = avio_tell(s->pb); | |
1103 | 3 | avio_seek(s->pb, 0, SEEK_SET); | |
1104 | 3 | asf_write_header1(s, file_size, data_size - asf->data_offset); | |
1105 | } | ||
1106 | |||
1107 | 3 | return 0; | |
1108 | } | ||
1109 | |||
1110 | 3 | static void asf_deinit(AVFormatContext *s) | |
1111 | { | ||
1112 | 3 | ASFContext *const asf = s->priv_data; | |
1113 | |||
1114 | 3 | av_freep(&asf->index_ptr); | |
1115 | 3 | } | |
1116 | |||
1117 | static const AVOption asf_options[] = { | ||
1118 | { "packet_size", "Packet size", offsetof(ASFContext, packet_size), AV_OPT_TYPE_INT, {.i64 = 3200}, PACKET_SIZE_MIN, PACKET_SIZE_MAX, AV_OPT_FLAG_ENCODING_PARAM }, | ||
1119 | { NULL }, | ||
1120 | }; | ||
1121 | |||
1122 | static const AVClass asf_muxer_class = { | ||
1123 | .class_name = "ASF (stream) muxer", | ||
1124 | .item_name = av_default_item_name, | ||
1125 | .option = asf_options, | ||
1126 | .version = LIBAVUTIL_VERSION_INT, | ||
1127 | }; | ||
1128 | |||
1129 | #if CONFIG_ASF_MUXER | ||
1130 | const FFOutputFormat ff_asf_muxer = { | ||
1131 | .p.name = "asf", | ||
1132 | .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), | ||
1133 | .p.mime_type = "video/x-ms-asf", | ||
1134 | .p.extensions = "asf,wmv,wma", | ||
1135 | .p.audio_codec = AV_CODEC_ID_WMAV2, | ||
1136 | .p.video_codec = AV_CODEC_ID_MSMPEG4V3, | ||
1137 | .p.flags = AVFMT_GLOBALHEADER, | ||
1138 | .p.codec_tag = asf_codec_tags, | ||
1139 | .p.priv_class = &asf_muxer_class, | ||
1140 | .priv_data_size = sizeof(ASFContext), | ||
1141 | .write_header = asf_write_header, | ||
1142 | .write_packet = asf_write_packet, | ||
1143 | .write_trailer = asf_write_trailer, | ||
1144 | .deinit = asf_deinit, | ||
1145 | }; | ||
1146 | #endif /* CONFIG_ASF_MUXER */ | ||
1147 | |||
1148 | #if CONFIG_ASF_STREAM_MUXER | ||
1149 | const FFOutputFormat ff_asf_stream_muxer = { | ||
1150 | .p.name = "asf_stream", | ||
1151 | .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), | ||
1152 | .p.mime_type = "video/x-ms-asf", | ||
1153 | .p.extensions = "asf,wmv,wma", | ||
1154 | .priv_data_size = sizeof(ASFContext), | ||
1155 | .p.audio_codec = AV_CODEC_ID_WMAV2, | ||
1156 | .p.video_codec = AV_CODEC_ID_MSMPEG4V3, | ||
1157 | .write_header = asf_write_stream_header, | ||
1158 | .write_packet = asf_write_packet, | ||
1159 | .write_trailer = asf_write_trailer, | ||
1160 | .p.flags = AVFMT_GLOBALHEADER, | ||
1161 | .p.codec_tag = asf_codec_tags, | ||
1162 | .p.priv_class = &asf_muxer_class, | ||
1163 | .deinit = asf_deinit, | ||
1164 | }; | ||
1165 | #endif /* CONFIG_ASF_STREAM_MUXER */ | ||
1166 |