Mercurial > libavformat.hg
annotate swf.c @ 1960:c0289552590f libavformat
Change the vhook code to send real timestamps to the filters instead of the
current time of day, which is useless, and which the filters could just as
easily query for themselves.
patch by Bobby Bingham, uhmmmm gmail com
author | diego |
---|---|
date | Thu, 29 Mar 2007 05:24:35 +0000 |
parents | 04f9a3ae30af |
children | 1a3c9056982a |
rev | line source |
---|---|
0 | 1 /* |
2 * Flash Compatible Streaming Format | |
3 * Copyright (c) 2000 Fabrice Bellard. | |
359 | 4 * Copyright (c) 2003 Tinic Uro. |
0 | 5 * |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
6 * This file is part of FFmpeg. |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
7 * |
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
8 * FFmpeg is free software; you can redistribute it and/or |
0 | 9 * modify it under the terms of the GNU Lesser General Public |
10 * License as published by the Free Software Foundation; either | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
11 * version 2.1 of the License, or (at your option) any later version. |
0 | 12 * |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
13 * FFmpeg is distributed in the hope that it will be useful, |
0 | 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 | |
1358
0899bfe4105c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
1305
diff
changeset
|
19 * License along with FFmpeg; if not, write to the Free Software |
896
edbe5c3717f9
Update licensing information: The FSF changed postal address.
diego
parents:
887
diff
changeset
|
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
0 | 21 */ |
22 #include "avformat.h" | |
628 | 23 #include "bitstream.h" |
1305 | 24 #include "riff.h" /* for CodecTag */ |
0 | 25 |
26 /* should have a generic way to indicate probable size */ | |
27 #define DUMMY_FILE_SIZE (100 * 1024 * 1024) | |
28 #define DUMMY_DURATION 600 /* in seconds */ | |
29 | |
30 #define TAG_END 0 | |
31 #define TAG_SHOWFRAME 1 | |
32 #define TAG_DEFINESHAPE 2 | |
33 #define TAG_FREECHARACTER 3 | |
34 #define TAG_PLACEOBJECT 4 | |
35 #define TAG_REMOVEOBJECT 5 | |
807 | 36 #define TAG_STREAMHEAD 18 |
0 | 37 #define TAG_STREAMBLOCK 19 |
38 #define TAG_JPEG2 21 | |
359 | 39 #define TAG_PLACEOBJECT2 26 |
40 #define TAG_STREAMHEAD2 45 | |
887 | 41 #define TAG_VIDEOSTREAM 60 |
359 | 42 #define TAG_VIDEOFRAME 61 |
0 | 43 |
44 #define TAG_LONG 0x100 | |
45 | |
46 /* flags for shape definition */ | |
47 #define FLAG_MOVETO 0x01 | |
48 #define FLAG_SETFILL0 0x02 | |
49 #define FLAG_SETFILL1 0x04 | |
50 | |
359 | 51 #define AUDIO_FIFO_SIZE 65536 |
52 | |
0 | 53 /* character id used */ |
54 #define BITMAP_ID 0 | |
359 | 55 #define VIDEO_ID 0 |
0 | 56 #define SHAPE_ID 1 |
57 | |
516 | 58 #undef NDEBUG |
59 #include <assert.h> | |
359 | 60 |
0 | 61 typedef struct { |
1638 | 62 int audio_stream_index; |
0 | 63 offset_t duration_pos; |
64 offset_t tag_pos; | |
885 | 65 |
359 | 66 int samples_per_frame; |
67 int sound_samples; | |
68 int swf_frame_number; | |
69 int video_frame_number; | |
1889 | 70 int frame_rate; |
0 | 71 int tag; |
359 | 72 |
1854 | 73 uint8_t audio_fifo[AUDIO_FIFO_SIZE]; |
359 | 74 int audio_in_pos; |
75 | |
76 int video_type; | |
77 int audio_type; | |
0 | 78 } SWFContext; |
79 | |
1677
2a85c82b8538
add codec_id <-> codec_tag tables to AVIn/OutputFormat
michael
parents:
1673
diff
changeset
|
80 static const AVCodecTag swf_codec_tags[] = { |
1305 | 81 {CODEC_ID_FLV1, 0x02}, |
82 {CODEC_ID_VP6F, 0x04}, | |
83 {0, 0}, | |
84 }; | |
85 | |
1833 | 86 static const AVCodecTag swf_audio_codec_tags[] = { |
87 {CODEC_ID_PCM_S16LE, 0x00}, | |
88 {CODEC_ID_ADPCM_SWF, 0x01}, | |
89 {CODEC_ID_MP3, 0x02}, | |
90 {CODEC_ID_PCM_S16LE, 0x03}, | |
91 //{CODEC_ID_NELLYMOSER, 0x06}, | |
92 {0, 0}, | |
93 }; | |
94 | |
858
66cc656ea404
Replace CONFIG_ENCODERS/CONFIG_DECODERS with CONFIG_MUXERS/CONFIG_DEMUXERS
diego
parents:
820
diff
changeset
|
95 #ifdef CONFIG_MUXERS |
0 | 96 static void put_swf_tag(AVFormatContext *s, int tag) |
97 { | |
98 SWFContext *swf = s->priv_data; | |
99 ByteIOContext *pb = &s->pb; | |
100 | |
101 swf->tag_pos = url_ftell(pb); | |
102 swf->tag = tag; | |
103 /* reserve some room for the tag */ | |
104 if (tag & TAG_LONG) { | |
105 put_le16(pb, 0); | |
106 put_le32(pb, 0); | |
107 } else { | |
108 put_le16(pb, 0); | |
109 } | |
110 } | |
111 | |
112 static void put_swf_end_tag(AVFormatContext *s) | |
113 { | |
114 SWFContext *swf = s->priv_data; | |
115 ByteIOContext *pb = &s->pb; | |
116 offset_t pos; | |
117 int tag_len, tag; | |
118 | |
119 pos = url_ftell(pb); | |
120 tag_len = pos - swf->tag_pos - 2; | |
121 tag = swf->tag; | |
122 url_fseek(pb, swf->tag_pos, SEEK_SET); | |
123 if (tag & TAG_LONG) { | |
124 tag &= ~TAG_LONG; | |
125 put_le16(pb, (tag << 6) | 0x3f); | |
126 put_le32(pb, tag_len - 4); | |
127 } else { | |
128 assert(tag_len < 0x3f); | |
129 put_le16(pb, (tag << 6) | tag_len); | |
130 } | |
131 url_fseek(pb, pos, SEEK_SET); | |
132 } | |
133 | |
134 static inline void max_nbits(int *nbits_ptr, int val) | |
135 { | |
136 int n; | |
137 | |
138 if (val == 0) | |
139 return; | |
140 val = abs(val); | |
141 n = 1; | |
142 while (val != 0) { | |
143 n++; | |
144 val >>= 1; | |
145 } | |
146 if (n > *nbits_ptr) | |
147 *nbits_ptr = n; | |
148 } | |
149 | |
885 | 150 static void put_swf_rect(ByteIOContext *pb, |
0 | 151 int xmin, int xmax, int ymin, int ymax) |
152 { | |
153 PutBitContext p; | |
65 | 154 uint8_t buf[256]; |
0 | 155 int nbits, mask; |
156 | |
276 | 157 init_put_bits(&p, buf, sizeof(buf)); |
885 | 158 |
0 | 159 nbits = 0; |
160 max_nbits(&nbits, xmin); | |
161 max_nbits(&nbits, xmax); | |
162 max_nbits(&nbits, ymin); | |
163 max_nbits(&nbits, ymax); | |
164 mask = (1 << nbits) - 1; | |
165 | |
166 /* rectangle info */ | |
167 put_bits(&p, 5, nbits); | |
168 put_bits(&p, nbits, xmin & mask); | |
169 put_bits(&p, nbits, xmax & mask); | |
170 put_bits(&p, nbits, ymin & mask); | |
171 put_bits(&p, nbits, ymax & mask); | |
885 | 172 |
0 | 173 flush_put_bits(&p); |
174 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
175 } | |
176 | |
177 static void put_swf_line_edge(PutBitContext *pb, int dx, int dy) | |
178 { | |
179 int nbits, mask; | |
180 | |
181 put_bits(pb, 1, 1); /* edge */ | |
182 put_bits(pb, 1, 1); /* line select */ | |
183 nbits = 2; | |
184 max_nbits(&nbits, dx); | |
185 max_nbits(&nbits, dy); | |
186 | |
187 mask = (1 << nbits) - 1; | |
188 put_bits(pb, 4, nbits - 2); /* 16 bits precision */ | |
189 if (dx == 0) { | |
885 | 190 put_bits(pb, 1, 0); |
191 put_bits(pb, 1, 1); | |
0 | 192 put_bits(pb, nbits, dy & mask); |
193 } else if (dy == 0) { | |
885 | 194 put_bits(pb, 1, 0); |
195 put_bits(pb, 1, 0); | |
0 | 196 put_bits(pb, nbits, dx & mask); |
197 } else { | |
885 | 198 put_bits(pb, 1, 1); |
0 | 199 put_bits(pb, nbits, dx & mask); |
200 put_bits(pb, nbits, dy & mask); | |
201 } | |
202 } | |
203 | |
204 #define FRAC_BITS 16 | |
205 | |
359 | 206 /* put matrix */ |
0 | 207 static void put_swf_matrix(ByteIOContext *pb, |
208 int a, int b, int c, int d, int tx, int ty) | |
209 { | |
210 PutBitContext p; | |
65 | 211 uint8_t buf[256]; |
359 | 212 int nbits; |
0 | 213 |
276 | 214 init_put_bits(&p, buf, sizeof(buf)); |
885 | 215 |
0 | 216 put_bits(&p, 1, 1); /* a, d present */ |
359 | 217 nbits = 1; |
218 max_nbits(&nbits, a); | |
219 max_nbits(&nbits, d); | |
220 put_bits(&p, 5, nbits); /* nb bits */ | |
221 put_bits(&p, nbits, a); | |
222 put_bits(&p, nbits, d); | |
885 | 223 |
0 | 224 put_bits(&p, 1, 1); /* b, c present */ |
359 | 225 nbits = 1; |
226 max_nbits(&nbits, c); | |
227 max_nbits(&nbits, b); | |
228 put_bits(&p, 5, nbits); /* nb bits */ | |
229 put_bits(&p, nbits, c); | |
230 put_bits(&p, nbits, b); | |
0 | 231 |
359 | 232 nbits = 1; |
233 max_nbits(&nbits, tx); | |
234 max_nbits(&nbits, ty); | |
235 put_bits(&p, 5, nbits); /* nb bits */ | |
236 put_bits(&p, nbits, tx); | |
237 put_bits(&p, nbits, ty); | |
0 | 238 |
239 flush_put_bits(&p); | |
240 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
241 } | |
242 | |
359 | 243 /* */ |
0 | 244 static int swf_write_header(AVFormatContext *s) |
245 { | |
1623 | 246 SWFContext *swf = s->priv_data; |
0 | 247 ByteIOContext *pb = &s->pb; |
248 AVCodecContext *enc, *audio_enc, *video_enc; | |
249 PutBitContext p; | |
65 | 250 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
|
251 int i, width, height, rate, rate_base; |
0 | 252 |
359 | 253 swf->audio_in_pos = 0; |
254 swf->sound_samples = 0; | |
255 swf->swf_frame_number = 0; | |
256 swf->video_frame_number = 0; | |
257 | |
0 | 258 video_enc = NULL; |
259 audio_enc = NULL; | |
260 for(i=0;i<s->nb_streams;i++) { | |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
807
diff
changeset
|
261 enc = s->streams[i]->codec; |
1854 | 262 if (enc->codec_type == CODEC_TYPE_AUDIO) { |
263 if (enc->codec_id == CODEC_ID_MP3) { | |
264 if (!enc->frame_size) { | |
265 av_log(s, AV_LOG_ERROR, "audio frame size not set\n"); | |
266 return -1; | |
267 } | |
268 audio_enc = enc; | |
269 } else { | |
1865 | 270 av_log(s, AV_LOG_ERROR, "SWF muxer only supports MP3\n"); |
1854 | 271 return -1; |
272 } | |
273 } else { | |
1305 | 274 if ( enc->codec_id == CODEC_ID_VP6F || |
275 enc->codec_id == CODEC_ID_FLV1 || | |
276 enc->codec_id == CODEC_ID_MJPEG ) { | |
359 | 277 video_enc = enc; |
278 } else { | |
1865 | 279 av_log(s, AV_LOG_ERROR, "SWF muxer only supports VP6, FLV1 and MJPEG\n"); |
359 | 280 return -1; |
281 } | |
282 } | |
0 | 283 } |
284 | |
285 if (!video_enc) { | |
286 /* currenty, cannot work correctly if audio only */ | |
359 | 287 swf->video_type = 0; |
0 | 288 width = 320; |
289 height = 200; | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
290 rate = 10; |
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
291 rate_base= 1; |
0 | 292 } else { |
359 | 293 swf->video_type = video_enc->codec_id; |
0 | 294 width = video_enc->width; |
295 height = video_enc->height; | |
743 | 296 rate = video_enc->time_base.den; |
297 rate_base = video_enc->time_base.num; | |
0 | 298 } |
299 | |
359 | 300 if (!audio_enc ) { |
301 swf->audio_type = 0; | |
302 swf->samples_per_frame = ( 44100. * rate_base ) / rate; | |
303 } else { | |
304 swf->audio_type = audio_enc->codec_id; | |
305 swf->samples_per_frame = ( ( audio_enc->sample_rate ) * rate_base ) / rate; | |
306 } | |
307 | |
0 | 308 put_tag(pb, "FWS"); |
1305 | 309 if ( video_enc && video_enc->codec_id == CODEC_ID_VP6F ) { |
310 put_byte(pb, 8); /* version (version 8 and above support VP6 codec) */ | |
311 } else if ( video_enc && video_enc->codec_id == CODEC_ID_FLV1 ) { | |
359 | 312 put_byte(pb, 6); /* version (version 6 and above support FLV1 codec) */ |
313 } else { | |
314 put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */ | |
315 } | |
885 | 316 put_le32(pb, DUMMY_FILE_SIZE); /* dummy size |
317 (will be patched if not streamed) */ | |
0 | 318 |
359 | 319 put_swf_rect(pb, 0, width * 20, 0, height * 20); |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
320 put_le16(pb, (rate * 256) / rate_base); /* frame rate */ |
0 | 321 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
|
322 put_le16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */ |
885 | 323 |
0 | 324 /* define a shape with the jpeg inside */ |
1305 | 325 if ( video_enc && (video_enc->codec_id == CODEC_ID_VP6F || |
326 video_enc->codec_id == CODEC_ID_FLV1 )) { | |
359 | 327 } else if ( video_enc && video_enc->codec_id == CODEC_ID_MJPEG ) { |
328 put_swf_tag(s, TAG_DEFINESHAPE); | |
0 | 329 |
359 | 330 put_le16(pb, SHAPE_ID); /* ID of shape */ |
331 /* bounding rectangle */ | |
332 put_swf_rect(pb, 0, width, 0, height); | |
333 /* style info */ | |
334 put_byte(pb, 1); /* one fill style */ | |
335 put_byte(pb, 0x41); /* clipped bitmap fill */ | |
336 put_le16(pb, BITMAP_ID); /* bitmap ID */ | |
337 /* position of the bitmap */ | |
885 | 338 put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, |
359 | 339 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); |
340 put_byte(pb, 0); /* no line style */ | |
885 | 341 |
359 | 342 /* shape drawing */ |
343 init_put_bits(&p, buf1, sizeof(buf1)); | |
344 put_bits(&p, 4, 1); /* one fill bit */ | |
345 put_bits(&p, 4, 0); /* zero line bit */ | |
885 | 346 |
359 | 347 put_bits(&p, 1, 0); /* not an edge */ |
348 put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0); | |
349 put_bits(&p, 5, 1); /* nbits */ | |
350 put_bits(&p, 1, 0); /* X */ | |
351 put_bits(&p, 1, 0); /* Y */ | |
352 put_bits(&p, 1, 1); /* set fill style 1 */ | |
885 | 353 |
359 | 354 /* draw the rectangle ! */ |
355 put_swf_line_edge(&p, width, 0); | |
356 put_swf_line_edge(&p, 0, height); | |
357 put_swf_line_edge(&p, -width, 0); | |
358 put_swf_line_edge(&p, 0, -height); | |
885 | 359 |
359 | 360 /* end of shape */ |
361 put_bits(&p, 1, 0); /* not an edge */ | |
362 put_bits(&p, 5, 0); | |
0 | 363 |
359 | 364 flush_put_bits(&p); |
365 put_buffer(pb, buf1, pbBufPtr(&p) - p.buf); | |
0 | 366 |
359 | 367 put_swf_end_tag(s); |
368 } | |
885 | 369 |
359 | 370 if (audio_enc && audio_enc->codec_id == CODEC_ID_MP3 ) { |
0 | 371 int v; |
372 | |
373 /* start sound */ | |
359 | 374 put_swf_tag(s, TAG_STREAMHEAD2); |
0 | 375 |
376 v = 0; | |
377 switch(audio_enc->sample_rate) { | |
378 case 11025: | |
379 v |= 1 << 2; | |
380 break; | |
381 case 22050: | |
382 v |= 2 << 2; | |
383 break; | |
384 case 44100: | |
385 v |= 3 << 2; | |
386 break; | |
387 default: | |
388 /* not supported */ | |
1519
f57c7614ff98
print error when unsupported sample rate is used with swf
bcoudurier
parents:
1417
diff
changeset
|
389 av_log(s, AV_LOG_ERROR, "swf doesnt support that sample rate, choose from (44100, 22050, 11025)\n"); |
0 | 390 return -1; |
391 } | |
359 | 392 v |= 0x02; /* 16 bit playback */ |
0 | 393 if (audio_enc->channels == 2) |
359 | 394 v |= 0x01; /* stereo playback */ |
395 put_byte(&s->pb, v); | |
0 | 396 v |= 0x20; /* mp3 compressed */ |
397 put_byte(&s->pb, v); | |
359 | 398 put_le16(&s->pb, swf->samples_per_frame); /* avg samples per frame */ |
399 put_le16(&s->pb, 0); | |
885 | 400 |
0 | 401 put_swf_end_tag(s); |
402 } | |
403 | |
404 put_flush_packet(&s->pb); | |
405 return 0; | |
406 } | |
407 | |
885 | 408 static int swf_write_video(AVFormatContext *s, |
241 | 409 AVCodecContext *enc, const uint8_t *buf, int size) |
0 | 410 { |
359 | 411 SWFContext *swf = s->priv_data; |
0 | 412 ByteIOContext *pb = &s->pb; |
885 | 413 |
359 | 414 /* Flash Player limit */ |
360 | 415 if ( swf->swf_frame_number == 16000 ) { |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
416 av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n"); |
359 | 417 } |
0 | 418 |
1305 | 419 if ( swf->video_type == CODEC_ID_VP6F || |
420 swf->video_type == CODEC_ID_FLV1 ) { | |
359 | 421 if ( swf->video_frame_number == 0 ) { |
422 /* create a new video object */ | |
423 put_swf_tag(s, TAG_VIDEOSTREAM); | |
424 put_le16(pb, VIDEO_ID); | |
425 put_le16(pb, 15000 ); /* hard flash player limit */ | |
426 put_le16(pb, enc->width); | |
427 put_le16(pb, enc->height); | |
428 put_byte(pb, 0); | |
1305 | 429 put_byte(pb,codec_get_tag(swf_codec_tags,swf->video_type)); |
359 | 430 put_swf_end_tag(s); |
885 | 431 |
359 | 432 /* place the video object for the first time */ |
433 put_swf_tag(s, TAG_PLACEOBJECT2); | |
434 put_byte(pb, 0x36); | |
435 put_le16(pb, 1); | |
436 put_le16(pb, VIDEO_ID); | |
437 put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); | |
438 put_le16(pb, swf->video_frame_number ); | |
439 put_byte(pb, 'v'); | |
440 put_byte(pb, 'i'); | |
441 put_byte(pb, 'd'); | |
442 put_byte(pb, 'e'); | |
443 put_byte(pb, 'o'); | |
444 put_byte(pb, 0x00); | |
445 put_swf_end_tag(s); | |
446 } else { | |
447 /* mark the character for update */ | |
448 put_swf_tag(s, TAG_PLACEOBJECT2); | |
449 put_byte(pb, 0x11); | |
450 put_le16(pb, 1); | |
451 put_le16(pb, swf->video_frame_number ); | |
452 put_swf_end_tag(s); | |
453 } | |
885 | 454 |
359 | 455 /* set video frame data */ |
456 put_swf_tag(s, TAG_VIDEOFRAME | TAG_LONG); | |
885 | 457 put_le16(pb, VIDEO_ID); |
359 | 458 put_le16(pb, swf->video_frame_number++ ); |
516 | 459 put_buffer(pb, buf, size); |
359 | 460 put_swf_end_tag(s); |
461 } else if ( swf->video_type == CODEC_ID_MJPEG ) { | |
462 if (swf->swf_frame_number > 0) { | |
463 /* remove the shape */ | |
464 put_swf_tag(s, TAG_REMOVEOBJECT); | |
465 put_le16(pb, SHAPE_ID); /* shape ID */ | |
466 put_le16(pb, 1); /* depth */ | |
467 put_swf_end_tag(s); | |
885 | 468 |
359 | 469 /* free the bitmap */ |
470 put_swf_tag(s, TAG_FREECHARACTER); | |
471 put_le16(pb, BITMAP_ID); | |
472 put_swf_end_tag(s); | |
473 } | |
885 | 474 |
359 | 475 put_swf_tag(s, TAG_JPEG2 | TAG_LONG); |
885 | 476 |
359 | 477 put_le16(pb, BITMAP_ID); /* ID of the image */ |
885 | 478 |
359 | 479 /* a dummy jpeg header seems to be required */ |
885 | 480 put_byte(pb, 0xff); |
359 | 481 put_byte(pb, 0xd8); |
482 put_byte(pb, 0xff); | |
483 put_byte(pb, 0xd9); | |
484 /* write the jpeg image */ | |
516 | 485 put_buffer(pb, buf, size); |
885 | 486 |
359 | 487 put_swf_end_tag(s); |
885 | 488 |
359 | 489 /* draw the shape */ |
885 | 490 |
359 | 491 put_swf_tag(s, TAG_PLACEOBJECT); |
492 put_le16(pb, SHAPE_ID); /* shape ID */ | |
493 put_le16(pb, 1); /* depth */ | |
494 put_swf_matrix(pb, 20 << FRAC_BITS, 0, 0, 20 << FRAC_BITS, 0, 0); | |
495 put_swf_end_tag(s); | |
496 } else { | |
497 /* invalid codec */ | |
498 } | |
885 | 499 |
359 | 500 swf->swf_frame_number ++; |
0 | 501 |
359 | 502 /* streaming sound always should be placed just before showframe tags */ |
1854 | 503 if (swf->audio_type && swf->audio_in_pos) { |
359 | 504 put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); |
1854 | 505 put_le16(pb, swf->sound_samples); |
506 put_le16(pb, 0); // seek samples | |
507 put_buffer(pb, swf->audio_fifo, swf->audio_in_pos); | |
359 | 508 put_swf_end_tag(s); |
885 | 509 |
359 | 510 /* update FIFO */ |
1854 | 511 swf->sound_samples = 0; |
512 swf->audio_in_pos = 0; | |
359 | 513 } |
514 | |
0 | 515 /* output the frame */ |
516 put_swf_tag(s, TAG_SHOWFRAME); | |
517 put_swf_end_tag(s); | |
885 | 518 |
0 | 519 put_flush_packet(&s->pb); |
885 | 520 |
0 | 521 return 0; |
522 } | |
523 | |
885 | 524 static int swf_write_audio(AVFormatContext *s, |
359 | 525 AVCodecContext *enc, const uint8_t *buf, int size) |
0 | 526 { |
359 | 527 SWFContext *swf = s->priv_data; |
528 | |
529 /* Flash Player limit */ | |
360 | 530 if ( swf->swf_frame_number == 16000 ) { |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
531 av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n"); |
359 | 532 } |
0 | 533 |
1854 | 534 if (swf->audio_in_pos + size >= AUDIO_FIFO_SIZE) { |
535 av_log(s, AV_LOG_ERROR, "audio fifo too small to mux audio essence\n"); | |
536 return -1; | |
359 | 537 } |
0 | 538 |
1854 | 539 memcpy(swf->audio_fifo + swf->audio_in_pos, buf, size); |
540 swf->audio_in_pos += size; | |
541 swf->sound_samples += enc->frame_size; | |
542 | |
359 | 543 /* if audio only stream make sure we add swf frames */ |
544 if ( swf->video_type == 0 ) { | |
545 swf_write_video(s, enc, 0, 0); | |
546 } | |
547 | |
0 | 548 return 0; |
549 } | |
550 | |
468 | 551 static int swf_write_packet(AVFormatContext *s, AVPacket *pkt) |
0 | 552 { |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
807
diff
changeset
|
553 AVCodecContext *codec = s->streams[pkt->stream_index]->codec; |
0 | 554 if (codec->codec_type == CODEC_TYPE_AUDIO) |
468 | 555 return swf_write_audio(s, codec, pkt->data, pkt->size); |
0 | 556 else |
468 | 557 return swf_write_video(s, codec, pkt->data, pkt->size); |
0 | 558 } |
559 | |
560 static int swf_write_trailer(AVFormatContext *s) | |
561 { | |
562 SWFContext *swf = s->priv_data; | |
563 ByteIOContext *pb = &s->pb; | |
564 AVCodecContext *enc, *video_enc; | |
565 int file_size, i; | |
566 | |
567 video_enc = NULL; | |
568 for(i=0;i<s->nb_streams;i++) { | |
820
feca73904e67
changing AVCodecContext codec -> *codec in AVStream so additions to AVCodecContext dont randomize AVStream and break binary compatibility
michael
parents:
807
diff
changeset
|
569 enc = s->streams[i]->codec; |
0 | 570 if (enc->codec_type == CODEC_TYPE_VIDEO) |
571 video_enc = enc; | |
572 } | |
573 | |
574 put_swf_tag(s, TAG_END); | |
575 put_swf_end_tag(s); | |
885 | 576 |
0 | 577 put_flush_packet(&s->pb); |
578 | |
579 /* patch file size and number of frames if not streamed */ | |
580 if (!url_is_streamed(&s->pb) && video_enc) { | |
581 file_size = url_ftell(pb); | |
582 url_fseek(pb, 4, SEEK_SET); | |
583 put_le32(pb, file_size); | |
584 url_fseek(pb, swf->duration_pos, SEEK_SET); | |
585 put_le16(pb, video_enc->frame_number); | |
1643
20c25a594c49
seek back at the end of file after updating header
bcoudurier
parents:
1642
diff
changeset
|
586 url_fseek(pb, file_size, SEEK_SET); |
0 | 587 } |
588 return 0; | |
589 } | |
858
66cc656ea404
Replace CONFIG_ENCODERS/CONFIG_DECODERS with CONFIG_MUXERS/CONFIG_DEMUXERS
diego
parents:
820
diff
changeset
|
590 #endif //CONFIG_MUXERS |
0 | 591 |
359 | 592 /*********************************************/ |
593 /* Extract FLV encoded frame and MP3 from swf | |
594 Note that the detection of the real frame | |
595 is inaccurate at this point as it can be | |
885 | 596 quite tricky to determine, you almost certainly |
359 | 597 will get a bad audio/video sync */ |
0 | 598 |
599 static int get_swf_tag(ByteIOContext *pb, int *len_ptr) | |
600 { | |
601 int tag, len; | |
885 | 602 |
0 | 603 if (url_feof(pb)) |
604 return -1; | |
605 | |
606 tag = get_le16(pb); | |
607 len = tag & 0x3f; | |
608 tag = tag >> 6; | |
609 if (len == 0x3f) { | |
610 len = get_le32(pb); | |
611 } | |
806 | 612 // av_log(NULL, AV_LOG_DEBUG, "Tag: %d - Len: %d\n", tag, len); |
0 | 613 *len_ptr = len; |
614 return tag; | |
615 } | |
616 | |
617 | |
618 static int swf_probe(AVProbeData *p) | |
619 { | |
620 /* check file header */ | |
621 if (p->buf_size <= 16) | |
622 return 0; | |
806 | 623 if ((p->buf[0] == 'F' || p->buf[0] == 'C') && p->buf[1] == 'W' && |
0 | 624 p->buf[2] == 'S') |
625 return AVPROBE_SCORE_MAX; | |
626 else | |
627 return 0; | |
628 } | |
629 | |
630 static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
631 { | |
1641 | 632 SWFContext *swf = s->priv_data; |
0 | 633 ByteIOContext *pb = &s->pb; |
1889 | 634 int nbits, len, tag, v; |
1827
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
635 offset_t frame_offset = -1; |
359 | 636 AVStream *ast = 0; |
637 AVStream *vst = 0; | |
638 | |
806 | 639 tag = get_be32(pb) & 0xffffff00; |
640 | |
641 if (tag == MKBETAG('C', 'W', 'S', 0)) | |
642 { | |
887 | 643 av_log(s, AV_LOG_ERROR, "Compressed SWF format not supported\n"); |
806 | 644 return AVERROR_IO; |
645 } | |
646 if (tag != MKBETAG('F', 'W', 'S', 0)) | |
482 | 647 return AVERROR_IO; |
0 | 648 get_le32(pb); |
649 /* skip rectangle size */ | |
650 nbits = get_byte(pb) >> 3; | |
651 len = (4 * nbits - 3 + 7) / 8; | |
652 url_fskip(pb, len); | |
1889 | 653 swf->frame_rate = get_le16(pb); /* 8.8 fixed */ |
0 | 654 get_le16(pb); /* frame count */ |
885 | 655 |
359 | 656 swf->samples_per_frame = 0; |
0 | 657 |
658 for(;;) { | |
1827
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
659 offset_t tag_offset = url_ftell(pb); |
0 | 660 tag = get_swf_tag(pb, &len); |
1827
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
661 if (tag < 0 || tag == TAG_VIDEOFRAME || tag == TAG_STREAMBLOCK) { |
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
662 url_fseek(pb, frame_offset == -1 ? tag_offset : frame_offset, SEEK_SET); |
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
663 break; |
0 | 664 } |
359 | 665 if ( tag == TAG_VIDEOSTREAM && !vst) { |
1637 | 666 int ch_id = get_le16(pb); |
359 | 667 get_le16(pb); |
668 get_le16(pb); | |
669 get_le16(pb); | |
670 get_byte(pb); | |
671 /* Check for FLV1 */ | |
1637 | 672 vst = av_new_stream(s, ch_id); |
1635 | 673 vst->codec->codec_type = CODEC_TYPE_VIDEO; |
674 vst->codec->codec_id = codec_get_id(swf_codec_tags, get_byte(pb)); | |
359 | 675 } else if ( ( tag == TAG_STREAMHEAD || tag == TAG_STREAMHEAD2 ) && !ast) { |
0 | 676 /* streaming found */ |
1642 | 677 int sample_rate_code; |
0 | 678 get_byte(pb); |
679 v = get_byte(pb); | |
359 | 680 swf->samples_per_frame = get_le16(pb); |
1638 | 681 ast = av_new_stream(s, -1); /* -1 to avoid clash with video stream ch_id */ |
682 swf->audio_stream_index = ast->index; | |
1636 | 683 ast->codec->channels = 1 + (v&1); |
684 ast->codec->codec_type = CODEC_TYPE_AUDIO; | |
1833 | 685 ast->codec->codec_id = codec_get_id(swf_audio_codec_tags, (v>>4) & 15); |
1636 | 686 ast->need_parsing = 1; |
1642 | 687 sample_rate_code= (v>>2) & 3; |
688 if (!sample_rate_code) | |
1636 | 689 return AVERROR_IO; |
1642 | 690 ast->codec->sample_rate = 11025 << (sample_rate_code-1); |
1890
04f9a3ae30af
seems safer to set pts timebase to sample rate, fix some mp3
bcoudurier
parents:
1889
diff
changeset
|
691 av_set_pts_info(ast, 64, 1, ast->codec->sample_rate); |
1619
b95cdbfc2d16
dont skip too many bytes if tag is TAG_STREAMHEAD2 and mp3
bcoudurier
parents:
1519
diff
changeset
|
692 if (len > 4) |
325
e1d4300bf783
SWF sanity check patch by (Glyn Kennington <glyn dot kennington at ox dot compsoc dot net>)
michael
parents:
277
diff
changeset
|
693 url_fskip(pb,len-4); |
5 | 694 |
1640 | 695 } else if (tag == TAG_JPEG2 && !vst) { |
696 vst = av_new_stream(s, -2); /* -2 to avoid clash with video stream and audio stream */ | |
697 vst->codec->codec_type = CODEC_TYPE_VIDEO; | |
698 vst->codec->codec_id = CODEC_ID_MJPEG; | |
699 url_fskip(pb, len); | |
1827
2f2afa0a7bd3
don't parse whole swf file in header, can now play ffserver swf streams
bcoudurier
parents:
1826
diff
changeset
|
700 frame_offset = tag_offset; |
0 | 701 } else { |
702 url_fskip(pb, len); | |
703 } | |
704 } | |
1889 | 705 if (vst) |
706 av_set_pts_info(vst, 64, 256, swf->frame_rate); | |
0 | 707 return 0; |
708 } | |
709 | |
710 static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) | |
711 { | |
359 | 712 SWFContext *swf = s->priv_data; |
0 | 713 ByteIOContext *pb = &s->pb; |
359 | 714 AVStream *st = 0; |
715 int tag, len, i, frame; | |
885 | 716 |
0 | 717 for(;;) { |
718 tag = get_swf_tag(pb, &len); | |
885 | 719 if (tag < 0) |
482 | 720 return AVERROR_IO; |
359 | 721 if (tag == TAG_VIDEOFRAME) { |
1637 | 722 int ch_id = get_le16(pb); |
1639 | 723 len -= 2; |
359 | 724 for( i=0; i<s->nb_streams; i++ ) { |
887 | 725 st = s->streams[i]; |
1637 | 726 if (st->codec->codec_type == CODEC_TYPE_VIDEO && st->id == ch_id) { |
727 frame = get_le16(pb); | |
1639 | 728 av_get_packet(pb, pkt, len-2); |
1889 | 729 pkt->pts = frame; |
1637 | 730 pkt->stream_index = st->index; |
731 return pkt->size; | |
359 | 732 } |
885 | 733 } |
359 | 734 } else if (tag == TAG_STREAMBLOCK) { |
1638 | 735 st = s->streams[swf->audio_stream_index]; |
736 if (st->codec->codec_id == CODEC_ID_MP3) { | |
737 url_fskip(pb, 4); | |
738 av_get_packet(pb, pkt, len-4); | |
1833 | 739 } else { // ADPCM, PCM |
740 av_get_packet(pb, pkt, len); | |
359 | 741 } |
1833 | 742 pkt->stream_index = st->index; |
743 return pkt->size; | |
1640 | 744 } else if (tag == TAG_JPEG2) { |
745 for (i=0; i<s->nb_streams; i++) { | |
746 st = s->streams[i]; | |
747 if (st->id == -2) { | |
748 get_le16(pb); /* BITMAP_ID */ | |
749 av_new_packet(pkt, len-2); | |
750 get_buffer(pb, pkt->data, 4); | |
1673 | 751 if (AV_RB32(pkt->data) == 0xffd8ffd9) { |
1640 | 752 /* old SWF files containing SOI/EOI as data start */ |
753 pkt->size -= 4; | |
754 get_buffer(pb, pkt->data, pkt->size); | |
755 } else { | |
756 get_buffer(pb, pkt->data + 4, pkt->size - 4); | |
757 } | |
758 pkt->stream_index = st->index; | |
759 return pkt->size; | |
760 } | |
761 } | |
0 | 762 } |
1639 | 763 url_fskip(pb, len); |
0 | 764 } |
765 return 0; | |
766 } | |
767 | |
768 static int swf_read_close(AVFormatContext *s) | |
769 { | |
770 return 0; | |
771 } | |
772 | |
1169 | 773 #ifdef CONFIG_SWF_DEMUXER |
774 AVInputFormat swf_demuxer = { | |
0 | 775 "swf", |
776 "Flash format", | |
359 | 777 sizeof(SWFContext), |
0 | 778 swf_probe, |
779 swf_read_header, | |
780 swf_read_packet, | |
781 swf_read_close, | |
782 }; | |
1169 | 783 #endif |
784 #ifdef CONFIG_SWF_MUXER | |
785 AVOutputFormat swf_muxer = { | |
0 | 786 "swf", |
787 "Flash format", | |
788 "application/x-shockwave-flash", | |
789 "swf", | |
790 sizeof(SWFContext), | |
359 | 791 CODEC_ID_MP3, |
792 CODEC_ID_FLV1, | |
0 | 793 swf_write_header, |
794 swf_write_packet, | |
795 swf_write_trailer, | |
796 }; | |
1169 | 797 #endif |