Mercurial > libavformat.hg
annotate swf.c @ 517:543990301444 libavformat
unofficial ids for sonic for using in nut (fixme)
author | alex |
---|---|
date | Sat, 21 Aug 2004 19:24:40 +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 } |