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