Mercurial > libavformat.hg
comparison flvenc.c @ 164:99fbacf0f764 libavformat
flash video (flv) support patch by (Garrick Meeker <gmeeker at theoryllc dot com>)
author | michaelni |
---|---|
date | Wed, 09 Jul 2003 23:10:59 +0000 |
parents | |
children | 2271829b6f7e |
comparison
equal
deleted
inserted
replaced
163:470456bd0065 | 164:99fbacf0f764 |
---|---|
1 /* | |
2 * FLV encoder. | |
3 * Copyright (c) 2003 The FFmpeg Project. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 #include "avformat.h" | |
20 | |
21 #define VIDEO_FIFO_SIZE 512 | |
22 | |
23 typedef struct FLVFrame { | |
24 int type; | |
25 int timestamp; | |
26 int flags; | |
27 uint8_t *data; | |
28 int size; | |
29 struct FLVFrame *next; | |
30 } FLVFrame; | |
31 | |
32 typedef struct FLVContext { | |
33 int hasAudio; | |
34 int hasVideo; | |
35 #ifdef CONFIG_MP3LAME | |
36 int audioTime; | |
37 int audioInPos; | |
38 int audioOutPos; | |
39 int audioSize; | |
40 int audioRate; | |
41 int initDelay; | |
42 int soundDelay; | |
43 uint8_t *audioFifo; | |
44 int64_t sampleCount; | |
45 #endif // CONFIG_MP3LAME | |
46 int64_t frameCount; | |
47 FLVFrame *frames; | |
48 } FLVContext; | |
49 | |
50 | |
51 #ifdef CONFIG_MP3LAME | |
52 | |
53 #define AUDIO_FIFO_SIZE 65536 | |
54 | |
55 static const int sSampleRates[3][4] = { | |
56 {44100, 48000, 32000, 0}, | |
57 {22050, 24000, 16000, 0}, | |
58 {11025, 12000, 8000, 0}, | |
59 }; | |
60 | |
61 static const int sBitRates[2][3][15] = { | |
62 { { 0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448}, | |
63 { 0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384}, | |
64 { 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320} | |
65 }, | |
66 { { 0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256}, | |
67 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160}, | |
68 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160} | |
69 }, | |
70 }; | |
71 | |
72 static const int sSamplesPerFrame[3][3] = | |
73 { | |
74 { 384, 1152, 1152 }, | |
75 { 384, 1152, 576 }, | |
76 { 384, 1152, 576 } | |
77 }; | |
78 | |
79 static const int sBitsPerSlot[3] = { | |
80 32, | |
81 8, | |
82 8 | |
83 }; | |
84 | |
85 static int mp3info(void *data, int *byteSize, int *samplesPerFrame, int *sampleRate, int *isMono ) | |
86 { | |
87 uint8_t *dataTmp = (uint8_t *)data; | |
88 uint32_t header = ( (uint32_t)dataTmp[0] << 24 ) | ( (uint32_t)dataTmp[1] << 16 ) | ( (uint32_t)dataTmp[2] << 8 ) | (uint32_t)dataTmp[3]; | |
89 int layerID = 3 - ((header >> 17) & 0x03); | |
90 int bitRateID = ((header >> 12) & 0x0f); | |
91 int sampleRateID = ((header >> 10) & 0x03); | |
92 int bitRate = 0; | |
93 int bitsPerSlot = sBitsPerSlot[layerID]; | |
94 int isPadded = ((header >> 9) & 0x01); | |
95 | |
96 if ( (( header >> 21 ) & 0x7ff) != 0x7ff ) { | |
97 return 0; | |
98 } | |
99 | |
100 if ( !isPadded ) { | |
101 printf("Fatal error: mp3 data is not padded!\n"); | |
102 exit(0); | |
103 } | |
104 | |
105 *isMono = ((header >> 6) & 0x03) == 0x03; | |
106 | |
107 if ( (header >> 19 ) & 0x01 ) { | |
108 *sampleRate = sSampleRates[0][sampleRateID]; | |
109 bitRate = sBitRates[0][layerID][bitRateID] * 1000; | |
110 *samplesPerFrame = sSamplesPerFrame[0][layerID]; | |
111 | |
112 } else { | |
113 if ( (header >> 20) & 0x01 ) { | |
114 *sampleRate = sSampleRates[1][sampleRateID]; | |
115 bitRate = sBitRates[1][layerID][bitRateID] * 1000; | |
116 *samplesPerFrame = sSamplesPerFrame[1][layerID]; | |
117 } else { | |
118 *sampleRate = sSampleRates[2][sampleRateID]; | |
119 bitRate = sBitRates[1][layerID][bitRateID] * 1000; | |
120 *samplesPerFrame = sSamplesPerFrame[2][layerID]; | |
121 } | |
122 } | |
123 | |
124 *byteSize = ( ( ( ( *samplesPerFrame * (bitRate / bitsPerSlot) ) / *sampleRate ) + isPadded ) * bitsPerSlot); | |
125 | |
126 return 1; | |
127 } | |
128 #endif // CONFIG_MP3LAME | |
129 | |
130 static int flv_write_header(AVFormatContext *s) | |
131 { | |
132 ByteIOContext *pb = &s->pb; | |
133 FLVContext *flv = s->priv_data; | |
134 | |
135 av_set_pts_info(s, 24, 1, 1000); /* 24 bit pts in ms */ | |
136 | |
137 flv->hasAudio = 0; | |
138 flv->hasVideo = 0; | |
139 | |
140 #ifdef CONFIG_MP3LAME | |
141 flv->audioTime = -1; | |
142 flv->audioFifo = av_malloc(AUDIO_FIFO_SIZE); | |
143 flv->audioInPos = 0; | |
144 flv->audioOutPos = 0; | |
145 flv->audioSize = 0; | |
146 flv->audioRate = 44100; | |
147 flv->initDelay = -1; | |
148 flv->soundDelay = 0; | |
149 #endif // CONFIG_MP3LAME | |
150 | |
151 flv->frames = 0; | |
152 | |
153 put_tag(pb,"FLV"); | |
154 put_byte(pb,1); | |
155 put_byte(pb,0); // delayed write | |
156 put_be32(pb,9); | |
157 put_be32(pb,0); | |
158 | |
159 return 0; | |
160 } | |
161 | |
162 static void put_be24(ByteIOContext *pb, int value) | |
163 { | |
164 put_byte(pb, (value>>16) & 0xFF ); | |
165 put_byte(pb, (value>> 8) & 0xFF ); | |
166 put_byte(pb, (value>> 0) & 0xFF ); | |
167 } | |
168 | |
169 static void InsertSorted(FLVContext *flv, FLVFrame *frame) | |
170 { | |
171 if ( !flv->frames ) { | |
172 flv->frames = frame; | |
173 } else { | |
174 FLVFrame *trav = flv->frames; | |
175 FLVFrame *prev = 0; | |
176 for (;trav;) { | |
177 if ( trav->timestamp >= frame->timestamp ) { | |
178 frame->next = trav; | |
179 if ( prev ) { | |
180 prev->next = frame; | |
181 } else { | |
182 flv->frames = frame; | |
183 } | |
184 break; | |
185 } | |
186 prev = trav; | |
187 trav = trav->next; | |
188 } | |
189 if ( !trav ) { | |
190 prev->next = frame; | |
191 } | |
192 } | |
193 } | |
194 | |
195 static void DumpFrame(ByteIOContext *pb, FLVFrame *frame) | |
196 { | |
197 put_byte(pb,frame->type); // message type | |
198 put_be24(pb,frame->size+1); // include flags | |
199 put_be24(pb,frame->timestamp); // time stamp | |
200 put_be32(pb,0); // reserved | |
201 put_byte(pb,frame->flags); | |
202 put_buffer(pb, frame->data, frame->size); | |
203 put_be32(pb,frame->size+1+11); // reserved | |
204 av_free(frame->data); | |
205 } | |
206 | |
207 static void Dump(FLVContext *flv, ByteIOContext *pb, int count) | |
208 { | |
209 int c=0; | |
210 FLVFrame *trav = flv->frames; | |
211 FLVFrame *prev = 0; | |
212 for (;trav;c++) { | |
213 trav = trav->next; | |
214 } | |
215 trav = flv->frames; | |
216 for ( ; c >= count; c-- ) { | |
217 DumpFrame(pb,trav); | |
218 prev = trav; | |
219 trav = trav->next; | |
220 av_free(prev); | |
221 } | |
222 flv->frames = trav; | |
223 } | |
224 | |
225 static int flv_write_trailer(AVFormatContext *s) | |
226 { | |
227 ByteIOContext *pb = &s->pb; | |
228 FLVContext *flv = s->priv_data; | |
229 | |
230 Dump(flv,pb,1); | |
231 | |
232 int64_t file_size = url_ftell(pb); | |
233 int flags = 0; | |
234 flags |= flv->hasAudio ? 4 : 0; | |
235 flags |= flv->hasVideo ? 1 : 0; | |
236 url_fseek(pb, 4, SEEK_SET); | |
237 put_byte(pb,flags); | |
238 url_fseek(pb, file_size, SEEK_SET); | |
239 return 0; | |
240 } | |
241 | |
242 static int flv_write_packet(AVFormatContext *s, int stream_index, | |
243 uint8_t *buf, int size, int timestamp) | |
244 { | |
245 ByteIOContext *pb = &s->pb; | |
246 AVCodecContext *enc = &s->streams[stream_index]->codec; | |
247 FLVContext *flv = s->priv_data; | |
248 | |
249 if (enc->codec_type == CODEC_TYPE_VIDEO) { | |
250 FLVFrame *frame = av_malloc(sizeof(FLVFrame)); | |
251 frame->next = 0; | |
252 frame->type = 9; | |
253 frame->flags = 2; // choose h263 | |
254 frame->flags |= enc->coded_frame->key_frame ? 0x10 : 0x20; // add keyframe indicator | |
255 frame->timestamp = timestamp; | |
256 //frame->timestamp = ( ( flv->frameCount * (int64_t)FRAME_RATE_BASE * (int64_t)1000 ) / (int64_t)enc->frame_rate ); | |
257 //printf("%08x %f %f\n",frame->timestamp,(double)enc->frame_rate/(double)FRAME_RATE_BASE,1000*(double)FRAME_RATE_BASE/(double)enc->frame_rate); | |
258 frame->size = size; | |
259 frame->data = av_malloc(size); | |
260 memcpy(frame->data,buf,size); | |
261 flv->hasVideo = 1; | |
262 | |
263 InsertSorted(flv,frame); | |
264 | |
265 flv->frameCount ++; | |
266 } | |
267 else if (enc->codec_type == CODEC_TYPE_AUDIO) { | |
268 #ifdef CONFIG_MP3LAME | |
269 if (enc->codec_id == CODEC_ID_MP3LAME ) { | |
270 int c=0; | |
271 for (;c<size;c++) { | |
272 flv->audioFifo[(flv->audioOutPos+c)%AUDIO_FIFO_SIZE] = buf[c]; | |
273 } | |
274 flv->audioSize += size; | |
275 flv->audioOutPos += size; | |
276 flv->audioOutPos %= AUDIO_FIFO_SIZE; | |
277 | |
278 if ( flv->initDelay == -1 ) { | |
279 flv->initDelay = timestamp; | |
280 } | |
281 | |
282 if ( flv->audioTime == -1 ) { | |
283 flv->audioTime = timestamp; | |
284 // flv->audioTime = ( ( ( flv->sampleCount - enc->delay ) * 8000 ) / flv->audioRate ) - flv->initDelay - 250; | |
285 // if ( flv->audioTime < 0 ) { | |
286 // flv->audioTime = 0; | |
287 // } | |
288 } | |
289 } | |
290 for ( ; flv->audioSize >= 4 ; ) { | |
291 | |
292 int mp3FrameSize = 0; | |
293 int mp3SampleRate = 0; | |
294 int mp3IsMono = 0; | |
295 int mp3SamplesPerFrame = 0; | |
296 | |
297 if ( mp3info(&flv->audioFifo[flv->audioInPos],&mp3FrameSize,&mp3SamplesPerFrame,&mp3SampleRate,&mp3IsMono) ) { | |
298 if ( flv->audioSize >= mp3FrameSize ) { | |
299 | |
300 int soundFormat = 0x22; | |
301 int c=0; | |
302 FLVFrame *frame = av_malloc(sizeof(FLVFrame)); | |
303 | |
304 flv->audioRate = mp3SampleRate; | |
305 | |
306 switch (mp3SampleRate) { | |
307 case 44100: | |
308 soundFormat |= 0x0C; | |
309 break; | |
310 case 22050: | |
311 soundFormat |= 0x08; | |
312 break; | |
313 case 11025: | |
314 soundFormat |= 0x04; | |
315 break; | |
316 } | |
317 | |
318 if ( !mp3IsMono ) { | |
319 soundFormat |= 0x01; | |
320 } | |
321 | |
322 frame->next = 0; | |
323 frame->type = 8; | |
324 frame->flags = soundFormat; | |
325 frame->timestamp = flv->audioTime; | |
326 frame->size = mp3FrameSize; | |
327 frame->data = av_malloc(mp3FrameSize); | |
328 | |
329 for (;c<mp3FrameSize;c++) { | |
330 frame->data[c] = flv->audioFifo[(flv->audioInPos+c)%AUDIO_FIFO_SIZE]; | |
331 } | |
332 | |
333 flv->audioInPos += mp3FrameSize; | |
334 flv->audioSize -= mp3FrameSize; | |
335 flv->audioInPos %= AUDIO_FIFO_SIZE; | |
336 flv->sampleCount += mp3SamplesPerFrame; | |
337 | |
338 // Reset audio for next round | |
339 flv->audioTime = -1; | |
340 // We got audio! Make sure we set this to the global flags on closure | |
341 flv->hasAudio = 1; | |
342 | |
343 InsertSorted(flv,frame); | |
344 } | |
345 break; | |
346 } | |
347 flv->audioInPos ++; | |
348 flv->audioSize --; | |
349 flv->audioInPos %= AUDIO_FIFO_SIZE; | |
350 // no audio in here! | |
351 flv->audioTime = -1; | |
352 } | |
353 #endif | |
354 } | |
355 Dump(flv,pb,128); | |
356 put_flush_packet(pb); | |
357 return 0; | |
358 } | |
359 | |
360 static AVOutputFormat flv_oformat = { | |
361 "flv", | |
362 "flv format", | |
363 "video/x-flashvideo", | |
364 "flv", | |
365 sizeof(FLVContext), | |
366 #ifdef CONFIG_MP3LAME | |
367 CODEC_ID_MP3LAME, | |
368 #else // CONFIG_MP3LAME | |
369 CODEC_ID_NONE, | |
370 #endif // CONFIG_MP3LAME | |
371 CODEC_ID_FLV1, | |
372 flv_write_header, | |
373 flv_write_packet, | |
374 flv_write_trailer, | |
375 }; | |
376 | |
377 int flvenc_init(void) | |
378 { | |
379 av_register_output_format(&flv_oformat); | |
380 return 0; | |
381 } |