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