ffmpeg / libavformat / swfenc.c @ a2704c97
History | View | Annotate | Download (15.6 KB)
1 |
/*
|
---|---|
2 |
* Flash Compatible Streaming Format muxer
|
3 |
* Copyright (c) 2000 Fabrice Bellard
|
4 |
* Copyright (c) 2003 Tinic Uro
|
5 |
*
|
6 |
* This file is part of FFmpeg.
|
7 |
*
|
8 |
* FFmpeg is free software; you can redistribute it and/or
|
9 |
* modify it under the terms of the GNU Lesser General Public
|
10 |
* License as published by the Free Software Foundation; either
|
11 |
* version 2.1 of the License, or (at your option) any later version.
|
12 |
*
|
13 |
* FFmpeg is distributed in the hope that it will be useful,
|
14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
16 |
* Lesser General Public License for more details.
|
17 |
*
|
18 |
* You should have received a copy of the GNU Lesser General Public
|
19 |
* License along with FFmpeg; if not, write to the Free Software
|
20 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
21 |
*/
|
22 |
|
23 |
#include "libavcodec/put_bits.h" |
24 |
#include "avformat.h" |
25 |
#include "swf.h" |
26 |
|
27 |
static void put_swf_tag(AVFormatContext *s, int tag) |
28 |
{ |
29 |
SWFContext *swf = s->priv_data; |
30 |
AVIOContext *pb = s->pb; |
31 |
|
32 |
swf->tag_pos = avio_tell(pb); |
33 |
swf->tag = tag; |
34 |
/* reserve some room for the tag */
|
35 |
if (tag & TAG_LONG) {
|
36 |
avio_wl16(pb, 0);
|
37 |
avio_wl32(pb, 0);
|
38 |
} else {
|
39 |
avio_wl16(pb, 0);
|
40 |
} |
41 |
} |
42 |
|
43 |
static void put_swf_end_tag(AVFormatContext *s) |
44 |
{ |
45 |
SWFContext *swf = s->priv_data; |
46 |
AVIOContext *pb = s->pb; |
47 |
int64_t pos; |
48 |
int tag_len, tag;
|
49 |
|
50 |
pos = avio_tell(pb); |
51 |
tag_len = pos - swf->tag_pos - 2;
|
52 |
tag = swf->tag; |
53 |
avio_seek(pb, swf->tag_pos, SEEK_SET); |
54 |
if (tag & TAG_LONG) {
|
55 |
tag &= ~TAG_LONG; |
56 |
avio_wl16(pb, (tag << 6) | 0x3f); |
57 |
avio_wl32(pb, tag_len - 4);
|
58 |
} else {
|
59 |
assert(tag_len < 0x3f);
|
60 |
avio_wl16(pb, (tag << 6) | tag_len);
|
61 |
} |
62 |
avio_seek(pb, pos, SEEK_SET); |
63 |
} |
64 |
|
65 |
static inline void max_nbits(int *nbits_ptr, int val) |
66 |
{ |
67 |
int n;
|
68 |
|
69 |
if (val == 0) |
70 |
return;
|
71 |
val = abs(val); |
72 |
n = 1;
|
73 |
while (val != 0) { |
74 |
n++; |
75 |
val >>= 1;
|
76 |
} |
77 |
if (n > *nbits_ptr)
|
78 |
*nbits_ptr = n; |
79 |
} |
80 |
|
81 |
static void put_swf_rect(AVIOContext *pb, |
82 |
int xmin, int xmax, int ymin, int ymax) |
83 |
{ |
84 |
PutBitContext p; |
85 |
uint8_t buf[256];
|
86 |
int nbits, mask;
|
87 |
|
88 |
init_put_bits(&p, buf, sizeof(buf));
|
89 |
|
90 |
nbits = 0;
|
91 |
max_nbits(&nbits, xmin); |
92 |
max_nbits(&nbits, xmax); |
93 |
max_nbits(&nbits, ymin); |
94 |
max_nbits(&nbits, ymax); |
95 |
mask = (1 << nbits) - 1; |
96 |
|
97 |
/* rectangle info */
|
98 |
put_bits(&p, 5, nbits);
|
99 |
put_bits(&p, nbits, xmin & mask); |
100 |
put_bits(&p, nbits, xmax & mask); |
101 |
put_bits(&p, nbits, ymin & mask); |
102 |
put_bits(&p, nbits, ymax & mask); |
103 |
|
104 |
flush_put_bits(&p); |
105 |
avio_write(pb, buf, put_bits_ptr(&p) - p.buf); |
106 |
} |
107 |
|
108 |
static void put_swf_line_edge(PutBitContext *pb, int dx, int dy) |
109 |
{ |
110 |
int nbits, mask;
|
111 |
|
112 |
put_bits(pb, 1, 1); /* edge */ |
113 |
put_bits(pb, 1, 1); /* line select */ |
114 |
nbits = 2;
|
115 |
max_nbits(&nbits, dx); |
116 |
max_nbits(&nbits, dy); |
117 |
|
118 |
mask = (1 << nbits) - 1; |
119 |
put_bits(pb, 4, nbits - 2); /* 16 bits precision */ |
120 |
if (dx == 0) { |
121 |
put_bits(pb, 1, 0); |
122 |
put_bits(pb, 1, 1); |
123 |
put_bits(pb, nbits, dy & mask); |
124 |
} else if (dy == 0) { |
125 |
put_bits(pb, 1, 0); |
126 |
put_bits(pb, 1, 0); |
127 |
put_bits(pb, nbits, dx & mask); |
128 |
} else {
|
129 |
put_bits(pb, 1, 1); |
130 |
put_bits(pb, nbits, dx & mask); |
131 |
put_bits(pb, nbits, dy & mask); |
132 |
} |
133 |
} |
134 |
|
135 |
#define FRAC_BITS 16 |
136 |
|
137 |
static void put_swf_matrix(AVIOContext *pb, |
138 |
int a, int b, int c, int d, int tx, int ty) |
139 |
{ |
140 |
PutBitContext p; |
141 |
uint8_t buf[256];
|
142 |
int nbits;
|
143 |
|
144 |
init_put_bits(&p, buf, sizeof(buf));
|
145 |
|
146 |
put_bits(&p, 1, 1); /* a, d present */ |
147 |
nbits = 1;
|
148 |
max_nbits(&nbits, a); |
149 |
max_nbits(&nbits, d); |
150 |
put_bits(&p, 5, nbits); /* nb bits */ |
151 |
put_bits(&p, nbits, a); |
152 |
put_bits(&p, nbits, d); |
153 |
|
154 |
put_bits(&p, 1, 1); /* b, c present */ |
155 |
nbits = 1;
|
156 |
max_nbits(&nbits, c); |
157 |
max_nbits(&nbits, b); |
158 |
put_bits(&p, 5, nbits); /* nb bits */ |
159 |
put_bits(&p, nbits, c); |
160 |
put_bits(&p, nbits, b); |
161 |
|
162 |
nbits = 1;
|
163 |
max_nbits(&nbits, tx); |
164 |
max_nbits(&nbits, ty); |
165 |
put_bits(&p, 5, nbits); /* nb bits */ |
166 |
put_bits(&p, nbits, tx); |
167 |
put_bits(&p, nbits, ty); |
168 |
|
169 |
flush_put_bits(&p); |
170 |
avio_write(pb, buf, put_bits_ptr(&p) - p.buf); |
171 |
} |
172 |
|
173 |
static int swf_write_header(AVFormatContext *s) |
174 |
{ |
175 |
SWFContext *swf = s->priv_data; |
176 |
AVIOContext *pb = s->pb; |
177 |
PutBitContext p; |
178 |
uint8_t buf1[256];
|
179 |
int i, width, height, rate, rate_base;
|
180 |
int version;
|
181 |
|
182 |
swf->sound_samples = 0;
|
183 |
swf->swf_frame_number = 0;
|
184 |
swf->video_frame_number = 0;
|
185 |
|
186 |
for(i=0;i<s->nb_streams;i++) { |
187 |
AVCodecContext *enc = s->streams[i]->codec; |
188 |
if (enc->codec_type == AVMEDIA_TYPE_AUDIO) {
|
189 |
if (enc->codec_id == CODEC_ID_MP3) {
|
190 |
if (!enc->frame_size) {
|
191 |
av_log(s, AV_LOG_ERROR, "audio frame size not set\n");
|
192 |
return -1; |
193 |
} |
194 |
swf->audio_enc = enc; |
195 |
swf->audio_fifo= av_fifo_alloc(AUDIO_FIFO_SIZE); |
196 |
if (!swf->audio_fifo)
|
197 |
return AVERROR(ENOMEM);
|
198 |
} else {
|
199 |
av_log(s, AV_LOG_ERROR, "SWF muxer only supports MP3\n");
|
200 |
return -1; |
201 |
} |
202 |
} else {
|
203 |
if (enc->codec_id == CODEC_ID_VP6F ||
|
204 |
enc->codec_id == CODEC_ID_FLV1 || |
205 |
enc->codec_id == CODEC_ID_MJPEG) { |
206 |
swf->video_enc = enc; |
207 |
} else {
|
208 |
av_log(s, AV_LOG_ERROR, "SWF muxer only supports VP6, FLV1 and MJPEG\n");
|
209 |
return -1; |
210 |
} |
211 |
} |
212 |
} |
213 |
|
214 |
if (!swf->video_enc) {
|
215 |
/* currently, cannot work correctly if audio only */
|
216 |
width = 320;
|
217 |
height = 200;
|
218 |
rate = 10;
|
219 |
rate_base= 1;
|
220 |
} else {
|
221 |
width = swf->video_enc->width; |
222 |
height = swf->video_enc->height; |
223 |
rate = swf->video_enc->time_base.den; |
224 |
rate_base = swf->video_enc->time_base.num; |
225 |
} |
226 |
|
227 |
if (!swf->audio_enc)
|
228 |
swf->samples_per_frame = (44100. * rate_base) / rate; |
229 |
else
|
230 |
swf->samples_per_frame = (swf->audio_enc->sample_rate * rate_base) / rate; |
231 |
|
232 |
avio_write(pb, "FWS", 3); |
233 |
|
234 |
if (!strcmp("avm2", s->oformat->name)) |
235 |
version = 9;
|
236 |
else if (swf->video_enc && swf->video_enc->codec_id == CODEC_ID_VP6F) |
237 |
version = 8; /* version 8 and above support VP6 codec */ |
238 |
else if (swf->video_enc && swf->video_enc->codec_id == CODEC_ID_FLV1) |
239 |
version = 6; /* version 6 and above support FLV1 codec */ |
240 |
else
|
241 |
version = 4; /* version 4 for mpeg audio support */ |
242 |
avio_w8(pb, version); |
243 |
|
244 |
avio_wl32(pb, DUMMY_FILE_SIZE); /* dummy size
|
245 |
(will be patched if not streamed) */
|
246 |
|
247 |
put_swf_rect(pb, 0, width * 20, 0, height * 20); |
248 |
avio_wl16(pb, (rate * 256) / rate_base); /* frame rate */ |
249 |
swf->duration_pos = avio_tell(pb); |
250 |
avio_wl16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */
|
251 |
|
252 |
/* avm2/swf v9 (also v8?) files require a file attribute tag */
|
253 |
if (version == 9) { |
254 |
put_swf_tag(s, TAG_FILEATTRIBUTES); |
255 |
avio_wl32(pb, 1<<3); /* set ActionScript v3/AVM2 flag */ |
256 |
put_swf_end_tag(s); |
257 |
} |
258 |
|
259 |
/* define a shape with the jpeg inside */
|
260 |
if (swf->video_enc && swf->video_enc->codec_id == CODEC_ID_MJPEG) {
|
261 |
put_swf_tag(s, TAG_DEFINESHAPE); |
262 |
|
263 |
avio_wl16(pb, SHAPE_ID); /* ID of shape */
|
264 |
/* bounding rectangle */
|
265 |
put_swf_rect(pb, 0, width, 0, height); |
266 |
/* style info */
|
267 |
avio_w8(pb, 1); /* one fill style */ |
268 |
avio_w8(pb, 0x41); /* clipped bitmap fill */ |
269 |
avio_wl16(pb, BITMAP_ID); /* bitmap ID */
|
270 |
/* position of the bitmap */
|
271 |
put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, |
272 |
0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); |
273 |
avio_w8(pb, 0); /* no line style */ |
274 |
|
275 |
/* shape drawing */
|
276 |
init_put_bits(&p, buf1, sizeof(buf1));
|
277 |
put_bits(&p, 4, 1); /* one fill bit */ |
278 |
put_bits(&p, 4, 0); /* zero line bit */ |
279 |
|
280 |
put_bits(&p, 1, 0); /* not an edge */ |
281 |
put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0);
|
282 |
put_bits(&p, 5, 1); /* nbits */ |
283 |
put_bits(&p, 1, 0); /* X */ |
284 |
put_bits(&p, 1, 0); /* Y */ |
285 |
put_bits(&p, 1, 1); /* set fill style 1 */ |
286 |
|
287 |
/* draw the rectangle ! */
|
288 |
put_swf_line_edge(&p, width, 0);
|
289 |
put_swf_line_edge(&p, 0, height);
|
290 |
put_swf_line_edge(&p, -width, 0);
|
291 |
put_swf_line_edge(&p, 0, -height);
|
292 |
|
293 |
/* end of shape */
|
294 |
put_bits(&p, 1, 0); /* not an edge */ |
295 |
put_bits(&p, 5, 0); |
296 |
|
297 |
flush_put_bits(&p); |
298 |
avio_write(pb, buf1, put_bits_ptr(&p) - p.buf); |
299 |
|
300 |
put_swf_end_tag(s); |
301 |
} |
302 |
|
303 |
if (swf->audio_enc && swf->audio_enc->codec_id == CODEC_ID_MP3) {
|
304 |
int v = 0; |
305 |
|
306 |
/* start sound */
|
307 |
put_swf_tag(s, TAG_STREAMHEAD2); |
308 |
switch(swf->audio_enc->sample_rate) {
|
309 |
case 11025: v |= 1 << 2; break; |
310 |
case 22050: v |= 2 << 2; break; |
311 |
case 44100: v |= 3 << 2; break; |
312 |
default:
|
313 |
/* not supported */
|
314 |
av_log(s, AV_LOG_ERROR, "swf does not support that sample rate, choose from (44100, 22050, 11025).\n");
|
315 |
return -1; |
316 |
} |
317 |
v |= 0x02; /* 16 bit playback */ |
318 |
if (swf->audio_enc->channels == 2) |
319 |
v |= 0x01; /* stereo playback */ |
320 |
avio_w8(s->pb, v); |
321 |
v |= 0x20; /* mp3 compressed */ |
322 |
avio_w8(s->pb, v); |
323 |
avio_wl16(s->pb, swf->samples_per_frame); /* avg samples per frame */
|
324 |
avio_wl16(s->pb, 0);
|
325 |
|
326 |
put_swf_end_tag(s); |
327 |
} |
328 |
|
329 |
put_flush_packet(s->pb); |
330 |
return 0; |
331 |
} |
332 |
|
333 |
static int swf_write_video(AVFormatContext *s, |
334 |
AVCodecContext *enc, const uint8_t *buf, int size) |
335 |
{ |
336 |
SWFContext *swf = s->priv_data; |
337 |
AVIOContext *pb = s->pb; |
338 |
|
339 |
/* Flash Player limit */
|
340 |
if (swf->swf_frame_number == 16000) |
341 |
av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n");
|
342 |
|
343 |
if (enc->codec_id == CODEC_ID_VP6F ||
|
344 |
enc->codec_id == CODEC_ID_FLV1) { |
345 |
if (swf->video_frame_number == 0) { |
346 |
/* create a new video object */
|
347 |
put_swf_tag(s, TAG_VIDEOSTREAM); |
348 |
avio_wl16(pb, VIDEO_ID); |
349 |
swf->vframes_pos = avio_tell(pb); |
350 |
avio_wl16(pb, 15000); /* hard flash player limit */ |
351 |
avio_wl16(pb, enc->width); |
352 |
avio_wl16(pb, enc->height); |
353 |
avio_w8(pb, 0);
|
354 |
avio_w8(pb,ff_codec_get_tag(swf_codec_tags,enc->codec_id)); |
355 |
put_swf_end_tag(s); |
356 |
|
357 |
/* place the video object for the first time */
|
358 |
put_swf_tag(s, TAG_PLACEOBJECT2); |
359 |
avio_w8(pb, 0x36);
|
360 |
avio_wl16(pb, 1);
|
361 |
avio_wl16(pb, VIDEO_ID); |
362 |
put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); |
363 |
avio_wl16(pb, swf->video_frame_number); |
364 |
avio_write(pb, "video", 5); |
365 |
avio_w8(pb, 0x00);
|
366 |
put_swf_end_tag(s); |
367 |
} else {
|
368 |
/* mark the character for update */
|
369 |
put_swf_tag(s, TAG_PLACEOBJECT2); |
370 |
avio_w8(pb, 0x11);
|
371 |
avio_wl16(pb, 1);
|
372 |
avio_wl16(pb, swf->video_frame_number); |
373 |
put_swf_end_tag(s); |
374 |
} |
375 |
|
376 |
/* set video frame data */
|
377 |
put_swf_tag(s, TAG_VIDEOFRAME | TAG_LONG); |
378 |
avio_wl16(pb, VIDEO_ID); |
379 |
avio_wl16(pb, swf->video_frame_number++); |
380 |
avio_write(pb, buf, size); |
381 |
put_swf_end_tag(s); |
382 |
} else if (enc->codec_id == CODEC_ID_MJPEG) { |
383 |
if (swf->swf_frame_number > 0) { |
384 |
/* remove the shape */
|
385 |
put_swf_tag(s, TAG_REMOVEOBJECT); |
386 |
avio_wl16(pb, SHAPE_ID); /* shape ID */
|
387 |
avio_wl16(pb, 1); /* depth */ |
388 |
put_swf_end_tag(s); |
389 |
|
390 |
/* free the bitmap */
|
391 |
put_swf_tag(s, TAG_FREECHARACTER); |
392 |
avio_wl16(pb, BITMAP_ID); |
393 |
put_swf_end_tag(s); |
394 |
} |
395 |
|
396 |
put_swf_tag(s, TAG_JPEG2 | TAG_LONG); |
397 |
|
398 |
avio_wl16(pb, BITMAP_ID); /* ID of the image */
|
399 |
|
400 |
/* a dummy jpeg header seems to be required */
|
401 |
avio_wb32(pb, 0xffd8ffd9);
|
402 |
/* write the jpeg image */
|
403 |
avio_write(pb, buf, size); |
404 |
|
405 |
put_swf_end_tag(s); |
406 |
|
407 |
/* draw the shape */
|
408 |
|
409 |
put_swf_tag(s, TAG_PLACEOBJECT); |
410 |
avio_wl16(pb, SHAPE_ID); /* shape ID */
|
411 |
avio_wl16(pb, 1); /* depth */ |
412 |
put_swf_matrix(pb, 20 << FRAC_BITS, 0, 0, 20 << FRAC_BITS, 0, 0); |
413 |
put_swf_end_tag(s); |
414 |
} |
415 |
|
416 |
swf->swf_frame_number++; |
417 |
|
418 |
/* streaming sound always should be placed just before showframe tags */
|
419 |
if (swf->audio_enc && av_fifo_size(swf->audio_fifo)) {
|
420 |
int frame_size = av_fifo_size(swf->audio_fifo);
|
421 |
put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); |
422 |
avio_wl16(pb, swf->sound_samples); |
423 |
avio_wl16(pb, 0); // seek samples |
424 |
av_fifo_generic_read(swf->audio_fifo, pb, frame_size, &avio_write); |
425 |
put_swf_end_tag(s); |
426 |
|
427 |
/* update FIFO */
|
428 |
swf->sound_samples = 0;
|
429 |
} |
430 |
|
431 |
/* output the frame */
|
432 |
put_swf_tag(s, TAG_SHOWFRAME); |
433 |
put_swf_end_tag(s); |
434 |
|
435 |
put_flush_packet(s->pb); |
436 |
|
437 |
return 0; |
438 |
} |
439 |
|
440 |
static int swf_write_audio(AVFormatContext *s, |
441 |
AVCodecContext *enc, uint8_t *buf, int size)
|
442 |
{ |
443 |
SWFContext *swf = s->priv_data; |
444 |
|
445 |
/* Flash Player limit */
|
446 |
if (swf->swf_frame_number == 16000) |
447 |
av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n");
|
448 |
|
449 |
if (av_fifo_size(swf->audio_fifo) + size > AUDIO_FIFO_SIZE) {
|
450 |
av_log(s, AV_LOG_ERROR, "audio fifo too small to mux audio essence\n");
|
451 |
return -1; |
452 |
} |
453 |
|
454 |
av_fifo_generic_write(swf->audio_fifo, buf, size, NULL);
|
455 |
swf->sound_samples += enc->frame_size; |
456 |
|
457 |
/* if audio only stream make sure we add swf frames */
|
458 |
if (!swf->video_enc)
|
459 |
swf_write_video(s, enc, 0, 0); |
460 |
|
461 |
return 0; |
462 |
} |
463 |
|
464 |
static int swf_write_packet(AVFormatContext *s, AVPacket *pkt) |
465 |
{ |
466 |
AVCodecContext *codec = s->streams[pkt->stream_index]->codec; |
467 |
if (codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
468 |
return swf_write_audio(s, codec, pkt->data, pkt->size);
|
469 |
else
|
470 |
return swf_write_video(s, codec, pkt->data, pkt->size);
|
471 |
} |
472 |
|
473 |
static int swf_write_trailer(AVFormatContext *s) |
474 |
{ |
475 |
SWFContext *swf = s->priv_data; |
476 |
AVIOContext *pb = s->pb; |
477 |
AVCodecContext *enc, *video_enc; |
478 |
int file_size, i;
|
479 |
|
480 |
video_enc = NULL;
|
481 |
for(i=0;i<s->nb_streams;i++) { |
482 |
enc = s->streams[i]->codec; |
483 |
if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
|
484 |
video_enc = enc; |
485 |
else
|
486 |
av_fifo_free(swf->audio_fifo); |
487 |
} |
488 |
|
489 |
put_swf_tag(s, TAG_END); |
490 |
put_swf_end_tag(s); |
491 |
|
492 |
put_flush_packet(s->pb); |
493 |
|
494 |
/* patch file size and number of frames if not streamed */
|
495 |
if (!url_is_streamed(s->pb) && video_enc) {
|
496 |
file_size = avio_tell(pb); |
497 |
avio_seek(pb, 4, SEEK_SET);
|
498 |
avio_wl32(pb, file_size); |
499 |
avio_seek(pb, swf->duration_pos, SEEK_SET); |
500 |
avio_wl16(pb, swf->video_frame_number); |
501 |
avio_seek(pb, swf->vframes_pos, SEEK_SET); |
502 |
avio_wl16(pb, swf->video_frame_number); |
503 |
avio_seek(pb, file_size, SEEK_SET); |
504 |
} |
505 |
return 0; |
506 |
} |
507 |
|
508 |
#if CONFIG_SWF_MUXER
|
509 |
AVOutputFormat ff_swf_muxer = { |
510 |
"swf",
|
511 |
NULL_IF_CONFIG_SMALL("Flash format"),
|
512 |
"application/x-shockwave-flash",
|
513 |
"swf",
|
514 |
sizeof(SWFContext),
|
515 |
CODEC_ID_MP3, |
516 |
CODEC_ID_FLV1, |
517 |
swf_write_header, |
518 |
swf_write_packet, |
519 |
swf_write_trailer, |
520 |
}; |
521 |
#endif
|
522 |
#if CONFIG_AVM2_MUXER
|
523 |
AVOutputFormat ff_avm2_muxer = { |
524 |
"avm2",
|
525 |
NULL_IF_CONFIG_SMALL("Flash 9 (AVM2) format"),
|
526 |
"application/x-shockwave-flash",
|
527 |
NULL,
|
528 |
sizeof(SWFContext),
|
529 |
CODEC_ID_MP3, |
530 |
CODEC_ID_FLV1, |
531 |
swf_write_header, |
532 |
swf_write_packet, |
533 |
swf_write_trailer, |
534 |
}; |
535 |
#endif
|