Mercurial > libavformat.hg
annotate asf-enc.c @ 372:2e12cd1b68ed libavformat
split asf patch by (Konstantin Andreyev <kandreyev at bcsii dot com>)
author | michael |
---|---|
date | Fri, 05 Mar 2004 21:34:30 +0000 |
parents | asf.c@845f9de2c883 |
children | e47d9c8e2054 |
rev | line source |
---|---|
0 | 1 /* |
372
2e12cd1b68ed
split asf patch by (Konstantin Andreyev <kandreyev at bcsii dot com>)
michael
parents:
370
diff
changeset
|
2 * Adaptive stream format encoder |
0 | 3 * Copyright (c) 2000, 2001 Fabrice Bellard. |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 #include "avformat.h" | |
20 #include "avi.h" | |
372
2e12cd1b68ed
split asf patch by (Konstantin Andreyev <kandreyev at bcsii dot com>)
michael
parents:
370
diff
changeset
|
21 #include "asf.h" |
0 | 22 |
348 | 23 #undef NDEBUG |
24 #include <assert.h> | |
25 | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
26 #ifdef CONFIG_ENCODERS |
0 | 27 static void put_guid(ByteIOContext *s, const GUID *g) |
28 { | |
29 int i; | |
30 | |
31 put_le32(s, g->v1); | |
32 put_le16(s, g->v2); | |
33 put_le16(s, g->v3); | |
34 for(i=0;i<8;i++) | |
35 put_byte(s, g->v4[i]); | |
36 } | |
37 | |
38 static void put_str16(ByteIOContext *s, const char *tag) | |
39 { | |
40 int c; | |
41 | |
42 put_le16(s,strlen(tag) + 1); | |
43 for(;;) { | |
65 | 44 c = (uint8_t)*tag++; |
0 | 45 put_le16(s, c); |
46 if (c == '\0') | |
47 break; | |
48 } | |
49 } | |
50 | |
51 static void put_str16_nolen(ByteIOContext *s, const char *tag) | |
52 { | |
53 int c; | |
54 | |
55 for(;;) { | |
65 | 56 c = (uint8_t)*tag++; |
0 | 57 put_le16(s, c); |
58 if (c == '\0') | |
59 break; | |
60 } | |
61 } | |
62 | |
65 | 63 static int64_t put_header(ByteIOContext *pb, const GUID *g) |
0 | 64 { |
65 | 65 int64_t pos; |
0 | 66 |
67 pos = url_ftell(pb); | |
68 put_guid(pb, g); | |
69 put_le64(pb, 24); | |
70 return pos; | |
71 } | |
72 | |
73 /* update header size */ | |
65 | 74 static void end_header(ByteIOContext *pb, int64_t pos) |
0 | 75 { |
65 | 76 int64_t pos1; |
0 | 77 |
78 pos1 = url_ftell(pb); | |
79 url_fseek(pb, pos + 16, SEEK_SET); | |
80 put_le64(pb, pos1 - pos); | |
81 url_fseek(pb, pos1, SEEK_SET); | |
82 } | |
83 | |
84 /* write an asf chunk (only used in streaming case) */ | |
85 static void put_chunk(AVFormatContext *s, int type, int payload_length, int flags) | |
86 { | |
87 ASFContext *asf = s->priv_data; | |
88 ByteIOContext *pb = &s->pb; | |
89 int length; | |
90 | |
91 length = payload_length + 8; | |
92 put_le16(pb, type); | |
93 put_le16(pb, length); | |
94 put_le32(pb, asf->seqno); | |
95 put_le16(pb, flags); /* unknown bytes */ | |
96 put_le16(pb, length); | |
97 asf->seqno++; | |
98 } | |
99 | |
100 /* convert from unix to windows time */ | |
65 | 101 static int64_t unix_to_file_time(int ti) |
0 | 102 { |
65 | 103 int64_t t; |
0 | 104 |
65 | 105 t = ti * int64_t_C(10000000); |
106 t += int64_t_C(116444736000000000); | |
0 | 107 return t; |
108 } | |
109 | |
110 /* write the header (used two times if non streamed) */ | |
65 | 111 static int asf_write_header1(AVFormatContext *s, int64_t file_size, int64_t data_chunk_size) |
0 | 112 { |
113 ASFContext *asf = s->priv_data; | |
114 ByteIOContext *pb = &s->pb; | |
115 int header_size, n, extra_size, extra_size2, wav_extra_size, file_time; | |
116 int has_title; | |
117 AVCodecContext *enc; | |
65 | 118 int64_t header_offset, cur_pos, hpos; |
0 | 119 int bit_rate; |
120 | |
121 has_title = (s->title[0] || s->author[0] || s->copyright[0] || s->comment[0]); | |
122 | |
123 bit_rate = 0; | |
124 for(n=0;n<s->nb_streams;n++) { | |
125 enc = &s->streams[n]->codec; | |
126 | |
127 bit_rate += enc->bit_rate; | |
128 } | |
129 | |
130 if (asf->is_streamed) { | |
131 put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ | |
132 } | |
133 | |
134 put_guid(pb, &asf_header); | |
135 put_le64(pb, -1); /* header length, will be patched after */ | |
136 put_le32(pb, 3 + has_title + s->nb_streams); /* number of chunks in header */ | |
137 put_byte(pb, 1); /* ??? */ | |
138 put_byte(pb, 2); /* ??? */ | |
139 | |
140 /* file header */ | |
141 header_offset = url_ftell(pb); | |
142 hpos = put_header(pb, &file_header); | |
143 put_guid(pb, &my_guid); | |
144 put_le64(pb, file_size); | |
145 file_time = 0; | |
146 put_le64(pb, unix_to_file_time(file_time)); | |
147 put_le64(pb, asf->nb_packets); /* number of packets */ | |
148 put_le64(pb, asf->duration); /* end time stamp (in 100ns units) */ | |
149 put_le64(pb, asf->duration); /* duration (in 100ns units) */ | |
150 put_le32(pb, 0); /* start time stamp */ | |
151 put_le32(pb, 0); /* ??? */ | |
152 put_le32(pb, asf->is_streamed ? 1 : 0); /* ??? */ | |
153 put_le32(pb, asf->packet_size); /* packet size */ | |
154 put_le32(pb, asf->packet_size); /* packet size */ | |
155 put_le32(pb, bit_rate); /* Nominal data rate in bps */ | |
156 end_header(pb, hpos); | |
157 | |
158 /* unknown headers */ | |
159 hpos = put_header(pb, &head1_guid); | |
160 put_guid(pb, &head2_guid); | |
161 put_le32(pb, 6); | |
162 put_le16(pb, 0); | |
163 end_header(pb, hpos); | |
164 | |
165 /* title and other infos */ | |
166 if (has_title) { | |
167 hpos = put_header(pb, &comment_header); | |
168 put_le16(pb, 2 * (strlen(s->title) + 1)); | |
169 put_le16(pb, 2 * (strlen(s->author) + 1)); | |
170 put_le16(pb, 2 * (strlen(s->copyright) + 1)); | |
171 put_le16(pb, 2 * (strlen(s->comment) + 1)); | |
172 put_le16(pb, 0); | |
173 put_str16_nolen(pb, s->title); | |
174 put_str16_nolen(pb, s->author); | |
175 put_str16_nolen(pb, s->copyright); | |
176 put_str16_nolen(pb, s->comment); | |
177 end_header(pb, hpos); | |
178 } | |
179 | |
180 /* stream headers */ | |
181 for(n=0;n<s->nb_streams;n++) { | |
65 | 182 int64_t es_pos; |
0 | 183 // ASFStream *stream = &asf->streams[n]; |
184 | |
185 enc = &s->streams[n]->codec; | |
186 asf->streams[n].num = n + 1; | |
187 asf->streams[n].seq = 0; | |
188 | |
189 switch(enc->codec_type) { | |
190 case CODEC_TYPE_AUDIO: | |
191 wav_extra_size = 0; | |
192 extra_size = 18 + wav_extra_size; | |
193 extra_size2 = 0; | |
194 break; | |
195 default: | |
196 case CODEC_TYPE_VIDEO: | |
197 wav_extra_size = 0; | |
198 extra_size = 0x33; | |
199 extra_size2 = 0; | |
200 break; | |
201 } | |
202 | |
203 hpos = put_header(pb, &stream_header); | |
204 if (enc->codec_type == CODEC_TYPE_AUDIO) { | |
205 put_guid(pb, &audio_stream); | |
206 put_guid(pb, &audio_conceal_none); | |
207 } else { | |
208 put_guid(pb, &video_stream); | |
209 put_guid(pb, &video_conceal_none); | |
210 } | |
211 put_le64(pb, 0); /* ??? */ | |
212 es_pos = url_ftell(pb); | |
213 put_le32(pb, extra_size); /* wav header len */ | |
214 put_le32(pb, extra_size2); /* additional data len */ | |
215 put_le16(pb, n + 1); /* stream number */ | |
216 put_le32(pb, 0); /* ??? */ | |
217 | |
218 if (enc->codec_type == CODEC_TYPE_AUDIO) { | |
219 /* WAVEFORMATEX header */ | |
220 int wavsize = put_wav_header(pb, enc); | |
221 | |
222 if (wavsize < 0) | |
223 return -1; | |
224 if (wavsize != extra_size) { | |
225 cur_pos = url_ftell(pb); | |
226 url_fseek(pb, es_pos, SEEK_SET); | |
227 put_le32(pb, wavsize); /* wav header len */ | |
228 url_fseek(pb, cur_pos, SEEK_SET); | |
229 } | |
230 } else { | |
231 put_le32(pb, enc->width); | |
232 put_le32(pb, enc->height); | |
233 put_byte(pb, 2); /* ??? */ | |
234 put_le16(pb, 40); /* size */ | |
235 | |
236 /* BITMAPINFOHEADER header */ | |
237 put_bmp_header(pb, enc, codec_bmp_tags, 1); | |
238 } | |
239 end_header(pb, hpos); | |
240 } | |
241 | |
242 /* media comments */ | |
243 | |
244 hpos = put_header(pb, &codec_comment_header); | |
245 put_guid(pb, &codec_comment1_header); | |
246 put_le32(pb, s->nb_streams); | |
247 for(n=0;n<s->nb_streams;n++) { | |
248 AVCodec *p; | |
249 | |
250 enc = &s->streams[n]->codec; | |
251 p = avcodec_find_encoder(enc->codec_id); | |
252 | |
253 put_le16(pb, asf->streams[n].num); | |
254 put_str16(pb, p ? p->name : enc->codec_name); | |
255 put_le16(pb, 0); /* no parameters */ | |
196 | 256 |
257 | |
0 | 258 /* id */ |
259 if (enc->codec_type == CODEC_TYPE_AUDIO) { | |
260 put_le16(pb, 2); | |
196 | 261 if(!enc->codec_tag) |
262 enc->codec_tag = codec_get_tag(codec_wav_tags, enc->codec_id); | |
263 if(!enc->codec_tag) | |
264 return -1; | |
265 put_le16(pb, enc->codec_tag); | |
0 | 266 } else { |
267 put_le16(pb, 4); | |
196 | 268 if(!enc->codec_tag) |
269 enc->codec_tag = codec_get_tag(codec_bmp_tags, enc->codec_id); | |
270 if(!enc->codec_tag) | |
271 return -1; | |
272 put_le32(pb, enc->codec_tag); | |
0 | 273 } |
274 } | |
275 end_header(pb, hpos); | |
276 | |
277 /* patch the header size fields */ | |
278 | |
279 cur_pos = url_ftell(pb); | |
280 header_size = cur_pos - header_offset; | |
281 if (asf->is_streamed) { | |
282 header_size += 8 + 30 + 50; | |
283 | |
284 url_fseek(pb, header_offset - 10 - 30, SEEK_SET); | |
285 put_le16(pb, header_size); | |
286 url_fseek(pb, header_offset - 2 - 30, SEEK_SET); | |
287 put_le16(pb, header_size); | |
288 | |
289 header_size -= 8 + 30 + 50; | |
290 } | |
291 header_size += 24 + 6; | |
292 url_fseek(pb, header_offset - 14, SEEK_SET); | |
293 put_le64(pb, header_size); | |
294 url_fseek(pb, cur_pos, SEEK_SET); | |
295 | |
296 /* movie chunk, followed by packets of packet_size */ | |
297 asf->data_offset = cur_pos; | |
298 put_guid(pb, &data_header); | |
299 put_le64(pb, data_chunk_size); | |
300 put_guid(pb, &my_guid); | |
301 put_le64(pb, asf->nb_packets); /* nb packets */ | |
302 put_byte(pb, 1); /* ??? */ | |
303 put_byte(pb, 1); /* ??? */ | |
304 return 0; | |
305 } | |
306 | |
307 static int asf_write_header(AVFormatContext *s) | |
308 { | |
309 ASFContext *asf = s->priv_data; | |
310 | |
311 av_set_pts_info(s, 32, 1, 1000); /* 32 bit pts in ms */ | |
312 | |
313 asf->packet_size = PACKET_SIZE; | |
314 asf->nb_packets = 0; | |
315 | |
316 if (asf_write_header1(s, 0, 50) < 0) { | |
317 //av_free(asf); | |
318 return -1; | |
319 } | |
320 | |
321 put_flush_packet(&s->pb); | |
322 | |
323 asf->packet_nb_frames = 0; | |
324 asf->packet_timestamp_start = -1; | |
325 asf->packet_timestamp_end = -1; | |
326 asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE; | |
327 init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, 1, | |
328 NULL, NULL, NULL, NULL); | |
329 | |
330 return 0; | |
331 } | |
332 | |
333 static int asf_write_stream_header(AVFormatContext *s) | |
334 { | |
335 ASFContext *asf = s->priv_data; | |
336 | |
337 asf->is_streamed = 1; | |
338 | |
339 return asf_write_header(s); | |
340 } | |
341 | |
342 /* write a fixed size packet */ | |
343 static int put_packet(AVFormatContext *s, | |
344 unsigned int timestamp, unsigned int duration, | |
345 int nb_frames, int padsize) | |
346 { | |
347 ASFContext *asf = s->priv_data; | |
348 ByteIOContext *pb = &s->pb; | |
349 int flags; | |
350 | |
351 if (asf->is_streamed) { | |
352 put_chunk(s, 0x4424, asf->packet_size, 0); | |
353 } | |
354 | |
355 put_byte(pb, 0x82); | |
356 put_le16(pb, 0); | |
357 | |
358 flags = 0x01; /* nb segments present */ | |
359 if (padsize > 0) { | |
360 if (padsize < 256) | |
361 flags |= 0x08; | |
362 else | |
363 flags |= 0x10; | |
364 } | |
365 put_byte(pb, flags); /* flags */ | |
366 put_byte(pb, 0x5d); | |
367 if (flags & 0x10) | |
368 put_le16(pb, padsize - 2); | |
369 if (flags & 0x08) | |
370 put_byte(pb, padsize - 1); | |
371 put_le32(pb, timestamp); | |
372 put_le16(pb, duration); | |
373 put_byte(pb, nb_frames | 0x80); | |
374 | |
375 return PACKET_HEADER_SIZE + ((flags & 0x18) >> 3); | |
376 } | |
377 | |
378 static void flush_packet(AVFormatContext *s) | |
379 { | |
380 ASFContext *asf = s->priv_data; | |
381 int hdr_size, ptr; | |
382 | |
383 hdr_size = put_packet(s, asf->packet_timestamp_start, | |
384 asf->packet_timestamp_end - asf->packet_timestamp_start, | |
385 asf->packet_nb_frames, asf->packet_size_left); | |
386 | |
387 /* Clear out the padding bytes */ | |
388 ptr = asf->packet_size - hdr_size - asf->packet_size_left; | |
389 memset(asf->packet_buf + ptr, 0, asf->packet_size_left); | |
390 | |
391 put_buffer(&s->pb, asf->packet_buf, asf->packet_size - hdr_size); | |
392 | |
393 put_flush_packet(&s->pb); | |
394 asf->nb_packets++; | |
395 asf->packet_nb_frames = 0; | |
396 asf->packet_timestamp_start = -1; | |
397 asf->packet_timestamp_end = -1; | |
398 asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE; | |
399 init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, 1, | |
400 NULL, NULL, NULL, NULL); | |
401 } | |
402 | |
403 static void put_frame_header(AVFormatContext *s, ASFStream *stream, int timestamp, | |
404 int payload_size, int frag_offset, int frag_len) | |
405 { | |
406 ASFContext *asf = s->priv_data; | |
407 ByteIOContext *pb = &asf->pb; | |
408 int val; | |
409 | |
410 val = stream->num; | |
7 | 411 if (s->streams[val - 1]->codec.coded_frame->key_frame /* && frag_offset == 0 */) |
0 | 412 val |= 0x80; |
413 put_byte(pb, val); | |
414 put_byte(pb, stream->seq); | |
415 put_le32(pb, frag_offset); /* fragment offset */ | |
416 put_byte(pb, 0x08); /* flags */ | |
417 put_le32(pb, payload_size); | |
418 put_le32(pb, timestamp); | |
419 put_le16(pb, frag_len); | |
420 } | |
421 | |
422 | |
423 /* Output a frame. We suppose that payload_size <= PACKET_SIZE. | |
424 | |
425 It is there that you understand that the ASF format is really | |
426 crap. They have misread the MPEG Systems spec ! | |
427 */ | |
428 static void put_frame(AVFormatContext *s, ASFStream *stream, int timestamp, | |
241 | 429 const uint8_t *buf, int payload_size) |
0 | 430 { |
431 ASFContext *asf = s->priv_data; | |
432 int frag_pos, frag_len, frag_len1; | |
433 | |
434 frag_pos = 0; | |
435 while (frag_pos < payload_size) { | |
436 frag_len = payload_size - frag_pos; | |
437 frag_len1 = asf->packet_size_left - FRAME_HEADER_SIZE; | |
438 if (frag_len1 > 0) { | |
439 if (frag_len > frag_len1) | |
440 frag_len = frag_len1; | |
441 put_frame_header(s, stream, timestamp+1, payload_size, frag_pos, frag_len); | |
442 put_buffer(&asf->pb, buf, frag_len); | |
443 asf->packet_size_left -= (frag_len + FRAME_HEADER_SIZE); | |
444 asf->packet_timestamp_end = timestamp; | |
445 if (asf->packet_timestamp_start == -1) | |
446 asf->packet_timestamp_start = timestamp; | |
447 asf->packet_nb_frames++; | |
448 } else { | |
449 frag_len = 0; | |
450 } | |
451 frag_pos += frag_len; | |
452 buf += frag_len; | |
453 /* output the frame if filled */ | |
454 if (asf->packet_size_left <= FRAME_HEADER_SIZE) | |
455 flush_packet(s); | |
456 } | |
457 stream->seq++; | |
458 } | |
459 | |
460 | |
461 static int asf_write_packet(AVFormatContext *s, int stream_index, | |
241 | 462 const uint8_t *buf, int size, int64_t timestamp) |
0 | 463 { |
464 ASFContext *asf = s->priv_data; | |
465 ASFStream *stream; | |
65 | 466 int64_t duration; |
0 | 467 AVCodecContext *codec; |
468 | |
469 codec = &s->streams[stream_index]->codec; | |
470 stream = &asf->streams[stream_index]; | |
471 | |
472 if (codec->codec_type == CODEC_TYPE_AUDIO) { | |
65 | 473 duration = (codec->frame_number * codec->frame_size * int64_t_C(10000000)) / |
0 | 474 codec->sample_rate; |
475 } else { | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
84
diff
changeset
|
476 duration = av_rescale(codec->frame_number * codec->frame_rate_base, 10000000, codec->frame_rate); |
0 | 477 } |
478 if (duration > asf->duration) | |
479 asf->duration = duration; | |
480 | |
481 put_frame(s, stream, timestamp, buf, size); | |
482 return 0; | |
483 } | |
484 | |
485 static int asf_write_trailer(AVFormatContext *s) | |
486 { | |
487 ASFContext *asf = s->priv_data; | |
65 | 488 int64_t file_size; |
0 | 489 |
490 /* flush the current packet */ | |
491 if (asf->pb.buf_ptr > asf->pb.buffer) | |
492 flush_packet(s); | |
493 | |
494 if (asf->is_streamed) { | |
495 put_chunk(s, 0x4524, 0, 0); /* end of stream */ | |
496 } else { | |
497 /* rewrite an updated header */ | |
498 file_size = url_ftell(&s->pb); | |
499 url_fseek(&s->pb, 0, SEEK_SET); | |
500 asf_write_header1(s, file_size, file_size - asf->data_offset); | |
501 } | |
502 | |
503 put_flush_packet(&s->pb); | |
504 return 0; | |
505 } | |
506 | |
372
2e12cd1b68ed
split asf patch by (Konstantin Andreyev <kandreyev at bcsii dot com>)
michael
parents:
370
diff
changeset
|
507 AVOutputFormat asf_oformat = { |
0 | 508 "asf", |
509 "asf format", | |
14
b167760cd0aa
mimetype fixes patch by (Ryutaroh Matsumoto <ryutaroh at it dot ss dot titech dot ac dot jp>)
michaelni
parents:
7
diff
changeset
|
510 "video/x-ms-asf", |
0 | 511 "asf,wmv", |
512 sizeof(ASFContext), | |
513 #ifdef CONFIG_MP3LAME | |
232 | 514 CODEC_ID_MP3, |
0 | 515 #else |
516 CODEC_ID_MP2, | |
517 #endif | |
518 CODEC_ID_MSMPEG4V3, | |
519 asf_write_header, | |
520 asf_write_packet, | |
521 asf_write_trailer, | |
522 }; | |
523 | |
372
2e12cd1b68ed
split asf patch by (Konstantin Andreyev <kandreyev at bcsii dot com>)
michael
parents:
370
diff
changeset
|
524 AVOutputFormat asf_stream_oformat = { |
0 | 525 "asf_stream", |
526 "asf format", | |
14
b167760cd0aa
mimetype fixes patch by (Ryutaroh Matsumoto <ryutaroh at it dot ss dot titech dot ac dot jp>)
michaelni
parents:
7
diff
changeset
|
527 "video/x-ms-asf", |
0 | 528 "asf,wmv", |
529 sizeof(ASFContext), | |
530 #ifdef CONFIG_MP3LAME | |
232 | 531 CODEC_ID_MP3, |
0 | 532 #else |
533 CODEC_ID_MP2, | |
534 #endif | |
535 CODEC_ID_MSMPEG4V3, | |
536 asf_write_stream_header, | |
537 asf_write_packet, | |
538 asf_write_trailer, | |
539 }; | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
540 #endif //CONFIG_ENCODERS |