Mercurial > libavformat.hg
annotate swf.c @ 455:e5174af0f52f libavformat
fix rounding errors with NTSC patch by (Luca Abeni <lucabe72 at email dot it>)
author | michael |
---|---|
date | Tue, 27 Apr 2004 13:28:16 +0000 |
parents | 845f9de2c883 |
children | b69898ffc92a |
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 * |
6 * This library is free software; you can redistribute it and/or | |
7 * modify it under the terms of the GNU Lesser General Public | |
8 * License as published by the Free Software Foundation; either | |
9 * version 2 of the License, or (at your option) any later version. | |
10 * | |
11 * This library is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 * Lesser General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU Lesser General Public | |
17 * License along with this library; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 */ | |
20 #include "avformat.h" | |
21 | |
22 /* should have a generic way to indicate probable size */ | |
23 #define DUMMY_FILE_SIZE (100 * 1024 * 1024) | |
24 #define DUMMY_DURATION 600 /* in seconds */ | |
25 | |
26 #define TAG_END 0 | |
27 #define TAG_SHOWFRAME 1 | |
28 #define TAG_DEFINESHAPE 2 | |
29 #define TAG_FREECHARACTER 3 | |
30 #define TAG_PLACEOBJECT 4 | |
31 #define TAG_REMOVEOBJECT 5 | |
359 | 32 #define TAG_STREAMHEAD 45 |
0 | 33 #define TAG_STREAMBLOCK 19 |
34 #define TAG_JPEG2 21 | |
359 | 35 #define TAG_PLACEOBJECT2 26 |
36 #define TAG_STREAMHEAD2 45 | |
37 #define TAG_VIDEOSTREAM 60 | |
38 #define TAG_VIDEOFRAME 61 | |
0 | 39 |
40 #define TAG_LONG 0x100 | |
41 | |
42 /* flags for shape definition */ | |
43 #define FLAG_MOVETO 0x01 | |
44 #define FLAG_SETFILL0 0x02 | |
45 #define FLAG_SETFILL1 0x04 | |
46 | |
359 | 47 #define SWF_VIDEO_CODEC_FLV1 0x02 |
48 | |
49 #define AUDIO_FIFO_SIZE 65536 | |
50 | |
0 | 51 /* character id used */ |
52 #define BITMAP_ID 0 | |
359 | 53 #define VIDEO_ID 0 |
0 | 54 #define SHAPE_ID 1 |
55 | |
359 | 56 typedef struct SWFFrame_s { |
57 void *data; | |
58 int size; | |
59 struct SWFFrame_s *prev; | |
60 struct SWFFrame_s *next; | |
61 } SWFFrame; | |
62 | |
0 | 63 typedef struct { |
359 | 64 |
0 | 65 offset_t duration_pos; |
66 offset_t tag_pos; | |
359 | 67 |
68 int samples_per_frame; | |
69 int sound_samples; | |
70 int video_samples; | |
71 int skip_samples; | |
72 int swf_frame_number; | |
73 int video_frame_number; | |
74 int ms_per_frame; | |
75 int ch_id; | |
0 | 76 int tag; |
359 | 77 |
78 uint8_t *audio_fifo; | |
79 int audio_in_pos; | |
80 int audio_out_pos; | |
81 int audio_size; | |
82 | |
83 int video_type; | |
84 int audio_type; | |
85 | |
86 SWFFrame *frame_head; | |
87 SWFFrame *frame_tail; | |
0 | 88 } SWFContext; |
89 | |
359 | 90 static const int sSampleRates[3][4] = { |
91 {44100, 48000, 32000, 0}, | |
92 {22050, 24000, 16000, 0}, | |
93 {11025, 12000, 8000, 0}, | |
94 }; | |
95 | |
96 static const int sBitRates[2][3][15] = { | |
97 { { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, | |
98 { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, | |
99 { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320} | |
100 }, | |
101 { { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, | |
102 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, | |
103 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160} | |
104 }, | |
105 }; | |
106 | |
107 static const int sSamplesPerFrame[3][3] = | |
108 { | |
109 { 384, 1152, 1152 }, | |
110 { 384, 1152, 576 }, | |
111 { 384, 1152, 576 } | |
112 }; | |
113 | |
114 static const int sBitsPerSlot[3] = { | |
115 32, | |
116 8, | |
117 8 | |
118 }; | |
119 | |
120 static int swf_mp3_info(void *data, int *byteSize, int *samplesPerFrame, int *sampleRate, int *isMono ) | |
121 { | |
122 uint8_t *dataTmp = (uint8_t *)data; | |
123 uint32_t header = ( (uint32_t)dataTmp[0] << 24 ) | ( (uint32_t)dataTmp[1] << 16 ) | ( (uint32_t)dataTmp[2] << 8 ) | (uint32_t)dataTmp[3]; | |
124 int layerID = 3 - ((header >> 17) & 0x03); | |
125 int bitRateID = ((header >> 12) & 0x0f); | |
126 int sampleRateID = ((header >> 10) & 0x03); | |
127 int bitRate = 0; | |
128 int bitsPerSlot = sBitsPerSlot[layerID]; | |
129 int isPadded = ((header >> 9) & 0x01); | |
130 | |
131 if ( (( header >> 21 ) & 0x7ff) != 0x7ff ) { | |
132 return 0; | |
133 } | |
134 | |
135 *isMono = ((header >> 6) & 0x03) == 0x03; | |
136 | |
137 if ( (header >> 19 ) & 0x01 ) { | |
138 *sampleRate = sSampleRates[0][sampleRateID]; | |
139 bitRate = sBitRates[0][layerID][bitRateID] * 1000; | |
140 *samplesPerFrame = sSamplesPerFrame[0][layerID]; | |
141 } else { | |
142 if ( (header >> 20) & 0x01 ) { | |
143 *sampleRate = sSampleRates[1][sampleRateID]; | |
144 bitRate = sBitRates[1][layerID][bitRateID] * 1000; | |
145 *samplesPerFrame = sSamplesPerFrame[1][layerID]; | |
146 } else { | |
147 *sampleRate = sSampleRates[2][sampleRateID]; | |
148 bitRate = sBitRates[1][layerID][bitRateID] * 1000; | |
149 *samplesPerFrame = sSamplesPerFrame[2][layerID]; | |
150 } | |
151 } | |
152 | |
153 *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) ); | |
154 | |
155 return 1; | |
156 } | |
157 | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
158 #ifdef CONFIG_ENCODERS |
0 | 159 static void put_swf_tag(AVFormatContext *s, int tag) |
160 { | |
161 SWFContext *swf = s->priv_data; | |
162 ByteIOContext *pb = &s->pb; | |
163 | |
164 swf->tag_pos = url_ftell(pb); | |
165 swf->tag = tag; | |
166 /* reserve some room for the tag */ | |
167 if (tag & TAG_LONG) { | |
168 put_le16(pb, 0); | |
169 put_le32(pb, 0); | |
170 } else { | |
171 put_le16(pb, 0); | |
172 } | |
173 } | |
174 | |
175 static void put_swf_end_tag(AVFormatContext *s) | |
176 { | |
177 SWFContext *swf = s->priv_data; | |
178 ByteIOContext *pb = &s->pb; | |
179 offset_t pos; | |
180 int tag_len, tag; | |
181 | |
182 pos = url_ftell(pb); | |
183 tag_len = pos - swf->tag_pos - 2; | |
184 tag = swf->tag; | |
185 url_fseek(pb, swf->tag_pos, SEEK_SET); | |
186 if (tag & TAG_LONG) { | |
187 tag &= ~TAG_LONG; | |
188 put_le16(pb, (tag << 6) | 0x3f); | |
189 put_le32(pb, tag_len - 4); | |
190 } else { | |
191 assert(tag_len < 0x3f); | |
192 put_le16(pb, (tag << 6) | tag_len); | |
193 } | |
194 url_fseek(pb, pos, SEEK_SET); | |
195 } | |
196 | |
197 static inline void max_nbits(int *nbits_ptr, int val) | |
198 { | |
199 int n; | |
200 | |
201 if (val == 0) | |
202 return; | |
203 val = abs(val); | |
204 n = 1; | |
205 while (val != 0) { | |
206 n++; | |
207 val >>= 1; | |
208 } | |
209 if (n > *nbits_ptr) | |
210 *nbits_ptr = n; | |
211 } | |
212 | |
213 static void put_swf_rect(ByteIOContext *pb, | |
214 int xmin, int xmax, int ymin, int ymax) | |
215 { | |
216 PutBitContext p; | |
65 | 217 uint8_t buf[256]; |
0 | 218 int nbits, mask; |
219 | |
276 | 220 init_put_bits(&p, buf, sizeof(buf)); |
0 | 221 |
222 nbits = 0; | |
223 max_nbits(&nbits, xmin); | |
224 max_nbits(&nbits, xmax); | |
225 max_nbits(&nbits, ymin); | |
226 max_nbits(&nbits, ymax); | |
227 mask = (1 << nbits) - 1; | |
228 | |
229 /* rectangle info */ | |
230 put_bits(&p, 5, nbits); | |
231 put_bits(&p, nbits, xmin & mask); | |
232 put_bits(&p, nbits, xmax & mask); | |
233 put_bits(&p, nbits, ymin & mask); | |
234 put_bits(&p, nbits, ymax & mask); | |
235 | |
236 flush_put_bits(&p); | |
237 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
238 } | |
239 | |
240 static void put_swf_line_edge(PutBitContext *pb, int dx, int dy) | |
241 { | |
242 int nbits, mask; | |
243 | |
244 put_bits(pb, 1, 1); /* edge */ | |
245 put_bits(pb, 1, 1); /* line select */ | |
246 nbits = 2; | |
247 max_nbits(&nbits, dx); | |
248 max_nbits(&nbits, dy); | |
249 | |
250 mask = (1 << nbits) - 1; | |
251 put_bits(pb, 4, nbits - 2); /* 16 bits precision */ | |
252 if (dx == 0) { | |
253 put_bits(pb, 1, 0); | |
254 put_bits(pb, 1, 1); | |
255 put_bits(pb, nbits, dy & mask); | |
256 } else if (dy == 0) { | |
257 put_bits(pb, 1, 0); | |
258 put_bits(pb, 1, 0); | |
259 put_bits(pb, nbits, dx & mask); | |
260 } else { | |
261 put_bits(pb, 1, 1); | |
262 put_bits(pb, nbits, dx & mask); | |
263 put_bits(pb, nbits, dy & mask); | |
264 } | |
265 } | |
266 | |
267 #define FRAC_BITS 16 | |
268 | |
359 | 269 /* put matrix */ |
0 | 270 static void put_swf_matrix(ByteIOContext *pb, |
271 int a, int b, int c, int d, int tx, int ty) | |
272 { | |
273 PutBitContext p; | |
65 | 274 uint8_t buf[256]; |
359 | 275 int nbits; |
0 | 276 |
276 | 277 init_put_bits(&p, buf, sizeof(buf)); |
0 | 278 |
279 put_bits(&p, 1, 1); /* a, d present */ | |
359 | 280 nbits = 1; |
281 max_nbits(&nbits, a); | |
282 max_nbits(&nbits, d); | |
283 put_bits(&p, 5, nbits); /* nb bits */ | |
284 put_bits(&p, nbits, a); | |
285 put_bits(&p, nbits, d); | |
0 | 286 |
287 put_bits(&p, 1, 1); /* b, c present */ | |
359 | 288 nbits = 1; |
289 max_nbits(&nbits, c); | |
290 max_nbits(&nbits, b); | |
291 put_bits(&p, 5, nbits); /* nb bits */ | |
292 put_bits(&p, nbits, c); | |
293 put_bits(&p, nbits, b); | |
0 | 294 |
359 | 295 nbits = 1; |
296 max_nbits(&nbits, tx); | |
297 max_nbits(&nbits, ty); | |
298 put_bits(&p, 5, nbits); /* nb bits */ | |
299 put_bits(&p, nbits, tx); | |
300 put_bits(&p, nbits, ty); | |
0 | 301 |
302 flush_put_bits(&p); | |
303 put_buffer(pb, buf, pbBufPtr(&p) - p.buf); | |
304 } | |
305 | |
359 | 306 /* */ |
0 | 307 static int swf_write_header(AVFormatContext *s) |
308 { | |
309 SWFContext *swf; | |
310 ByteIOContext *pb = &s->pb; | |
311 AVCodecContext *enc, *audio_enc, *video_enc; | |
312 PutBitContext p; | |
65 | 313 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
|
314 int i, width, height, rate, rate_base; |
0 | 315 |
316 swf = av_malloc(sizeof(SWFContext)); | |
317 if (!swf) | |
318 return -1; | |
319 s->priv_data = swf; | |
320 | |
359 | 321 swf->ch_id = -1; |
322 swf->audio_in_pos = 0; | |
323 swf->audio_out_pos = 0; | |
324 swf->audio_size = 0; | |
325 swf->audio_fifo = av_malloc(AUDIO_FIFO_SIZE); | |
326 swf->frame_head = 0; | |
327 swf->frame_tail = 0; | |
328 swf->sound_samples = 0; | |
329 swf->video_samples = 0; | |
330 swf->swf_frame_number = 0; | |
331 swf->video_frame_number = 0; | |
332 swf->skip_samples = 0; | |
333 | |
0 | 334 video_enc = NULL; |
335 audio_enc = NULL; | |
336 for(i=0;i<s->nb_streams;i++) { | |
337 enc = &s->streams[i]->codec; | |
338 if (enc->codec_type == CODEC_TYPE_AUDIO) | |
339 audio_enc = enc; | |
359 | 340 else { |
341 if ( enc->codec_id == CODEC_ID_FLV1 || enc->codec_id == CODEC_ID_MJPEG ) { | |
342 video_enc = enc; | |
343 } else { | |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
344 av_log(enc, AV_LOG_ERROR, "SWF only supports FLV1 and MJPEG\n"); |
359 | 345 return -1; |
346 } | |
347 } | |
0 | 348 } |
349 | |
350 if (!video_enc) { | |
351 /* currenty, cannot work correctly if audio only */ | |
359 | 352 swf->video_type = 0; |
0 | 353 width = 320; |
354 height = 200; | |
85
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
355 rate = 10; |
25062c9b1f86
per context frame_rate_base, this should finally fix frame_rate related av sync issues
michaelni
parents:
65
diff
changeset
|
356 rate_base= 1; |
0 | 357 } else { |
359 | 358 swf->video_type = video_enc->codec_id; |
0 | 359 width = video_enc->width; |
360 height = video_enc->height; | |
361 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
|
362 rate_base = video_enc->frame_rate_base; |
0 | 363 } |
364 | |
359 | 365 if (!audio_enc ) { |
366 swf->audio_type = 0; | |
367 swf->samples_per_frame = ( 44100. * rate_base ) / rate; | |
368 } else { | |
369 swf->audio_type = audio_enc->codec_id; | |
370 swf->samples_per_frame = ( ( audio_enc->sample_rate ) * rate_base ) / rate; | |
371 } | |
372 | |
0 | 373 put_tag(pb, "FWS"); |
359 | 374 if ( video_enc && video_enc->codec_id == CODEC_ID_FLV1 ) { |
375 put_byte(pb, 6); /* version (version 6 and above support FLV1 codec) */ | |
376 } else { | |
377 put_byte(pb, 4); /* version (should use 4 for mpeg audio support) */ | |
378 } | |
0 | 379 put_le32(pb, DUMMY_FILE_SIZE); /* dummy size |
380 (will be patched if not streamed) */ | |
381 | |
359 | 382 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
|
383 put_le16(pb, (rate * 256) / rate_base); /* frame rate */ |
0 | 384 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
|
385 put_le16(pb, (uint16_t)(DUMMY_DURATION * (int64_t)rate / rate_base)); /* frame count */ |
0 | 386 |
387 /* define a shape with the jpeg inside */ | |
359 | 388 if ( video_enc && video_enc->codec_id == CODEC_ID_FLV1 ) { |
389 } else if ( video_enc && video_enc->codec_id == CODEC_ID_MJPEG ) { | |
390 put_swf_tag(s, TAG_DEFINESHAPE); | |
0 | 391 |
359 | 392 put_le16(pb, SHAPE_ID); /* ID of shape */ |
393 /* bounding rectangle */ | |
394 put_swf_rect(pb, 0, width, 0, height); | |
395 /* style info */ | |
396 put_byte(pb, 1); /* one fill style */ | |
397 put_byte(pb, 0x41); /* clipped bitmap fill */ | |
398 put_le16(pb, BITMAP_ID); /* bitmap ID */ | |
399 /* position of the bitmap */ | |
400 put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, | |
401 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); | |
402 put_byte(pb, 0); /* no line style */ | |
0 | 403 |
359 | 404 /* shape drawing */ |
405 init_put_bits(&p, buf1, sizeof(buf1)); | |
406 put_bits(&p, 4, 1); /* one fill bit */ | |
407 put_bits(&p, 4, 0); /* zero line bit */ | |
408 | |
409 put_bits(&p, 1, 0); /* not an edge */ | |
410 put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0); | |
411 put_bits(&p, 5, 1); /* nbits */ | |
412 put_bits(&p, 1, 0); /* X */ | |
413 put_bits(&p, 1, 0); /* Y */ | |
414 put_bits(&p, 1, 1); /* set fill style 1 */ | |
0 | 415 |
359 | 416 /* draw the rectangle ! */ |
417 put_swf_line_edge(&p, width, 0); | |
418 put_swf_line_edge(&p, 0, height); | |
419 put_swf_line_edge(&p, -width, 0); | |
420 put_swf_line_edge(&p, 0, -height); | |
0 | 421 |
359 | 422 /* end of shape */ |
423 put_bits(&p, 1, 0); /* not an edge */ | |
424 put_bits(&p, 5, 0); | |
0 | 425 |
359 | 426 flush_put_bits(&p); |
427 put_buffer(pb, buf1, pbBufPtr(&p) - p.buf); | |
0 | 428 |
359 | 429 put_swf_end_tag(s); |
430 } | |
0 | 431 |
359 | 432 if (audio_enc && audio_enc->codec_id == CODEC_ID_MP3 ) { |
0 | 433 int v; |
434 | |
435 /* start sound */ | |
359 | 436 put_swf_tag(s, TAG_STREAMHEAD2); |
0 | 437 |
438 v = 0; | |
439 switch(audio_enc->sample_rate) { | |
440 case 11025: | |
441 v |= 1 << 2; | |
442 break; | |
443 case 22050: | |
444 v |= 2 << 2; | |
445 break; | |
446 case 44100: | |
447 v |= 3 << 2; | |
448 break; | |
449 default: | |
450 /* not supported */ | |
359 | 451 av_free(swf->audio_fifo); |
0 | 452 av_free(swf); |
453 return -1; | |
454 } | |
359 | 455 v |= 0x02; /* 16 bit playback */ |
0 | 456 if (audio_enc->channels == 2) |
359 | 457 v |= 0x01; /* stereo playback */ |
458 put_byte(&s->pb, v); | |
0 | 459 v |= 0x20; /* mp3 compressed */ |
460 put_byte(&s->pb, v); | |
359 | 461 put_le16(&s->pb, swf->samples_per_frame); /* avg samples per frame */ |
462 put_le16(&s->pb, 0); | |
0 | 463 |
464 put_swf_end_tag(s); | |
465 } | |
466 | |
467 put_flush_packet(&s->pb); | |
468 return 0; | |
469 } | |
470 | |
471 static int swf_write_video(AVFormatContext *s, | |
241 | 472 AVCodecContext *enc, const uint8_t *buf, int size) |
0 | 473 { |
359 | 474 SWFContext *swf = s->priv_data; |
0 | 475 ByteIOContext *pb = &s->pb; |
359 | 476 int c = 0; |
477 int outSize = 0; | |
478 int outSamples = 0; | |
479 | |
480 /* Flash Player limit */ | |
360 | 481 if ( swf->swf_frame_number == 16000 ) { |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
482 av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n"); |
359 | 483 } |
0 | 484 |
359 | 485 /* Store video data in queue */ |
486 if ( enc->codec_type == CODEC_TYPE_VIDEO ) { | |
487 SWFFrame *new_frame = av_malloc(sizeof(SWFFrame)); | |
488 new_frame->prev = 0; | |
489 new_frame->next = swf->frame_head; | |
490 new_frame->data = av_malloc(size); | |
491 new_frame->size = size; | |
492 memcpy(new_frame->data,buf,size); | |
493 swf->frame_head = new_frame; | |
494 if ( swf->frame_tail == 0 ) { | |
495 swf->frame_tail = new_frame; | |
496 } | |
497 } | |
498 | |
499 if ( swf->audio_type ) { | |
500 /* Prescan audio data for this swf frame */ | |
501 retry_swf_audio_packet: | |
502 if ( ( swf->audio_size-outSize ) >= 4 ) { | |
503 int mp3FrameSize = 0; | |
504 int mp3SampleRate = 0; | |
505 int mp3IsMono = 0; | |
506 int mp3SamplesPerFrame = 0; | |
507 | |
508 /* copy out mp3 header from ring buffer */ | |
509 uint8_t header[4]; | |
510 for (c=0; c<4; c++) { | |
511 header[c] = swf->audio_fifo[(swf->audio_in_pos+outSize+c) % AUDIO_FIFO_SIZE]; | |
512 } | |
513 | |
514 if ( swf_mp3_info(header,&mp3FrameSize,&mp3SamplesPerFrame,&mp3SampleRate,&mp3IsMono) ) { | |
515 if ( ( swf->audio_size-outSize ) >= mp3FrameSize ) { | |
516 outSize += mp3FrameSize; | |
517 outSamples += mp3SamplesPerFrame; | |
518 if ( ( swf->sound_samples + outSamples + swf->samples_per_frame ) < swf->video_samples ) { | |
519 goto retry_swf_audio_packet; | |
520 } | |
521 } | |
522 } else { | |
523 /* invalid mp3 data, skip forward | |
524 we need to do this since the Flash Player | |
525 does not like custom headers */ | |
526 swf->audio_in_pos ++; | |
527 swf->audio_size --; | |
528 swf->audio_in_pos %= AUDIO_FIFO_SIZE; | |
529 goto retry_swf_audio_packet; | |
530 } | |
531 } | |
0 | 532 |
359 | 533 /* audio stream is behind video stream, bail */ |
534 if ( ( swf->sound_samples + outSamples + swf->samples_per_frame ) < swf->video_samples ) { | |
535 return 0; | |
536 } | |
537 | |
538 /* compute audio/video drift */ | |
539 if ( enc->codec_type == CODEC_TYPE_VIDEO ) { | |
540 swf->skip_samples = (int)( ( (double)(swf->swf_frame_number) * (double)enc->frame_rate_base * 44100. ) / (double)(enc->frame_rate) ); | |
541 swf->skip_samples -= swf->video_samples; | |
542 } | |
0 | 543 } |
544 | |
359 | 545 /* check if we need to insert a padding frame */ |
546 if (swf->skip_samples <= ( swf->samples_per_frame / 2 ) ) { | |
547 /* no, it is time for a real frame, check if one is available */ | |
548 if ( swf->frame_tail ) { | |
549 if ( swf->video_type == CODEC_ID_FLV1 ) { | |
550 if ( swf->video_frame_number == 0 ) { | |
551 /* create a new video object */ | |
552 put_swf_tag(s, TAG_VIDEOSTREAM); | |
553 put_le16(pb, VIDEO_ID); | |
554 put_le16(pb, 15000 ); /* hard flash player limit */ | |
555 put_le16(pb, enc->width); | |
556 put_le16(pb, enc->height); | |
557 put_byte(pb, 0); | |
558 put_byte(pb, SWF_VIDEO_CODEC_FLV1); | |
559 put_swf_end_tag(s); | |
560 | |
561 /* place the video object for the first time */ | |
562 put_swf_tag(s, TAG_PLACEOBJECT2); | |
563 put_byte(pb, 0x36); | |
564 put_le16(pb, 1); | |
565 put_le16(pb, VIDEO_ID); | |
566 put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); | |
567 put_le16(pb, swf->video_frame_number ); | |
568 put_byte(pb, 'v'); | |
569 put_byte(pb, 'i'); | |
570 put_byte(pb, 'd'); | |
571 put_byte(pb, 'e'); | |
572 put_byte(pb, 'o'); | |
573 put_byte(pb, 0x00); | |
574 put_swf_end_tag(s); | |
575 } else { | |
576 /* mark the character for update */ | |
577 put_swf_tag(s, TAG_PLACEOBJECT2); | |
578 put_byte(pb, 0x11); | |
579 put_le16(pb, 1); | |
580 put_le16(pb, swf->video_frame_number ); | |
581 put_swf_end_tag(s); | |
582 } | |
583 | |
584 // write out pending frames | |
585 for (; ( enc->frame_number - swf->video_frame_number ) > 0;) { | |
586 /* set video frame data */ | |
587 put_swf_tag(s, TAG_VIDEOFRAME | TAG_LONG); | |
588 put_le16(pb, VIDEO_ID); | |
589 put_le16(pb, swf->video_frame_number++ ); | |
590 put_buffer(pb, swf->frame_tail->data, swf->frame_tail->size); | |
591 put_swf_end_tag(s); | |
592 } | |
0 | 593 |
359 | 594 } else if ( swf->video_type == CODEC_ID_MJPEG ) { |
595 if (swf->swf_frame_number > 0) { | |
596 /* remove the shape */ | |
597 put_swf_tag(s, TAG_REMOVEOBJECT); | |
598 put_le16(pb, SHAPE_ID); /* shape ID */ | |
599 put_le16(pb, 1); /* depth */ | |
600 put_swf_end_tag(s); | |
601 | |
602 /* free the bitmap */ | |
603 put_swf_tag(s, TAG_FREECHARACTER); | |
604 put_le16(pb, BITMAP_ID); | |
605 put_swf_end_tag(s); | |
606 } | |
607 | |
608 put_swf_tag(s, TAG_JPEG2 | TAG_LONG); | |
609 | |
610 put_le16(pb, BITMAP_ID); /* ID of the image */ | |
611 | |
612 /* a dummy jpeg header seems to be required */ | |
613 put_byte(pb, 0xff); | |
614 put_byte(pb, 0xd8); | |
615 put_byte(pb, 0xff); | |
616 put_byte(pb, 0xd9); | |
617 /* write the jpeg image */ | |
618 put_buffer(pb, swf->frame_tail->data, swf->frame_tail->size); | |
619 | |
620 put_swf_end_tag(s); | |
621 | |
622 /* draw the shape */ | |
623 | |
624 put_swf_tag(s, TAG_PLACEOBJECT); | |
625 put_le16(pb, SHAPE_ID); /* shape ID */ | |
626 put_le16(pb, 1); /* depth */ | |
627 put_swf_matrix(pb, 20 << FRAC_BITS, 0, 0, 20 << FRAC_BITS, 0, 0); | |
628 put_swf_end_tag(s); | |
629 } else { | |
630 /* invalid codec */ | |
631 } | |
632 | |
633 av_free(swf->frame_tail->data); | |
634 swf->frame_tail = swf->frame_tail->prev; | |
635 if ( swf->frame_tail ) { | |
636 if ( swf->frame_tail->next ) { | |
637 av_free(swf->frame_tail->next); | |
638 } | |
639 swf->frame_tail->next = 0; | |
640 } else { | |
641 swf->frame_head = 0; | |
642 } | |
643 swf->swf_frame_number ++; | |
644 } | |
645 } | |
0 | 646 |
359 | 647 swf->video_samples += swf->samples_per_frame; |
0 | 648 |
359 | 649 /* streaming sound always should be placed just before showframe tags */ |
650 if ( outSize > 0 ) { | |
651 put_swf_tag(s, TAG_STREAMBLOCK | TAG_LONG); | |
652 put_le16(pb, outSamples); | |
653 put_le16(pb, 0); | |
654 for (c=0; c<outSize; c++) { | |
655 put_byte(pb,swf->audio_fifo[(swf->audio_in_pos+c) % AUDIO_FIFO_SIZE]); | |
656 } | |
657 put_swf_end_tag(s); | |
0 | 658 |
359 | 659 /* update FIFO */ |
660 swf->sound_samples += outSamples; | |
661 swf->audio_in_pos += outSize; | |
662 swf->audio_size -= outSize; | |
663 swf->audio_in_pos %= AUDIO_FIFO_SIZE; | |
664 } | |
665 | |
0 | 666 /* output the frame */ |
667 put_swf_tag(s, TAG_SHOWFRAME); | |
668 put_swf_end_tag(s); | |
669 | |
670 put_flush_packet(&s->pb); | |
359 | 671 |
0 | 672 return 0; |
673 } | |
674 | |
359 | 675 static int swf_write_audio(AVFormatContext *s, |
676 AVCodecContext *enc, const uint8_t *buf, int size) | |
0 | 677 { |
359 | 678 SWFContext *swf = s->priv_data; |
679 int c = 0; | |
680 | |
681 /* Flash Player limit */ | |
360 | 682 if ( swf->swf_frame_number == 16000 ) { |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
683 av_log(enc, AV_LOG_INFO, "warning: Flash Player limit of 16000 frames reached\n"); |
359 | 684 } |
0 | 685 |
359 | 686 if (enc->codec_id == CODEC_ID_MP3 ) { |
687 for (c=0; c<size; c++) { | |
688 swf->audio_fifo[(swf->audio_out_pos+c)%AUDIO_FIFO_SIZE] = buf[c]; | |
689 } | |
690 swf->audio_size += size; | |
691 swf->audio_out_pos += size; | |
692 swf->audio_out_pos %= AUDIO_FIFO_SIZE; | |
693 } | |
0 | 694 |
359 | 695 /* if audio only stream make sure we add swf frames */ |
696 if ( swf->video_type == 0 ) { | |
697 swf_write_video(s, enc, 0, 0); | |
698 } | |
699 | |
0 | 700 return 0; |
701 } | |
702 | |
703 static int swf_write_packet(AVFormatContext *s, int stream_index, | |
241 | 704 const uint8_t *buf, int size, int64_t pts) |
0 | 705 { |
706 AVCodecContext *codec = &s->streams[stream_index]->codec; | |
707 if (codec->codec_type == CODEC_TYPE_AUDIO) | |
359 | 708 return swf_write_audio(s, codec, buf, size); |
0 | 709 else |
710 return swf_write_video(s, codec, buf, size); | |
711 } | |
712 | |
713 static int swf_write_trailer(AVFormatContext *s) | |
714 { | |
715 SWFContext *swf = s->priv_data; | |
716 ByteIOContext *pb = &s->pb; | |
717 AVCodecContext *enc, *video_enc; | |
718 int file_size, i; | |
719 | |
720 video_enc = NULL; | |
721 for(i=0;i<s->nb_streams;i++) { | |
722 enc = &s->streams[i]->codec; | |
723 if (enc->codec_type == CODEC_TYPE_VIDEO) | |
724 video_enc = enc; | |
725 } | |
726 | |
727 put_swf_tag(s, TAG_END); | |
728 put_swf_end_tag(s); | |
729 | |
730 put_flush_packet(&s->pb); | |
731 | |
732 /* patch file size and number of frames if not streamed */ | |
733 if (!url_is_streamed(&s->pb) && video_enc) { | |
734 file_size = url_ftell(pb); | |
735 url_fseek(pb, 4, SEEK_SET); | |
736 put_le32(pb, file_size); | |
737 url_fseek(pb, swf->duration_pos, SEEK_SET); | |
738 put_le16(pb, video_enc->frame_number); | |
739 } | |
359 | 740 |
741 av_free(swf->audio_fifo); | |
742 | |
0 | 743 return 0; |
744 } | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
745 #endif //CONFIG_ENCODERS |
0 | 746 |
359 | 747 /*********************************************/ |
748 /* Extract FLV encoded frame and MP3 from swf | |
749 Note that the detection of the real frame | |
750 is inaccurate at this point as it can be | |
751 quite tricky to determine, you almost certainly | |
752 will get a bad audio/video sync */ | |
0 | 753 |
754 static int get_swf_tag(ByteIOContext *pb, int *len_ptr) | |
755 { | |
756 int tag, len; | |
757 | |
758 if (url_feof(pb)) | |
759 return -1; | |
760 | |
761 tag = get_le16(pb); | |
762 len = tag & 0x3f; | |
763 tag = tag >> 6; | |
764 if (len == 0x3f) { | |
765 len = get_le32(pb); | |
766 } | |
767 *len_ptr = len; | |
768 return tag; | |
769 } | |
770 | |
771 | |
772 static int swf_probe(AVProbeData *p) | |
773 { | |
774 /* check file header */ | |
775 if (p->buf_size <= 16) | |
776 return 0; | |
777 if (p->buf[0] == 'F' && p->buf[1] == 'W' && | |
778 p->buf[2] == 'S') | |
779 return AVPROBE_SCORE_MAX; | |
780 else | |
781 return 0; | |
782 } | |
783 | |
784 static int swf_read_header(AVFormatContext *s, AVFormatParameters *ap) | |
785 { | |
359 | 786 SWFContext *swf = 0; |
0 | 787 ByteIOContext *pb = &s->pb; |
788 int nbits, len, frame_rate, tag, v; | |
359 | 789 offset_t firstTagOff; |
790 AVStream *ast = 0; | |
791 AVStream *vst = 0; | |
792 | |
793 swf = av_malloc(sizeof(SWFContext)); | |
794 if (!swf) | |
795 return -1; | |
796 s->priv_data = swf; | |
0 | 797 |
798 if ((get_be32(pb) & 0xffffff00) != MKBETAG('F', 'W', 'S', 0)) | |
799 return -EIO; | |
800 get_le32(pb); | |
801 /* skip rectangle size */ | |
802 nbits = get_byte(pb) >> 3; | |
803 len = (4 * nbits - 3 + 7) / 8; | |
804 url_fskip(pb, len); | |
805 frame_rate = get_le16(pb); | |
806 get_le16(pb); /* frame count */ | |
359 | 807 |
808 av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ | |
809 | |
810 /* The Flash Player converts 8.8 frame rates | |
811 to milliseconds internally. Do the same to get | |
812 a correct framerate */ | |
813 swf->ms_per_frame = ( 1000 * 256 ) / frame_rate; | |
814 swf->samples_per_frame = 0; | |
815 swf->ch_id = -1; | |
0 | 816 |
359 | 817 firstTagOff = url_ftell(pb); |
0 | 818 for(;;) { |
819 tag = get_swf_tag(pb, &len); | |
820 if (tag < 0) { | |
359 | 821 if ( ast || vst ) { |
822 if ( vst && ast ) { | |
823 vst->codec.frame_rate = ast->codec.sample_rate / swf->samples_per_frame; | |
824 vst->codec.frame_rate_base = 1; | |
825 } | |
826 break; | |
827 } | |
370
845f9de2c883
av_log() patch by (Michel Bardiaux <mbardiaux at peaktime dot be>)
michael
parents:
360
diff
changeset
|
828 av_log(s, AV_LOG_ERROR, "No media found in SWF\n"); |
0 | 829 return -EIO; |
830 } | |
359 | 831 if ( tag == TAG_VIDEOSTREAM && !vst) { |
832 swf->ch_id = get_le16(pb); | |
833 get_le16(pb); | |
834 get_le16(pb); | |
835 get_le16(pb); | |
836 get_byte(pb); | |
837 /* Check for FLV1 */ | |
838 if ( get_byte(pb) == SWF_VIDEO_CODEC_FLV1 ) { | |
839 vst = av_new_stream(s, 0); | |
840 vst->codec.codec_type = CODEC_TYPE_VIDEO; | |
841 vst->codec.codec_id = CODEC_ID_FLV1; | |
842 if ( swf->samples_per_frame ) { | |
843 vst->codec.frame_rate = 1000. / swf->ms_per_frame; | |
844 vst->codec.frame_rate_base = 1; | |
845 } | |
846 } | |
847 } else if ( ( tag == TAG_STREAMHEAD || tag == TAG_STREAMHEAD2 ) && !ast) { | |
0 | 848 /* streaming found */ |
849 get_byte(pb); | |
850 v = get_byte(pb); | |
359 | 851 swf->samples_per_frame = get_le16(pb); |
325
e1d4300bf783
SWF sanity check patch by (Glyn Kennington <glyn dot kennington at ox dot compsoc dot net>)
michael
parents:
277
diff
changeset
|
852 if (len!=4) |
e1d4300bf783
SWF sanity check patch by (Glyn Kennington <glyn dot kennington at ox dot compsoc dot net>)
michael
parents:
277
diff
changeset
|
853 url_fskip(pb,len-4); |
0 | 854 /* if mp3 streaming found, OK */ |
855 if ((v & 0x20) != 0) { | |
359 | 856 if ( tag == TAG_STREAMHEAD2 ) { |
857 get_le16(pb); | |
858 } | |
859 ast = av_new_stream(s, 1); | |
860 if (!ast) | |
0 | 861 return -ENOMEM; |
5 | 862 |
0 | 863 if (v & 0x01) |
359 | 864 ast->codec.channels = 2; |
0 | 865 else |
359 | 866 ast->codec.channels = 1; |
0 | 867 |
868 switch((v>> 2) & 0x03) { | |
869 case 1: | |
359 | 870 ast->codec.sample_rate = 11025; |
0 | 871 break; |
872 case 2: | |
359 | 873 ast->codec.sample_rate = 22050; |
0 | 874 break; |
875 case 3: | |
359 | 876 ast->codec.sample_rate = 44100; |
0 | 877 break; |
878 default: | |
359 | 879 av_free(ast); |
0 | 880 return -EIO; |
881 } | |
359 | 882 ast->codec.codec_type = CODEC_TYPE_AUDIO; |
883 ast->codec.codec_id = CODEC_ID_MP3; | |
0 | 884 } |
885 } else { | |
886 url_fskip(pb, len); | |
887 } | |
888 } | |
359 | 889 url_fseek(pb, firstTagOff, SEEK_SET); |
890 | |
0 | 891 return 0; |
892 } | |
893 | |
894 static int swf_read_packet(AVFormatContext *s, AVPacket *pkt) | |
895 { | |
359 | 896 SWFContext *swf = s->priv_data; |
0 | 897 ByteIOContext *pb = &s->pb; |
359 | 898 AVStream *st = 0; |
899 int tag, len, i, frame; | |
0 | 900 |
901 for(;;) { | |
902 tag = get_swf_tag(pb, &len); | |
903 if (tag < 0) | |
904 return -EIO; | |
359 | 905 if (tag == TAG_VIDEOFRAME) { |
906 for( i=0; i<s->nb_streams; i++ ) { | |
907 st = s->streams[i]; | |
908 if (st->id == 0) { | |
909 if ( get_le16(pb) == swf->ch_id ) { | |
910 frame = get_le16(pb); | |
911 av_new_packet(pkt, len-4); | |
912 pkt->pts = frame * swf->ms_per_frame; | |
913 pkt->stream_index = st->index; | |
914 get_buffer(pb, pkt->data, pkt->size); | |
915 return pkt->size; | |
916 } else { | |
917 url_fskip(pb, len-2); | |
918 continue; | |
919 } | |
920 } | |
921 } | |
922 url_fskip(pb, len); | |
923 } else if (tag == TAG_STREAMBLOCK) { | |
924 for( i=0; i<s->nb_streams; i++ ) { | |
925 st = s->streams[i]; | |
926 if (st->id == 1) { | |
927 av_new_packet(pkt, len); | |
928 pkt->stream_index = st->index; | |
929 get_buffer(pb, pkt->data, pkt->size); | |
930 return pkt->size; | |
931 } | |
932 } | |
933 url_fskip(pb, len); | |
0 | 934 } else { |
935 url_fskip(pb, len); | |
936 } | |
937 } | |
938 return 0; | |
939 } | |
940 | |
941 static int swf_read_close(AVFormatContext *s) | |
942 { | |
943 return 0; | |
944 } | |
945 | |
946 static AVInputFormat swf_iformat = { | |
947 "swf", | |
948 "Flash format", | |
359 | 949 sizeof(SWFContext), |
0 | 950 swf_probe, |
951 swf_read_header, | |
952 swf_read_packet, | |
953 swf_read_close, | |
954 }; | |
955 | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
956 #ifdef CONFIG_ENCODERS |
0 | 957 static AVOutputFormat swf_oformat = { |
958 "swf", | |
959 "Flash format", | |
960 "application/x-shockwave-flash", | |
961 "swf", | |
962 sizeof(SWFContext), | |
359 | 963 CODEC_ID_MP3, |
964 CODEC_ID_FLV1, | |
0 | 965 swf_write_header, |
966 swf_write_packet, | |
967 swf_write_trailer, | |
968 }; | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
969 #endif //CONFIG_ENCODERS |
0 | 970 |
971 int swf_init(void) | |
972 { | |
973 av_register_input_format(&swf_iformat); | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
974 #ifdef CONFIG_ENCODERS |
0 | 975 av_register_output_format(&swf_oformat); |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
276
diff
changeset
|
976 #endif //CONFIG_ENCODERS |
0 | 977 return 0; |
978 } |