Mercurial > libavformat.hg
annotate swf.c @ 104:1f218c4b0e2e libavformat
Clear out an unused field in the created AVI file. This helps make
the MD5 checksums *much* more predictable!!
author | philipjsg |
---|---|
date | Sat, 12 Apr 2003 03:04:08 +0000 |
parents | 25062c9b1f86 |
children | 2fa5e94ba716 |
rev | line source |
---|---|
0 | 1 /* |
2 * Flash Compatible Streaming Format | |
3 * Copyright (c) 2000 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 | |
21 /* should have a generic way to indicate probable size */ | |
22 #define DUMMY_FILE_SIZE (100 * 1024 * 1024) | |
23 #define DUMMY_DURATION 600 /* in seconds */ | |
24 | |
25 #define TAG_END 0 | |
26 #define TAG_SHOWFRAME 1 | |
27 #define TAG_DEFINESHAPE 2 | |
28 #define TAG_FREECHARACTER 3 | |
29 #define TAG_PLACEOBJECT 4 | |
30 #define TAG_REMOVEOBJECT 5 | |
31 #define TAG_STREAMHEAD 18 | |
32 #define TAG_STREAMBLOCK 19 | |
33 #define TAG_JPEG2 21 | |
34 | |
35 #define TAG_LONG 0x100 | |
36 | |
37 /* flags for shape definition */ | |
38 #define FLAG_MOVETO 0x01 | |
39 #define FLAG_SETFILL0 0x02 | |
40 #define FLAG_SETFILL1 0x04 | |
41 | |
42 /* character id used */ | |
43 #define BITMAP_ID 0 | |
44 #define SHAPE_ID 1 | |
45 | |
46 typedef struct { | |
47 offset_t duration_pos; | |
48 offset_t tag_pos; | |
49 int tag; | |
50 } SWFContext; | |
51 | |
52 static void put_swf_tag(AVFormatContext *s, int tag) | |
53 { | |
54 SWFContext *swf = s->priv_data; | |
55 ByteIOContext *pb = &s->pb; | |
56 | |
57 swf->tag_pos = url_ftell(pb); | |
58 swf->tag = tag; | |
59 /* reserve some room for the tag */ | |
60 if (tag & TAG_LONG) { | |
61 put_le16(pb, 0); | |
62 put_le32(pb, 0); | |
63 } else { | |
64 put_le16(pb, 0); | |
65 } | |
66 } | |
67 | |
68 static void put_swf_end_tag(AVFormatContext *s) | |
69 { | |
70 SWFContext *swf = s->priv_data; | |
71 ByteIOContext *pb = &s->pb; | |
72 offset_t pos; | |
73 int tag_len, tag; | |
74 | |
75 pos = url_ftell(pb); | |
76 tag_len = pos - swf->tag_pos - 2; | |
77 tag = swf->tag; | |
78 url_fseek(pb, swf->tag_pos, SEEK_SET); | |
79 if (tag & TAG_LONG) { | |
80 tag &= ~TAG_LONG; | |
81 put_le16(pb, (tag << 6) | 0x3f); | |
82 put_le32(pb, tag_len - 4); | |
83 } else { | |
84 assert(tag_len < 0x3f); | |
85 put_le16(pb, (tag << 6) | tag_len); | |
86 } | |
87 url_fseek(pb, pos, SEEK_SET); | |
88 } | |
89 | |
90 static inline void max_nbits(int *nbits_ptr, int val) | |
91 { | |
92 int n; | |
93 | |
94 if (val == 0) | |
95 return; | |
96 val = abs(val); | |
97 n = 1; | |
98 while (val != 0) { | |
99 n++; | |
100 val >>= 1; | |
101 } | |
102 if (n > *nbits_ptr) | |
103 *nbits_ptr = n; | |
104 } | |
105 | |
106 static void put_swf_rect(ByteIOContext *pb, | |
107 int xmin, int xmax, int ymin, int ymax) | |
108 { | |
109 PutBitContext p; | |
65 | 110 uint8_t buf[256]; |
0 | 111 int nbits, mask; |
112 | |
113 init_put_bits(&p, buf, sizeof(buf), NULL, NULL); | |
114 | |
115 nbits = 0; | |
116 max_nbits(&nbits, xmin); | |
117 max_nbits(&nbits, xmax); | |
118 max_nbits(&nbits, ymin); | |
119 max_nbits(&nbits, ymax); | |
120 mask = (1 << nbits) - 1; | |
121 | |
122 /* rectangle info */ | |
123 put_bits(&p, 5, nbits); | |
124 put_bits(&p, nbits, xmin & mask); | |
125 put_bits(&p, nbits, xmax & mask); | |
126 put_bits(&p, nbits, ymin & mask); | |
127 put_bits(&p, nbits, ymax & mask); | |
128 | |
129 flush_put_bits(&p); | |
130 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
131 } | |
132 | |
133 static void put_swf_line_edge(PutBitContext *pb, int dx, int dy) | |
134 { | |
135 int nbits, mask; | |
136 | |
137 put_bits(pb, 1, 1); /* edge */ | |
138 put_bits(pb, 1, 1); /* line select */ | |
139 nbits = 2; | |
140 max_nbits(&nbits, dx); | |
141 max_nbits(&nbits, dy); | |
142 | |
143 mask = (1 << nbits) - 1; | |
144 put_bits(pb, 4, nbits - 2); /* 16 bits precision */ | |
145 if (dx == 0) { | |
146 put_bits(pb, 1, 0); | |
147 put_bits(pb, 1, 1); | |
148 put_bits(pb, nbits, dy & mask); | |
149 } else if (dy == 0) { | |
150 put_bits(pb, 1, 0); | |
151 put_bits(pb, 1, 0); | |
152 put_bits(pb, nbits, dx & mask); | |
153 } else { | |
154 put_bits(pb, 1, 1); | |
155 put_bits(pb, nbits, dx & mask); | |
156 put_bits(pb, nbits, dy & mask); | |
157 } | |
158 } | |
159 | |
160 #define FRAC_BITS 16 | |
161 | |
162 /* put matrix (not size optimized */ | |
163 static void put_swf_matrix(ByteIOContext *pb, | |
164 int a, int b, int c, int d, int tx, int ty) | |
165 { | |
166 PutBitContext p; | |
65 | 167 uint8_t buf[256]; |
0 | 168 |
169 init_put_bits(&p, buf, sizeof(buf), NULL, NULL); | |
170 | |
171 put_bits(&p, 1, 1); /* a, d present */ | |
172 put_bits(&p, 5, 20); /* nb bits */ | |
173 put_bits(&p, 20, a); | |
174 put_bits(&p, 20, d); | |
175 | |
176 put_bits(&p, 1, 1); /* b, c present */ | |
177 put_bits(&p, 5, 20); /* nb bits */ | |
178 put_bits(&p, 20, c); | |
179 put_bits(&p, 20, b); | |
180 | |
181 put_bits(&p, 5, 20); /* nb bits */ | |
182 put_bits(&p, 20, tx); | |
183 put_bits(&p, 20, ty); | |
184 | |
185 flush_put_bits(&p); | |
186 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
187 } | |
188 | |
189 /* XXX: handle audio only */ | |
190 static int swf_write_header(AVFormatContext *s) | |
191 { | |
192 SWFContext *swf; | |
193 ByteIOContext *pb = &s->pb; | |
194 AVCodecContext *enc, *audio_enc, *video_enc; | |
195 PutBitContext p; | |
65 | 196 uint8_t buf1[256]; |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
197 int i, width, height, rate, rate_base; |
0 | 198 |
199 swf = av_malloc(sizeof(SWFContext)); | |
200 if (!swf) | |
201 return -1; | |
202 s->priv_data = swf; | |
203 | |
204 video_enc = NULL; | |
205 audio_enc = NULL; | |
206 for(i=0;i<s->nb_streams;i++) { | |
207 enc = &s->streams[i]->codec; | |
208 if (enc->codec_type == CODEC_TYPE_AUDIO) | |
209 audio_enc = enc; | |
210 else | |
211 video_enc = enc; | |
212 } | |
213 | |
214 if (!video_enc) { | |
215 /* currenty, cannot work correctly if audio only */ | |
216 width = 320; | |
217 height = 200; | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
218 rate = 10; |
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
219 rate_base= 1; |
0 | 220 } else { |
221 width = video_enc->width; | |
222 height = video_enc->height; | |
223 rate = video_enc->frame_rate; | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
224 rate_base = video_enc->frame_rate_base; |
0 | 225 } |
226 | |
227 put_tag(pb, "FWS"); | |
228 put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */ | |
229 put_le32(pb, DUMMY_FILE_SIZE); /* dummy size | |
230 (will be patched if not streamed) */ | |
231 | |
232 put_swf_rect(pb, 0, width, 0, height); | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
233 put_le16(pb, (rate * 256) / rate_base); /* frame rate */ |
0 | 234 swf->duration_pos = url_ftell(pb); |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
235 put_le16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */ |
0 | 236 |
237 /* define a shape with the jpeg inside */ | |
238 | |
239 put_swf_tag(s, TAG_DEFINESHAPE); | |
240 | |
241 put_le16(pb, SHAPE_ID); /* ID of shape */ | |
242 /* bounding rectangle */ | |
243 put_swf_rect(pb, 0, width, 0, height); | |
244 /* style info */ | |
245 put_byte(pb, 1); /* one fill style */ | |
246 put_byte(pb, 0x41); /* clipped bitmap fill */ | |
247 put_le16(pb, BITMAP_ID); /* bitmap ID */ | |
248 /* position of the bitmap */ | |
249 put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, | |
250 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); | |
251 put_byte(pb, 0); /* no line style */ | |
252 | |
253 /* shape drawing */ | |
254 init_put_bits(&p, buf1, sizeof(buf1), NULL, NULL); | |
255 put_bits(&p, 4, 1); /* one fill bit */ | |
256 put_bits(&p, 4, 0); /* zero line bit */ | |
257 | |
258 put_bits(&p, 1, 0); /* not an edge */ | |
259 put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0); | |
260 put_bits(&p, 5, 1); /* nbits */ | |
261 put_bits(&p, 1, 0); /* X */ | |
262 put_bits(&p, 1, 0); /* Y */ | |
263 put_bits(&p, 1, 1); /* set fill style 1 */ | |
264 | |
265 /* draw the rectangle ! */ | |
266 put_swf_line_edge(&p, width, 0); | |
267 put_swf_line_edge(&p, 0, height); | |
268 put_swf_line_edge(&p, -width, 0); | |
269 put_swf_line_edge(&p, 0, -height); | |
270 | |
271 /* end of shape */ | |
272 put_bits(&p, 1, 0); /* not an edge */ | |
273 put_bits(&p, 5, 0); | |
274 | |
275 flush_put_bits(&p); | |
276 put_buffer(pb, buf1, pbBufPtr(&p) - p.buf); | |
277 | |
278 put_swf_end_tag(s); | |
279 | |
280 | |
281 if (audio_enc) { | |
282 int v; | |
283 | |
284 /* start sound */ | |
285 | |
286 v = 0; | |
287 switch(audio_enc->sample_rate) { | |
288 case 11025: | |
289 v |= 1 << 2; | |
290 break; | |
291 case 22050: | |
292 v |= 2 << 2; | |
293 break; | |
294 case 44100: | |
295 v |= 3 << 2; | |
296 break; | |
297 default: | |
298 /* not supported */ | |
299 av_free(swf); | |
300 return -1; | |
301 } | |
302 if (audio_enc->channels == 2) | |
303 v |= 1; | |
304 v |= 0x20; /* mp3 compressed */ | |
305 v |= 0x02; /* 16 bits */ | |
306 | |
307 put_swf_tag(s, TAG_STREAMHEAD); | |
308 put_byte(&s->pb, 0); | |
309 put_byte(&s->pb, v); | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
310 put_le16(&s->pb, (audio_enc->sample_rate * rate_base) / rate); /* avg samples per frame */ |
0 | 311 |
312 | |
313 put_swf_end_tag(s); | |
314 } | |
315 | |
316 put_flush_packet(&s->pb); | |
317 return 0; | |
318 } | |
319 | |
320 static int swf_write_video(AVFormatContext *s, | |
65 | 321 AVCodecContext *enc, uint8_t *buf, int size) |
0 | 322 { |
323 ByteIOContext *pb = &s->pb; | |
324 static int tag_id = 0; | |
325 | |
326 if (enc->frame_number > 1) { | |
327 /* remove the shape */ | |
328 put_swf_tag(s, TAG_REMOVEOBJECT); | |
329 put_le16(pb, SHAPE_ID); /* shape ID */ | |
330 put_le16(pb, 1); /* depth */ | |
331 put_swf_end_tag(s); | |
332 | |
333 /* free the bitmap */ | |
334 put_swf_tag(s, TAG_FREECHARACTER); | |
335 put_le16(pb, BITMAP_ID); | |
336 put_swf_end_tag(s); | |
337 } | |
338 | |
339 put_swf_tag(s, TAG_JPEG2 | TAG_LONG); | |
340 | |
341 put_le16(pb, tag_id); /* ID of the image */ | |
342 | |
343 /* a dummy jpeg header seems to be required */ | |
344 put_byte(pb, 0xff); | |
345 put_byte(pb, 0xd8); | |
346 put_byte(pb, 0xff); | |
347 put_byte(pb, 0xd9); | |
348 /* write the jpeg image */ | |
349 put_buffer(pb, buf, size); | |
350 | |
351 put_swf_end_tag(s); | |
352 | |
353 /* draw the shape */ | |
354 | |
355 put_swf_tag(s, TAG_PLACEOBJECT); | |
356 put_le16(pb, SHAPE_ID); /* shape ID */ | |
357 put_le16(pb, 1); /* depth */ | |
358 put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); | |
359 put_swf_end_tag(s); | |
360 | |
361 /* output the frame */ | |
362 put_swf_tag(s, TAG_SHOWFRAME); | |
363 put_swf_end_tag(s); | |
364 | |
365 put_flush_packet(&s->pb); | |
366 return 0; | |
367 } | |
368 | |
65 | 369 static int swf_write_audio(AVFormatContext *s, uint8_t *buf, int size) |
0 | 370 { |
371 ByteIOContext *pb = &s->pb; | |
372 | |
373 put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); | |
374 | |
375 put_buffer(pb, buf, size); | |
376 | |
377 put_swf_end_tag(s); | |
378 put_flush_packet(&s->pb); | |
379 return 0; | |
380 } | |
381 | |
382 static int swf_write_packet(AVFormatContext *s, int stream_index, | |
65 | 383 uint8_t *buf, int size, int force_pts) |
0 | 384 { |
385 AVCodecContext *codec = &s->streams[stream_index]->codec; | |
386 if (codec->codec_type == CODEC_TYPE_AUDIO) | |
387 return swf_write_audio(s, buf, size); | |
388 else | |
389 return swf_write_video(s, codec, buf, size); | |
390 } | |
391 | |
392 static int swf_write_trailer(AVFormatContext *s) | |
393 { | |
394 SWFContext *swf = s->priv_data; | |
395 ByteIOContext *pb = &s->pb; | |
396 AVCodecContext *enc, *video_enc; | |
397 int file_size, i; | |
398 | |
399 video_enc = NULL; | |
400 for(i=0;i<s->nb_streams;i++) { | |
401 enc = &s->streams[i]->codec; | |
402 if (enc->codec_type == CODEC_TYPE_VIDEO) | |
403 video_enc = enc; | |
404 } | |
405 | |
406 put_swf_tag(s, TAG_END); | |
407 put_swf_end_tag(s); | |
408 | |
409 put_flush_packet(&s->pb); | |
410 | |
411 /* patch file size and number of frames if not streamed */ | |
412 if (!url_is_streamed(&s->pb) && video_enc) { | |
413 file_size = url_ftell(pb); | |
414 url_fseek(pb, 4, SEEK_SET); | |
415 put_le32(pb, file_size); | |
416 url_fseek(pb, swf->duration_pos, SEEK_SET); | |
417 put_le16(pb, video_enc->frame_number); | |
418 } | |
419 return 0; | |
420 } | |
421 | |
422 /***********************************/ | |
423 /* just to extract MP3 from swf */ | |
424 | |
425 static int get_swf_tag(ByteIOContext *pb, int *len_ptr) | |
426 { | |
427 int tag, len; | |
428 | |
429 if (url_feof(pb)) | |
430 return -1; | |
431 | |
432 tag = get_le16(pb); | |
433 len = tag & 0x3f; | |
434 tag = tag >> 6; | |
435 if (len == 0x3f) { | |
436 len = get_le32(pb); | |
437 } | |
438 *len_ptr = len; | |
439 return tag; | |
440 } | |
441 | |
442 | |
443 static int swf_probe(AVProbeData *p) | |
444 { | |
445 /* check file header */ | |
446 if (p->buf_size <= 16) | |
447 return 0; | |
448 if (p->buf[0] == 'F' && p->buf[1] == 'W' && | |
449 p->buf[2] == 'S') | |
450 return AVPROBE_SCORE_MAX; | |
451 else | |
452 return 0; | |
453 } | |
454 | |
455 static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
456 { | |
457 ByteIOContext *pb = &s->pb; | |
458 int nbits, len, frame_rate, tag, v; | |
459 AVStream *st; | |
460 | |
461 if ((get_be32(pb) & 0xffffff00) != MKBETAG('F', 'W', 'S', 0)) | |
462 return -EIO; | |
463 get_le32(pb); | |
464 /* skip rectangle size */ | |
465 nbits = get_byte(pb) >> 3; | |
466 len = (4 * nbits - 3 + 7) / 8; | |
467 url_fskip(pb, len); | |
468 frame_rate = get_le16(pb); | |
469 get_le16(pb); /* frame count */ | |
470 | |
471 for(;;) { | |
472 tag = get_swf_tag(pb, &len); | |
473 if (tag < 0) { | |
474 fprintf(stderr, "No streaming found in SWF\n"); | |
475 return -EIO; | |
476 } | |
477 if (tag == TAG_STREAMHEAD) { | |
478 /* streaming found */ | |
479 get_byte(pb); | |
480 v = get_byte(pb); | |
481 get_le16(pb); | |
482 /* if mp3 streaming found, OK */ | |
483 if ((v & 0x20) != 0) { | |
484 st = av_mallocz(sizeof(AVStream)); | |
485 if (!st) | |
486 return -ENOMEM; | |
5 | 487 avcodec_get_context_defaults(&st->codec); |
488 | |
0 | 489 if (v & 0x01) |
490 st->codec.channels = 2; | |
491 else | |
492 st->codec.channels = 1; | |
493 s->nb_streams = 1; | |
494 s->streams[0] = st; | |
495 | |
496 switch((v>> 2) & 0x03) { | |
497 case 1: | |
498 st->codec.sample_rate = 11025; | |
499 break; | |
500 case 2: | |
501 st->codec.sample_rate = 22050; | |
502 break; | |
503 case 3: | |
504 st->codec.sample_rate = 44100; | |
505 break; | |
506 default: | |
507 av_free(st); | |
508 return -EIO; | |
509 } | |
510 st->codec.codec_type = CODEC_TYPE_AUDIO; | |
511 st->codec.codec_id = CODEC_ID_MP2; | |
512 break; | |
513 } | |
514 } else { | |
515 url_fskip(pb, len); | |
516 } | |
517 } | |
518 | |
519 return 0; | |
520 } | |
521 | |
522 static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) | |
523 { | |
524 ByteIOContext *pb = &s->pb; | |
525 int tag, len; | |
526 | |
527 for(;;) { | |
528 tag = get_swf_tag(pb, &len); | |
529 if (tag < 0) | |
530 return -EIO; | |
531 if (tag == TAG_STREAMBLOCK) { | |
532 av_new_packet(pkt, len); | |
533 get_buffer(pb, pkt->data, pkt->size); | |
534 break; | |
535 } else { | |
536 url_fskip(pb, len); | |
537 } | |
538 } | |
539 return 0; | |
540 } | |
541 | |
542 static int swf_read_close(AVFormatContext *s) | |
543 { | |
544 return 0; | |
545 } | |
546 | |
547 static AVInputFormat swf_iformat = { | |
548 "swf", | |
549 "Flash format", | |
550 0, | |
551 swf_probe, | |
552 swf_read_header, | |
553 swf_read_packet, | |
554 swf_read_close, | |
555 }; | |
556 | |
557 static AVOutputFormat swf_oformat = { | |
558 "swf", | |
559 "Flash format", | |
560 "application/x-shockwave-flash", | |
561 "swf", | |
562 sizeof(SWFContext), | |
563 CODEC_ID_MP2, | |
564 CODEC_ID_MJPEG, | |
565 swf_write_header, | |
566 swf_write_packet, | |
567 swf_write_trailer, | |
568 }; | |
569 | |
570 int swf_init(void) | |
571 { | |
572 av_register_input_format(&swf_iformat); | |
573 av_register_output_format(&swf_oformat); | |
574 return 0; | |
575 } |