Commit | Line | Data |
---|---|---|
8de65d4f FB |
1 | /* |
2 | * Libavformat API example: Output a media file in any supported | |
3 | * libavformat format. The default codecs are used. | |
4 | * | |
5 | * Copyright (c) 2003 Fabrice Bellard | |
6 | * | |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
25 | #include <stdlib.h> | |
26 | #include <stdio.h> | |
af547ce5 | 27 | #include <string.h> |
8de65d4f FB |
28 | #include <math.h> |
29 | ||
264cb2b8 MN |
30 | #ifndef M_PI |
31 | #define M_PI 3.1415926535897931 | |
32 | #endif | |
33 | ||
8de65d4f FB |
34 | #include "avformat.h" |
35 | ||
36 | /* 5 seconds stream duration */ | |
cb0c448a FB |
37 | #define STREAM_DURATION 5.0 |
38 | #define STREAM_FRAME_RATE 25 /* 25 images/s */ | |
39 | #define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) | |
8de65d4f FB |
40 | |
41 | /**************************************************************/ | |
42 | /* audio output */ | |
43 | ||
e70fcf07 | 44 | float t, tincr, tincr2; |
8de65d4f FB |
45 | int16_t *samples; |
46 | uint8_t *audio_outbuf; | |
47 | int audio_outbuf_size; | |
48 | int audio_input_frame_size; | |
49 | ||
50 | /* | |
51 | * add an audio output stream | |
52 | */ | |
53 | AVStream *add_audio_stream(AVFormatContext *oc, int codec_id) | |
54 | { | |
8de65d4f FB |
55 | AVCodecContext *c; |
56 | AVStream *st; | |
57 | ||
58 | st = av_new_stream(oc, 1); | |
59 | if (!st) { | |
60 | fprintf(stderr, "Could not alloc stream\n"); | |
61 | exit(1); | |
62 | } | |
63 | ||
8de65d4f | 64 | c = &st->codec; |
e70fcf07 | 65 | c->codec_id = codec_id; |
8de65d4f FB |
66 | c->codec_type = CODEC_TYPE_AUDIO; |
67 | ||
68 | /* put sample parameters */ | |
69 | c->bit_rate = 64000; | |
70 | c->sample_rate = 44100; | |
71 | c->channels = 2; | |
e70fcf07 FB |
72 | return st; |
73 | } | |
74 | ||
75 | void open_audio(AVFormatContext *oc, AVStream *st) | |
76 | { | |
77 | AVCodecContext *c; | |
78 | AVCodec *codec; | |
79 | ||
80 | c = &st->codec; | |
81 | ||
82 | /* find the audio encoder */ | |
83 | codec = avcodec_find_encoder(c->codec_id); | |
84 | if (!codec) { | |
85 | fprintf(stderr, "codec not found\n"); | |
86 | exit(1); | |
87 | } | |
8de65d4f FB |
88 | |
89 | /* open it */ | |
90 | if (avcodec_open(c, codec) < 0) { | |
91 | fprintf(stderr, "could not open codec\n"); | |
92 | exit(1); | |
93 | } | |
94 | ||
95 | /* init signal generator */ | |
96 | t = 0; | |
e70fcf07 FB |
97 | tincr = 2 * M_PI * 110.0 / c->sample_rate; |
98 | /* increment frequency by 110 Hz per second */ | |
99 | tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; | |
8de65d4f FB |
100 | |
101 | audio_outbuf_size = 10000; | |
102 | audio_outbuf = malloc(audio_outbuf_size); | |
103 | ||
104 | /* ugly hack for PCM codecs (will be removed ASAP with new PCM | |
105 | support to compute the input frame size in samples */ | |
106 | if (c->frame_size <= 1) { | |
107 | audio_input_frame_size = audio_outbuf_size / c->channels; | |
108 | switch(st->codec.codec_id) { | |
109 | case CODEC_ID_PCM_S16LE: | |
110 | case CODEC_ID_PCM_S16BE: | |
111 | case CODEC_ID_PCM_U16LE: | |
112 | case CODEC_ID_PCM_U16BE: | |
113 | audio_input_frame_size >>= 1; | |
114 | break; | |
115 | default: | |
116 | break; | |
117 | } | |
118 | } else { | |
119 | audio_input_frame_size = c->frame_size; | |
120 | } | |
121 | samples = malloc(audio_input_frame_size * 2 * c->channels); | |
8de65d4f FB |
122 | } |
123 | ||
cb750e33 FB |
124 | /* prepare a 16 bit dummy audio frame of 'frame_size' samples and |
125 | 'nb_channels' channels */ | |
126 | void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) | |
127 | { | |
128 | int j, i, v; | |
129 | int16_t *q; | |
130 | ||
131 | q = samples; | |
132 | for(j=0;j<frame_size;j++) { | |
133 | v = (int)(sin(t) * 10000); | |
134 | for(i = 0; i < nb_channels; i++) | |
135 | *q++ = v; | |
136 | t += tincr; | |
137 | tincr += tincr2; | |
138 | } | |
139 | } | |
140 | ||
8de65d4f FB |
141 | void write_audio_frame(AVFormatContext *oc, AVStream *st) |
142 | { | |
cb750e33 | 143 | int out_size; |
8de65d4f FB |
144 | AVCodecContext *c; |
145 | ||
146 | ||
147 | c = &st->codec; | |
148 | ||
cb750e33 FB |
149 | get_audio_frame(samples, audio_input_frame_size, c->channels); |
150 | ||
8de65d4f FB |
151 | out_size = avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples); |
152 | ||
153 | /* write the compressed frame in the media file */ | |
154 | if (av_write_frame(oc, st->index, audio_outbuf, out_size) != 0) { | |
155 | fprintf(stderr, "Error while writing audio frame\n"); | |
156 | exit(1); | |
157 | } | |
158 | } | |
159 | ||
e70fcf07 FB |
160 | void close_audio(AVFormatContext *oc, AVStream *st) |
161 | { | |
162 | avcodec_close(&st->codec); | |
163 | ||
164 | av_free(samples); | |
165 | av_free(audio_outbuf); | |
166 | } | |
167 | ||
8de65d4f FB |
168 | /**************************************************************/ |
169 | /* video output */ | |
170 | ||
e70fcf07 | 171 | AVFrame *picture, *tmp_picture; |
8de65d4f FB |
172 | uint8_t *video_outbuf; |
173 | int frame_count, video_outbuf_size; | |
174 | ||
175 | /* add a video output stream */ | |
176 | AVStream *add_video_stream(AVFormatContext *oc, int codec_id) | |
177 | { | |
8de65d4f FB |
178 | AVCodecContext *c; |
179 | AVStream *st; | |
8de65d4f FB |
180 | |
181 | st = av_new_stream(oc, 0); | |
182 | if (!st) { | |
183 | fprintf(stderr, "Could not alloc stream\n"); | |
184 | exit(1); | |
185 | } | |
186 | ||
8de65d4f | 187 | c = &st->codec; |
e70fcf07 | 188 | c->codec_id = codec_id; |
8de65d4f FB |
189 | c->codec_type = CODEC_TYPE_VIDEO; |
190 | ||
191 | /* put sample parameters */ | |
192 | c->bit_rate = 400000; | |
193 | /* resolution must be a multiple of two */ | |
194 | c->width = 352; | |
195 | c->height = 288; | |
196 | /* frames per second */ | |
cb0c448a FB |
197 | c->frame_rate = STREAM_FRAME_RATE; |
198 | c->frame_rate_base = 1; | |
199 | c->gop_size = 12; /* emit one intra frame every twelve frames at most */ | |
85e33747 | 200 | if (c->codec_id == CODEC_ID_MPEG2VIDEO) { |
cb0c448a FB |
201 | /* just for testing, we also add B frames */ |
202 | c->max_b_frames = 2; | |
203 | } | |
85e33747 MN |
204 | if (c->codec_id == CODEC_ID_MPEG1VIDEO){ |
205 | /* needed to avoid using macroblocks in which some coeffs overflow | |
206 | this doesnt happen with normal video, it just happens here as the | |
207 | motion of the chroma plane doesnt match the luma plane */ | |
208 | c->mb_decision=2; | |
209 | } | |
af547ce5 MN |
210 | // some formats want stream headers to be seperate |
211 | if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp")) | |
212 | c->flags |= CODEC_FLAG_GLOBAL_HEADER; | |
213 | ||
e70fcf07 FB |
214 | return st; |
215 | } | |
216 | ||
217 | AVFrame *alloc_picture(int pix_fmt, int width, int height) | |
218 | { | |
219 | AVFrame *picture; | |
220 | uint8_t *picture_buf; | |
221 | int size; | |
222 | ||
223 | picture = avcodec_alloc_frame(); | |
224 | if (!picture) | |
225 | return NULL; | |
226 | size = avpicture_get_size(pix_fmt, width, height); | |
227 | picture_buf = malloc(size); | |
228 | if (!picture_buf) { | |
229 | av_free(picture); | |
230 | return NULL; | |
231 | } | |
232 | avpicture_fill((AVPicture *)picture, picture_buf, | |
233 | pix_fmt, width, height); | |
234 | return picture; | |
235 | } | |
236 | ||
237 | void open_video(AVFormatContext *oc, AVStream *st) | |
238 | { | |
239 | AVCodec *codec; | |
240 | AVCodecContext *c; | |
241 | ||
242 | c = &st->codec; | |
243 | ||
244 | /* find the video encoder */ | |
245 | codec = avcodec_find_encoder(c->codec_id); | |
246 | if (!codec) { | |
247 | fprintf(stderr, "codec not found\n"); | |
248 | exit(1); | |
249 | } | |
250 | ||
251 | /* open the codec */ | |
8de65d4f FB |
252 | if (avcodec_open(c, codec) < 0) { |
253 | fprintf(stderr, "could not open codec\n"); | |
254 | exit(1); | |
255 | } | |
256 | ||
e70fcf07 FB |
257 | video_outbuf = NULL; |
258 | if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { | |
259 | /* allocate output buffer */ | |
260 | /* XXX: API change will be done */ | |
261 | video_outbuf_size = 200000; | |
262 | video_outbuf = malloc(video_outbuf_size); | |
263 | } | |
8de65d4f | 264 | |
e70fcf07 FB |
265 | /* allocate the encoded raw picture */ |
266 | picture = alloc_picture(c->pix_fmt, c->width, c->height); | |
267 | if (!picture) { | |
268 | fprintf(stderr, "Could not allocate picture\n"); | |
269 | exit(1); | |
270 | } | |
8de65d4f | 271 | |
e70fcf07 FB |
272 | /* if the output format is not YUV420P, then a temporary YUV420P |
273 | picture is needed too. It is then converted to the required | |
274 | output format */ | |
275 | tmp_picture = NULL; | |
276 | if (c->pix_fmt != PIX_FMT_YUV420P) { | |
277 | tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height); | |
278 | if (!tmp_picture) { | |
279 | fprintf(stderr, "Could not allocate temporary picture\n"); | |
280 | exit(1); | |
281 | } | |
282 | } | |
283 | } | |
8de65d4f | 284 | |
e70fcf07 FB |
285 | /* prepare a dummy image */ |
286 | void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height) | |
8de65d4f | 287 | { |
e70fcf07 FB |
288 | int x, y, i; |
289 | ||
290 | i = frame_index; | |
8de65d4f | 291 | |
8de65d4f | 292 | /* Y */ |
e70fcf07 FB |
293 | for(y=0;y<height;y++) { |
294 | for(x=0;x<width;x++) { | |
295 | pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; | |
8de65d4f FB |
296 | } |
297 | } | |
298 | ||
299 | /* Cb and Cr */ | |
e70fcf07 FB |
300 | for(y=0;y<height/2;y++) { |
301 | for(x=0;x<width/2;x++) { | |
302 | pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; | |
303 | pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; | |
8de65d4f FB |
304 | } |
305 | } | |
e70fcf07 | 306 | } |
8de65d4f | 307 | |
e70fcf07 FB |
308 | void write_video_frame(AVFormatContext *oc, AVStream *st) |
309 | { | |
310 | int out_size, ret; | |
311 | AVCodecContext *c; | |
cb0c448a FB |
312 | AVFrame *picture_ptr; |
313 | ||
e70fcf07 FB |
314 | c = &st->codec; |
315 | ||
cb0c448a FB |
316 | if (frame_count >= STREAM_NB_FRAMES) { |
317 | /* no more frame to compress. The codec has a latency of a few | |
318 | frames if using B frames, so we get the last frames by | |
319 | passing a NULL picture */ | |
320 | picture_ptr = NULL; | |
e70fcf07 | 321 | } else { |
cb0c448a FB |
322 | if (c->pix_fmt != PIX_FMT_YUV420P) { |
323 | /* as we only generate a YUV420P picture, we must convert it | |
324 | to the codec pixel format if needed */ | |
325 | fill_yuv_image(tmp_picture, frame_count, c->width, c->height); | |
326 | img_convert((AVPicture *)picture, c->pix_fmt, | |
327 | (AVPicture *)tmp_picture, PIX_FMT_YUV420P, | |
328 | c->width, c->height); | |
329 | } else { | |
330 | fill_yuv_image(picture, frame_count, c->width, c->height); | |
331 | } | |
332 | picture_ptr = picture; | |
e70fcf07 FB |
333 | } |
334 | ||
335 | ||
336 | if (oc->oformat->flags & AVFMT_RAWPICTURE) { | |
337 | /* raw video case. The API will change slightly in the near | |
338 | futur for that */ | |
339 | ret = av_write_frame(oc, st->index, | |
cb0c448a | 340 | (uint8_t *)picture_ptr, sizeof(AVPicture)); |
e70fcf07 FB |
341 | } else { |
342 | /* encode the image */ | |
cb0c448a FB |
343 | out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture_ptr); |
344 | /* if zero size, it means the image was buffered */ | |
345 | if (out_size != 0) { | |
346 | /* write the compressed frame in the media file */ | |
347 | /* XXX: in case of B frames, the pts is not yet valid */ | |
348 | ret = av_write_frame(oc, st->index, video_outbuf, out_size); | |
349 | } else { | |
350 | ret = 0; | |
351 | } | |
e70fcf07 FB |
352 | } |
353 | if (ret != 0) { | |
8de65d4f FB |
354 | fprintf(stderr, "Error while writing video frame\n"); |
355 | exit(1); | |
356 | } | |
e70fcf07 FB |
357 | frame_count++; |
358 | } | |
359 | ||
360 | void close_video(AVFormatContext *oc, AVStream *st) | |
361 | { | |
362 | avcodec_close(&st->codec); | |
363 | av_free(picture->data[0]); | |
364 | av_free(picture); | |
365 | if (tmp_picture) { | |
366 | av_free(tmp_picture->data[0]); | |
367 | av_free(tmp_picture); | |
368 | } | |
369 | av_free(video_outbuf); | |
8de65d4f FB |
370 | } |
371 | ||
372 | /**************************************************************/ | |
373 | /* media file output */ | |
374 | ||
375 | int main(int argc, char **argv) | |
376 | { | |
377 | const char *filename; | |
378 | AVOutputFormat *fmt; | |
379 | AVFormatContext *oc; | |
e70fcf07 | 380 | AVStream *audio_st, *video_st; |
8de65d4f | 381 | double audio_pts, video_pts; |
e70fcf07 FB |
382 | int i; |
383 | ||
8de65d4f FB |
384 | /* initialize libavcodec, and register all codecs and formats */ |
385 | av_register_all(); | |
386 | ||
387 | if (argc != 2) { | |
388 | printf("usage: %s output_file\n" | |
e70fcf07 FB |
389 | "API example program to output a media file with libavformat.\n" |
390 | "The output format is automatically guessed according to the file extension.\n" | |
391 | "Raw images can also be output by using '%%d' in the filename\n" | |
8de65d4f FB |
392 | "\n", argv[0]); |
393 | exit(1); | |
394 | } | |
395 | ||
396 | filename = argv[1]; | |
397 | ||
398 | /* auto detect the output format from the name. default is | |
399 | mpeg. */ | |
400 | fmt = guess_format(NULL, filename, NULL); | |
401 | if (!fmt) { | |
402 | printf("Could not deduce output format from file extension: using MPEG.\n"); | |
403 | fmt = guess_format("mpeg", NULL, NULL); | |
404 | } | |
405 | if (!fmt) { | |
406 | fprintf(stderr, "Could not find suitable output format\n"); | |
407 | exit(1); | |
408 | } | |
409 | ||
410 | /* allocate the output media context */ | |
bc874dae | 411 | oc = av_alloc_format_context(); |
8de65d4f FB |
412 | if (!oc) { |
413 | fprintf(stderr, "Memory error\n"); | |
414 | exit(1); | |
415 | } | |
416 | oc->oformat = fmt; | |
e70fcf07 | 417 | snprintf(oc->filename, sizeof(oc->filename), "%s", filename); |
8de65d4f FB |
418 | |
419 | /* add the audio and video streams using the default format codecs | |
420 | and initialize the codecs */ | |
421 | video_st = NULL; | |
422 | audio_st = NULL; | |
423 | if (fmt->video_codec != CODEC_ID_NONE) { | |
424 | video_st = add_video_stream(oc, fmt->video_codec); | |
425 | } | |
426 | if (fmt->audio_codec != CODEC_ID_NONE) { | |
427 | audio_st = add_audio_stream(oc, fmt->audio_codec); | |
428 | } | |
429 | ||
e70fcf07 FB |
430 | /* set the output parameters (must be done even if no |
431 | parameters). */ | |
432 | if (av_set_parameters(oc, NULL) < 0) { | |
433 | fprintf(stderr, "Invalid output format parameters\n"); | |
434 | exit(1); | |
435 | } | |
436 | ||
8de65d4f FB |
437 | dump_format(oc, 0, filename, 1); |
438 | ||
e70fcf07 FB |
439 | /* now that all the parameters are set, we can open the audio and |
440 | video codecs and allocate the necessary encode buffers */ | |
441 | if (video_st) | |
442 | open_video(oc, video_st); | |
443 | if (audio_st) | |
444 | open_audio(oc, audio_st); | |
445 | ||
8de65d4f FB |
446 | /* open the output file, if needed */ |
447 | if (!(fmt->flags & AVFMT_NOFILE)) { | |
448 | if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { | |
449 | fprintf(stderr, "Could not open '%s'\n", filename); | |
450 | exit(1); | |
451 | } | |
452 | } | |
453 | ||
8de65d4f FB |
454 | /* write the stream header, if any */ |
455 | av_write_header(oc); | |
456 | ||
457 | for(;;) { | |
458 | /* compute current audio and video time */ | |
459 | if (audio_st) | |
460 | audio_pts = (double)audio_st->pts.val * oc->pts_num / oc->pts_den; | |
461 | else | |
462 | audio_pts = 0.0; | |
463 | ||
464 | if (video_st) | |
465 | video_pts = (double)video_st->pts.val * oc->pts_num / oc->pts_den; | |
466 | else | |
467 | video_pts = 0.0; | |
468 | ||
469 | if ((!audio_st || audio_pts >= STREAM_DURATION) && | |
470 | (!video_st || video_pts >= STREAM_DURATION)) | |
471 | break; | |
472 | ||
473 | /* write interleaved audio and video frames */ | |
e70fcf07 | 474 | if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { |
8de65d4f FB |
475 | write_audio_frame(oc, audio_st); |
476 | } else { | |
477 | write_video_frame(oc, video_st); | |
478 | } | |
479 | } | |
480 | ||
481 | /* close each codec */ | |
e70fcf07 FB |
482 | if (video_st) |
483 | close_video(oc, video_st); | |
484 | if (audio_st) | |
485 | close_audio(oc, audio_st); | |
8de65d4f FB |
486 | |
487 | /* write the trailer, if any */ | |
488 | av_write_trailer(oc); | |
489 | ||
e70fcf07 FB |
490 | /* free the streams */ |
491 | for(i = 0; i < oc->nb_streams; i++) { | |
492 | av_freep(&oc->streams[i]); | |
493 | } | |
494 | ||
8de65d4f FB |
495 | if (!(fmt->flags & AVFMT_NOFILE)) { |
496 | /* close the output file */ | |
497 | url_fclose(&oc->pb); | |
498 | } | |
499 | ||
500 | /* free the stream */ | |
501 | av_free(oc); | |
502 | ||
503 | return 0; | |
504 | } |