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