Mercurial > libavformat.hg
comparison swf.c @ 0:05318cf2e886 libavformat
renamed libav to libavformat
author | bellard |
---|---|
date | Mon, 25 Nov 2002 19:07:40 +0000 |
parents | |
children | 39c4c4336486 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:05318cf2e886 |
---|---|
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; | |
110 UINT8 buf[256]; | |
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; | |
167 UINT8 buf[256]; | |
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; | |
196 UINT8 buf1[256]; | |
197 int i, width, height, rate; | |
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; | |
218 rate = 10 * FRAME_RATE_BASE; | |
219 } else { | |
220 width = video_enc->width; | |
221 height = video_enc->height; | |
222 rate = video_enc->frame_rate; | |
223 } | |
224 | |
225 put_tag(pb, "FWS"); | |
226 put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */ | |
227 put_le32(pb, DUMMY_FILE_SIZE); /* dummy size | |
228 (will be patched if not streamed) */ | |
229 | |
230 put_swf_rect(pb, 0, width, 0, height); | |
231 put_le16(pb, (rate * 256) / FRAME_RATE_BASE); /* frame rate */ | |
232 swf->duration_pos = url_ftell(pb); | |
233 put_le16(pb, (UINT16)(DUMMY_DURATION * (INT64)rate / FRAME_RATE_BASE)); /* frame count */ | |
234 | |
235 /* define a shape with the jpeg inside */ | |
236 | |
237 put_swf_tag(s, TAG_DEFINESHAPE); | |
238 | |
239 put_le16(pb, SHAPE_ID); /* ID of shape */ | |
240 /* bounding rectangle */ | |
241 put_swf_rect(pb, 0, width, 0, height); | |
242 /* style info */ | |
243 put_byte(pb, 1); /* one fill style */ | |
244 put_byte(pb, 0x41); /* clipped bitmap fill */ | |
245 put_le16(pb, BITMAP_ID); /* bitmap ID */ | |
246 /* position of the bitmap */ | |
247 put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, | |
248 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); | |
249 put_byte(pb, 0); /* no line style */ | |
250 | |
251 /* shape drawing */ | |
252 init_put_bits(&p, buf1, sizeof(buf1), NULL, NULL); | |
253 put_bits(&p, 4, 1); /* one fill bit */ | |
254 put_bits(&p, 4, 0); /* zero line bit */ | |
255 | |
256 put_bits(&p, 1, 0); /* not an edge */ | |
257 put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0); | |
258 put_bits(&p, 5, 1); /* nbits */ | |
259 put_bits(&p, 1, 0); /* X */ | |
260 put_bits(&p, 1, 0); /* Y */ | |
261 put_bits(&p, 1, 1); /* set fill style 1 */ | |
262 | |
263 /* draw the rectangle ! */ | |
264 put_swf_line_edge(&p, width, 0); | |
265 put_swf_line_edge(&p, 0, height); | |
266 put_swf_line_edge(&p, -width, 0); | |
267 put_swf_line_edge(&p, 0, -height); | |
268 | |
269 /* end of shape */ | |
270 put_bits(&p, 1, 0); /* not an edge */ | |
271 put_bits(&p, 5, 0); | |
272 | |
273 flush_put_bits(&p); | |
274 put_buffer(pb, buf1, pbBufPtr(&p) - p.buf); | |
275 | |
276 put_swf_end_tag(s); | |
277 | |
278 | |
279 if (audio_enc) { | |
280 int v; | |
281 | |
282 /* start sound */ | |
283 | |
284 v = 0; | |
285 switch(audio_enc->sample_rate) { | |
286 case 11025: | |
287 v |= 1 << 2; | |
288 break; | |
289 case 22050: | |
290 v |= 2 << 2; | |
291 break; | |
292 case 44100: | |
293 v |= 3 << 2; | |
294 break; | |
295 default: | |
296 /* not supported */ | |
297 av_free(swf); | |
298 return -1; | |
299 } | |
300 if (audio_enc->channels == 2) | |
301 v |= 1; | |
302 v |= 0x20; /* mp3 compressed */ | |
303 v |= 0x02; /* 16 bits */ | |
304 | |
305 put_swf_tag(s, TAG_STREAMHEAD); | |
306 put_byte(&s->pb, 0); | |
307 put_byte(&s->pb, v); | |
308 put_le16(&s->pb, (audio_enc->sample_rate * FRAME_RATE_BASE) / rate); /* avg samples per frame */ | |
309 | |
310 | |
311 put_swf_end_tag(s); | |
312 } | |
313 | |
314 put_flush_packet(&s->pb); | |
315 return 0; | |
316 } | |
317 | |
318 static int swf_write_video(AVFormatContext *s, | |
319 AVCodecContext *enc, UINT8 *buf, int size) | |
320 { | |
321 ByteIOContext *pb = &s->pb; | |
322 static int tag_id = 0; | |
323 | |
324 if (enc->frame_number > 1) { | |
325 /* remove the shape */ | |
326 put_swf_tag(s, TAG_REMOVEOBJECT); | |
327 put_le16(pb, SHAPE_ID); /* shape ID */ | |
328 put_le16(pb, 1); /* depth */ | |
329 put_swf_end_tag(s); | |
330 | |
331 /* free the bitmap */ | |
332 put_swf_tag(s, TAG_FREECHARACTER); | |
333 put_le16(pb, BITMAP_ID); | |
334 put_swf_end_tag(s); | |
335 } | |
336 | |
337 put_swf_tag(s, TAG_JPEG2 | TAG_LONG); | |
338 | |
339 put_le16(pb, tag_id); /* ID of the image */ | |
340 | |
341 /* a dummy jpeg header seems to be required */ | |
342 put_byte(pb, 0xff); | |
343 put_byte(pb, 0xd8); | |
344 put_byte(pb, 0xff); | |
345 put_byte(pb, 0xd9); | |
346 /* write the jpeg image */ | |
347 put_buffer(pb, buf, size); | |
348 | |
349 put_swf_end_tag(s); | |
350 | |
351 /* draw the shape */ | |
352 | |
353 put_swf_tag(s, TAG_PLACEOBJECT); | |
354 put_le16(pb, SHAPE_ID); /* shape ID */ | |
355 put_le16(pb, 1); /* depth */ | |
356 put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); | |
357 put_swf_end_tag(s); | |
358 | |
359 /* output the frame */ | |
360 put_swf_tag(s, TAG_SHOWFRAME); | |
361 put_swf_end_tag(s); | |
362 | |
363 put_flush_packet(&s->pb); | |
364 return 0; | |
365 } | |
366 | |
367 static int swf_write_audio(AVFormatContext *s, UINT8 *buf, int size) | |
368 { | |
369 ByteIOContext *pb = &s->pb; | |
370 | |
371 put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); | |
372 | |
373 put_buffer(pb, buf, size); | |
374 | |
375 put_swf_end_tag(s); | |
376 put_flush_packet(&s->pb); | |
377 return 0; | |
378 } | |
379 | |
380 static int swf_write_packet(AVFormatContext *s, int stream_index, | |
381 UINT8 *buf, int size, int force_pts) | |
382 { | |
383 AVCodecContext *codec = &s->streams[stream_index]->codec; | |
384 if (codec->codec_type == CODEC_TYPE_AUDIO) | |
385 return swf_write_audio(s, buf, size); | |
386 else | |
387 return swf_write_video(s, codec, buf, size); | |
388 } | |
389 | |
390 static int swf_write_trailer(AVFormatContext *s) | |
391 { | |
392 SWFContext *swf = s->priv_data; | |
393 ByteIOContext *pb = &s->pb; | |
394 AVCodecContext *enc, *video_enc; | |
395 int file_size, i; | |
396 | |
397 video_enc = NULL; | |
398 for(i=0;i<s->nb_streams;i++) { | |
399 enc = &s->streams[i]->codec; | |
400 if (enc->codec_type == CODEC_TYPE_VIDEO) | |
401 video_enc = enc; | |
402 } | |
403 | |
404 put_swf_tag(s, TAG_END); | |
405 put_swf_end_tag(s); | |
406 | |
407 put_flush_packet(&s->pb); | |
408 | |
409 /* patch file size and number of frames if not streamed */ | |
410 if (!url_is_streamed(&s->pb) && video_enc) { | |
411 file_size = url_ftell(pb); | |
412 url_fseek(pb, 4, SEEK_SET); | |
413 put_le32(pb, file_size); | |
414 url_fseek(pb, swf->duration_pos, SEEK_SET); | |
415 put_le16(pb, video_enc->frame_number); | |
416 } | |
417 return 0; | |
418 } | |
419 | |
420 /***********************************/ | |
421 /* just to extract MP3 from swf */ | |
422 | |
423 static int get_swf_tag(ByteIOContext *pb, int *len_ptr) | |
424 { | |
425 int tag, len; | |
426 | |
427 if (url_feof(pb)) | |
428 return -1; | |
429 | |
430 tag = get_le16(pb); | |
431 len = tag & 0x3f; | |
432 tag = tag >> 6; | |
433 if (len == 0x3f) { | |
434 len = get_le32(pb); | |
435 } | |
436 *len_ptr = len; | |
437 return tag; | |
438 } | |
439 | |
440 | |
441 static int swf_probe(AVProbeData *p) | |
442 { | |
443 /* check file header */ | |
444 if (p->buf_size <= 16) | |
445 return 0; | |
446 if (p->buf[0] == 'F' && p->buf[1] == 'W' && | |
447 p->buf[2] == 'S') | |
448 return AVPROBE_SCORE_MAX; | |
449 else | |
450 return 0; | |
451 } | |
452 | |
453 static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
454 { | |
455 ByteIOContext *pb = &s->pb; | |
456 int nbits, len, frame_rate, tag, v; | |
457 AVStream *st; | |
458 | |
459 if ((get_be32(pb) & 0xffffff00) != MKBETAG('F', 'W', 'S', 0)) | |
460 return -EIO; | |
461 get_le32(pb); | |
462 /* skip rectangle size */ | |
463 nbits = get_byte(pb) >> 3; | |
464 len = (4 * nbits - 3 + 7) / 8; | |
465 url_fskip(pb, len); | |
466 frame_rate = get_le16(pb); | |
467 get_le16(pb); /* frame count */ | |
468 | |
469 for(;;) { | |
470 tag = get_swf_tag(pb, &len); | |
471 if (tag < 0) { | |
472 fprintf(stderr, "No streaming found in SWF\n"); | |
473 return -EIO; | |
474 } | |
475 if (tag == TAG_STREAMHEAD) { | |
476 /* streaming found */ | |
477 get_byte(pb); | |
478 v = get_byte(pb); | |
479 get_le16(pb); | |
480 /* if mp3 streaming found, OK */ | |
481 if ((v & 0x20) != 0) { | |
482 st = av_mallocz(sizeof(AVStream)); | |
483 if (!st) | |
484 return -ENOMEM; | |
485 if (v & 0x01) | |
486 st->codec.channels = 2; | |
487 else | |
488 st->codec.channels = 1; | |
489 s->nb_streams = 1; | |
490 s->streams[0] = st; | |
491 | |
492 switch((v>> 2) & 0x03) { | |
493 case 1: | |
494 st->codec.sample_rate = 11025; | |
495 break; | |
496 case 2: | |
497 st->codec.sample_rate = 22050; | |
498 break; | |
499 case 3: | |
500 st->codec.sample_rate = 44100; | |
501 break; | |
502 default: | |
503 av_free(st); | |
504 return -EIO; | |
505 } | |
506 st->codec.codec_type = CODEC_TYPE_AUDIO; | |
507 st->codec.codec_id = CODEC_ID_MP2; | |
508 break; | |
509 } | |
510 } else { | |
511 url_fskip(pb, len); | |
512 } | |
513 } | |
514 | |
515 return 0; | |
516 } | |
517 | |
518 static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) | |
519 { | |
520 ByteIOContext *pb = &s->pb; | |
521 int tag, len; | |
522 | |
523 for(;;) { | |
524 tag = get_swf_tag(pb, &len); | |
525 if (tag < 0) | |
526 return -EIO; | |
527 if (tag == TAG_STREAMBLOCK) { | |
528 av_new_packet(pkt, len); | |
529 get_buffer(pb, pkt->data, pkt->size); | |
530 break; | |
531 } else { | |
532 url_fskip(pb, len); | |
533 } | |
534 } | |
535 return 0; | |
536 } | |
537 | |
538 static int swf_read_close(AVFormatContext *s) | |
539 { | |
540 return 0; | |
541 } | |
542 | |
543 static AVInputFormat swf_iformat = { | |
544 "swf", | |
545 "Flash format", | |
546 0, | |
547 swf_probe, | |
548 swf_read_header, | |
549 swf_read_packet, | |
550 swf_read_close, | |
551 }; | |
552 | |
553 static AVOutputFormat swf_oformat = { | |
554 "swf", | |
555 "Flash format", | |
556 "application/x-shockwave-flash", | |
557 "swf", | |
558 sizeof(SWFContext), | |
559 CODEC_ID_MP2, | |
560 CODEC_ID_MJPEG, | |
561 swf_write_header, | |
562 swf_write_packet, | |
563 swf_write_trailer, | |
564 }; | |
565 | |
566 int swf_init(void) | |
567 { | |
568 av_register_input_format(&swf_iformat); | |
569 av_register_output_format(&swf_oformat); | |
570 return 0; | |
571 } |