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. | |
115329f1 | 4 | * |
8de65d4f | 5 | * Copyright (c) 2003 Fabrice Bellard |
115329f1 | 6 | * |
8de65d4f FB |
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: | |
115329f1 | 13 | * |
8de65d4f FB |
14 | * The above copyright notice and this permission notice shall be included in |
15 | * all copies or substantial portions of the Software. | |
115329f1 | 16 | * |
8de65d4f FB |
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 | |
115329f1 | 23 | * THE SOFTWARE. |
8de65d4f FB |
24 | */ |
25 | #include <stdlib.h> | |
26 | #include <stdio.h> | |
af547ce5 | 27 | #include <string.h> |
8de65d4f FB |
28 | #include <math.h> |
29 | ||
264cb2b8 | 30 | #ifndef M_PI |
a40de112 | 31 | #define M_PI 3.14159265358979323846 |
264cb2b8 MN |
32 | #endif |
33 | ||
245976da DB |
34 | #include "libavformat/avformat.h" |
35 | #include "libswscale/swscale.h" | |
8de65d4f | 36 | |
364a9607 DB |
37 | #undef exit |
38 | ||
8de65d4f | 39 | /* 5 seconds stream duration */ |
cb0c448a FB |
40 | #define STREAM_DURATION 5.0 |
41 | #define STREAM_FRAME_RATE 25 /* 25 images/s */ | |
42 | #define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) | |
002c01a6 | 43 | #define STREAM_PIX_FMT PIX_FMT_YUV420P /* default pix_fmt */ |
8de65d4f | 44 | |
03ae87a3 LA |
45 | static int sws_flags = SWS_BICUBIC; |
46 | ||
8de65d4f FB |
47 | /**************************************************************/ |
48 | /* audio output */ | |
49 | ||
e70fcf07 | 50 | float t, tincr, tincr2; |
8de65d4f FB |
51 | int16_t *samples; |
52 | uint8_t *audio_outbuf; | |
53 | int audio_outbuf_size; | |
54 | int audio_input_frame_size; | |
55 | ||
115329f1 | 56 | /* |
8de65d4f FB |
57 | * add an audio output stream |
58 | */ | |
ceaf1909 | 59 | static AVStream *add_audio_stream(AVFormatContext *oc, int codec_id) |
8de65d4f | 60 | { |
8de65d4f FB |
61 | AVCodecContext *c; |
62 | AVStream *st; | |
63 | ||
64 | st = av_new_stream(oc, 1); | |
65 | if (!st) { | |
66 | fprintf(stderr, "Could not alloc stream\n"); | |
67 | exit(1); | |
68 | } | |
69 | ||
01f4895c | 70 | c = st->codec; |
e70fcf07 | 71 | c->codec_id = codec_id; |
8de65d4f FB |
72 | c->codec_type = CODEC_TYPE_AUDIO; |
73 | ||
74 | /* put sample parameters */ | |
75 | c->bit_rate = 64000; | |
76 | c->sample_rate = 44100; | |
77 | c->channels = 2; | |
e70fcf07 FB |
78 | return st; |
79 | } | |
80 | ||
ceaf1909 | 81 | static void open_audio(AVFormatContext *oc, AVStream *st) |
e70fcf07 FB |
82 | { |
83 | AVCodecContext *c; | |
84 | AVCodec *codec; | |
85 | ||
01f4895c | 86 | c = st->codec; |
e70fcf07 FB |
87 | |
88 | /* find the audio encoder */ | |
89 | codec = avcodec_find_encoder(c->codec_id); | |
90 | if (!codec) { | |
91 | fprintf(stderr, "codec not found\n"); | |
92 | exit(1); | |
93 | } | |
8de65d4f FB |
94 | |
95 | /* open it */ | |
96 | if (avcodec_open(c, codec) < 0) { | |
97 | fprintf(stderr, "could not open codec\n"); | |
98 | exit(1); | |
99 | } | |
100 | ||
101 | /* init signal generator */ | |
102 | t = 0; | |
e70fcf07 FB |
103 | tincr = 2 * M_PI * 110.0 / c->sample_rate; |
104 | /* increment frequency by 110 Hz per second */ | |
105 | tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; | |
8de65d4f FB |
106 | |
107 | audio_outbuf_size = 10000; | |
5d915e82 | 108 | audio_outbuf = av_malloc(audio_outbuf_size); |
8de65d4f FB |
109 | |
110 | /* ugly hack for PCM codecs (will be removed ASAP with new PCM | |
111 | support to compute the input frame size in samples */ | |
112 | if (c->frame_size <= 1) { | |
113 | audio_input_frame_size = audio_outbuf_size / c->channels; | |
01f4895c | 114 | switch(st->codec->codec_id) { |
8de65d4f FB |
115 | case CODEC_ID_PCM_S16LE: |
116 | case CODEC_ID_PCM_S16BE: | |
117 | case CODEC_ID_PCM_U16LE: | |
118 | case CODEC_ID_PCM_U16BE: | |
119 | audio_input_frame_size >>= 1; | |
120 | break; | |
121 | default: | |
122 | break; | |
123 | } | |
124 | } else { | |
125 | audio_input_frame_size = c->frame_size; | |
126 | } | |
5d915e82 | 127 | samples = av_malloc(audio_input_frame_size * 2 * c->channels); |
8de65d4f FB |
128 | } |
129 | ||
cb750e33 FB |
130 | /* prepare a 16 bit dummy audio frame of 'frame_size' samples and |
131 | 'nb_channels' channels */ | |
ceaf1909 | 132 | static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) |
cb750e33 FB |
133 | { |
134 | int j, i, v; | |
135 | int16_t *q; | |
136 | ||
137 | q = samples; | |
138 | for(j=0;j<frame_size;j++) { | |
139 | v = (int)(sin(t) * 10000); | |
140 | for(i = 0; i < nb_channels; i++) | |
141 | *q++ = v; | |
142 | t += tincr; | |
143 | tincr += tincr2; | |
144 | } | |
145 | } | |
146 | ||
ceaf1909 | 147 | static void write_audio_frame(AVFormatContext *oc, AVStream *st) |
8de65d4f | 148 | { |
8de65d4f | 149 | AVCodecContext *c; |
e928649b MN |
150 | AVPacket pkt; |
151 | av_init_packet(&pkt); | |
115329f1 | 152 | |
01f4895c | 153 | c = st->codec; |
8de65d4f | 154 | |
cb750e33 FB |
155 | get_audio_frame(samples, audio_input_frame_size, c->channels); |
156 | ||
e928649b MN |
157 | pkt.size= avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples); |
158 | ||
fb34e75d | 159 | if (c->coded_frame->pts != AV_NOPTS_VALUE) |
817a3de9 | 160 | pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); |
e928649b MN |
161 | pkt.flags |= PKT_FLAG_KEY; |
162 | pkt.stream_index= st->index; | |
163 | pkt.data= audio_outbuf; | |
8de65d4f FB |
164 | |
165 | /* write the compressed frame in the media file */ | |
e928649b | 166 | if (av_write_frame(oc, &pkt) != 0) { |
8de65d4f FB |
167 | fprintf(stderr, "Error while writing audio frame\n"); |
168 | exit(1); | |
169 | } | |
170 | } | |
171 | ||
ceaf1909 | 172 | static void close_audio(AVFormatContext *oc, AVStream *st) |
e70fcf07 | 173 | { |
01f4895c | 174 | avcodec_close(st->codec); |
115329f1 | 175 | |
e70fcf07 FB |
176 | av_free(samples); |
177 | av_free(audio_outbuf); | |
178 | } | |
179 | ||
8de65d4f FB |
180 | /**************************************************************/ |
181 | /* video output */ | |
182 | ||
e70fcf07 | 183 | AVFrame *picture, *tmp_picture; |
8de65d4f FB |
184 | uint8_t *video_outbuf; |
185 | int frame_count, video_outbuf_size; | |
186 | ||
187 | /* add a video output stream */ | |
ceaf1909 | 188 | static AVStream *add_video_stream(AVFormatContext *oc, int codec_id) |
8de65d4f | 189 | { |
8de65d4f FB |
190 | AVCodecContext *c; |
191 | AVStream *st; | |
8de65d4f FB |
192 | |
193 | st = av_new_stream(oc, 0); | |
194 | if (!st) { | |
195 | fprintf(stderr, "Could not alloc stream\n"); | |
196 | exit(1); | |
197 | } | |
115329f1 | 198 | |
01f4895c | 199 | c = st->codec; |
e70fcf07 | 200 | c->codec_id = codec_id; |
8de65d4f FB |
201 | c->codec_type = CODEC_TYPE_VIDEO; |
202 | ||
203 | /* put sample parameters */ | |
204 | c->bit_rate = 400000; | |
205 | /* resolution must be a multiple of two */ | |
115329f1 | 206 | c->width = 352; |
8de65d4f | 207 | c->height = 288; |
5b28c8c3 MN |
208 | /* time base: this is the fundamental unit of time (in seconds) in terms |
209 | of which frame timestamps are represented. for fixed-fps content, | |
210 | timebase should be 1/framerate and timestamp increments should be | |
211 | identically 1. */ | |
115329f1 | 212 | c->time_base.den = STREAM_FRAME_RATE; |
c0df9d75 | 213 | c->time_base.num = 1; |
cb0c448a | 214 | c->gop_size = 12; /* emit one intra frame every twelve frames at most */ |
002c01a6 | 215 | c->pix_fmt = STREAM_PIX_FMT; |
85e33747 | 216 | if (c->codec_id == CODEC_ID_MPEG2VIDEO) { |
cb0c448a FB |
217 | /* just for testing, we also add B frames */ |
218 | c->max_b_frames = 2; | |
219 | } | |
85e33747 | 220 | if (c->codec_id == CODEC_ID_MPEG1VIDEO){ |
755bfeab DB |
221 | /* Needed to avoid using macroblocks in which some coeffs overflow. |
222 | This does not happen with normal video, it just happens here as | |
223 | the motion of the chroma plane does not match the luma plane. */ | |
85e33747 MN |
224 | c->mb_decision=2; |
225 | } | |
0afd2a92 | 226 | // some formats want stream headers to be separate |
af547ce5 MN |
227 | if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp")) |
228 | c->flags |= CODEC_FLAG_GLOBAL_HEADER; | |
115329f1 | 229 | |
e70fcf07 FB |
230 | return st; |
231 | } | |
232 | ||
ceaf1909 | 233 | static AVFrame *alloc_picture(int pix_fmt, int width, int height) |
e70fcf07 FB |
234 | { |
235 | AVFrame *picture; | |
236 | uint8_t *picture_buf; | |
237 | int size; | |
115329f1 | 238 | |
e70fcf07 FB |
239 | picture = avcodec_alloc_frame(); |
240 | if (!picture) | |
241 | return NULL; | |
242 | size = avpicture_get_size(pix_fmt, width, height); | |
5d915e82 | 243 | picture_buf = av_malloc(size); |
e70fcf07 FB |
244 | if (!picture_buf) { |
245 | av_free(picture); | |
246 | return NULL; | |
247 | } | |
115329f1 | 248 | avpicture_fill((AVPicture *)picture, picture_buf, |
e70fcf07 FB |
249 | pix_fmt, width, height); |
250 | return picture; | |
251 | } | |
115329f1 | 252 | |
ceaf1909 | 253 | static void open_video(AVFormatContext *oc, AVStream *st) |
e70fcf07 FB |
254 | { |
255 | AVCodec *codec; | |
256 | AVCodecContext *c; | |
257 | ||
01f4895c | 258 | c = st->codec; |
e70fcf07 FB |
259 | |
260 | /* find the video encoder */ | |
261 | codec = avcodec_find_encoder(c->codec_id); | |
262 | if (!codec) { | |
263 | fprintf(stderr, "codec not found\n"); | |
264 | exit(1); | |
265 | } | |
266 | ||
267 | /* open the codec */ | |
8de65d4f FB |
268 | if (avcodec_open(c, codec) < 0) { |
269 | fprintf(stderr, "could not open codec\n"); | |
270 | exit(1); | |
271 | } | |
272 | ||
e70fcf07 FB |
273 | video_outbuf = NULL; |
274 | if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { | |
275 | /* allocate output buffer */ | |
276 | /* XXX: API change will be done */ | |
5d915e82 RP |
277 | /* buffers passed into lav* can be allocated any way you prefer, |
278 | as long as they're aligned enough for the architecture, and | |
279 | they're freed appropriately (such as using av_free for buffers | |
280 | allocated with av_malloc) */ | |
e70fcf07 | 281 | video_outbuf_size = 200000; |
5d915e82 | 282 | video_outbuf = av_malloc(video_outbuf_size); |
e70fcf07 | 283 | } |
8de65d4f | 284 | |
e70fcf07 FB |
285 | /* allocate the encoded raw picture */ |
286 | picture = alloc_picture(c->pix_fmt, c->width, c->height); | |
287 | if (!picture) { | |
288 | fprintf(stderr, "Could not allocate picture\n"); | |
289 | exit(1); | |
290 | } | |
8de65d4f | 291 | |
e70fcf07 FB |
292 | /* if the output format is not YUV420P, then a temporary YUV420P |
293 | picture is needed too. It is then converted to the required | |
294 | output format */ | |
295 | tmp_picture = NULL; | |
296 | if (c->pix_fmt != PIX_FMT_YUV420P) { | |
297 | tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height); | |
298 | if (!tmp_picture) { | |
299 | fprintf(stderr, "Could not allocate temporary picture\n"); | |
300 | exit(1); | |
301 | } | |
302 | } | |
303 | } | |
8de65d4f | 304 | |
e70fcf07 | 305 | /* prepare a dummy image */ |
ceaf1909 | 306 | static void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height) |
8de65d4f | 307 | { |
e70fcf07 FB |
308 | int x, y, i; |
309 | ||
310 | i = frame_index; | |
8de65d4f | 311 | |
8de65d4f | 312 | /* Y */ |
e70fcf07 FB |
313 | for(y=0;y<height;y++) { |
314 | for(x=0;x<width;x++) { | |
315 | pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3; | |
8de65d4f FB |
316 | } |
317 | } | |
115329f1 | 318 | |
8de65d4f | 319 | /* Cb and Cr */ |
e70fcf07 FB |
320 | for(y=0;y<height/2;y++) { |
321 | for(x=0;x<width/2;x++) { | |
322 | pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2; | |
323 | pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; | |
8de65d4f FB |
324 | } |
325 | } | |
e70fcf07 | 326 | } |
8de65d4f | 327 | |
ceaf1909 | 328 | static void write_video_frame(AVFormatContext *oc, AVStream *st) |
e70fcf07 FB |
329 | { |
330 | int out_size, ret; | |
331 | AVCodecContext *c; | |
03ae87a3 | 332 | static struct SwsContext *img_convert_ctx; |
115329f1 | 333 | |
01f4895c | 334 | c = st->codec; |
115329f1 | 335 | |
cb0c448a FB |
336 | if (frame_count >= STREAM_NB_FRAMES) { |
337 | /* no more frame to compress. The codec has a latency of a few | |
338 | frames if using B frames, so we get the last frames by | |
002c01a6 | 339 | passing the same picture again */ |
e70fcf07 | 340 | } else { |
cb0c448a FB |
341 | if (c->pix_fmt != PIX_FMT_YUV420P) { |
342 | /* as we only generate a YUV420P picture, we must convert it | |
343 | to the codec pixel format if needed */ | |
03ae87a3 LA |
344 | if (img_convert_ctx == NULL) { |
345 | img_convert_ctx = sws_getContext(c->width, c->height, | |
346 | PIX_FMT_YUV420P, | |
347 | c->width, c->height, | |
348 | c->pix_fmt, | |
349 | sws_flags, NULL, NULL, NULL); | |
350 | if (img_convert_ctx == NULL) { | |
351 | fprintf(stderr, "Cannot initialize the conversion context\n"); | |
352 | exit(1); | |
353 | } | |
354 | } | |
cb0c448a | 355 | fill_yuv_image(tmp_picture, frame_count, c->width, c->height); |
03ae87a3 LA |
356 | sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, |
357 | 0, c->height, picture->data, picture->linesize); | |
cb0c448a FB |
358 | } else { |
359 | fill_yuv_image(picture, frame_count, c->width, c->height); | |
360 | } | |
e70fcf07 FB |
361 | } |
362 | ||
115329f1 | 363 | |
e70fcf07 FB |
364 | if (oc->oformat->flags & AVFMT_RAWPICTURE) { |
365 | /* raw video case. The API will change slightly in the near | |
366 | futur for that */ | |
e928649b MN |
367 | AVPacket pkt; |
368 | av_init_packet(&pkt); | |
115329f1 | 369 | |
e928649b MN |
370 | pkt.flags |= PKT_FLAG_KEY; |
371 | pkt.stream_index= st->index; | |
002c01a6 | 372 | pkt.data= (uint8_t *)picture; |
e928649b | 373 | pkt.size= sizeof(AVPicture); |
115329f1 | 374 | |
e928649b | 375 | ret = av_write_frame(oc, &pkt); |
e70fcf07 FB |
376 | } else { |
377 | /* encode the image */ | |
002c01a6 | 378 | out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); |
cb0c448a | 379 | /* if zero size, it means the image was buffered */ |
b5bc8591 | 380 | if (out_size > 0) { |
e928649b MN |
381 | AVPacket pkt; |
382 | av_init_packet(&pkt); | |
115329f1 | 383 | |
fb34e75d | 384 | if (c->coded_frame->pts != AV_NOPTS_VALUE) |
817a3de9 | 385 | pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); |
e928649b MN |
386 | if(c->coded_frame->key_frame) |
387 | pkt.flags |= PKT_FLAG_KEY; | |
388 | pkt.stream_index= st->index; | |
389 | pkt.data= video_outbuf; | |
390 | pkt.size= out_size; | |
115329f1 | 391 | |
cb0c448a | 392 | /* write the compressed frame in the media file */ |
e928649b | 393 | ret = av_write_frame(oc, &pkt); |
cb0c448a FB |
394 | } else { |
395 | ret = 0; | |
396 | } | |
e70fcf07 FB |
397 | } |
398 | if (ret != 0) { | |
8de65d4f FB |
399 | fprintf(stderr, "Error while writing video frame\n"); |
400 | exit(1); | |
401 | } | |
e70fcf07 FB |
402 | frame_count++; |
403 | } | |
404 | ||
ceaf1909 | 405 | static void close_video(AVFormatContext *oc, AVStream *st) |
e70fcf07 | 406 | { |
01f4895c | 407 | avcodec_close(st->codec); |
e70fcf07 FB |
408 | av_free(picture->data[0]); |
409 | av_free(picture); | |
410 | if (tmp_picture) { | |
411 | av_free(tmp_picture->data[0]); | |
412 | av_free(tmp_picture); | |
413 | } | |
414 | av_free(video_outbuf); | |
8de65d4f FB |
415 | } |
416 | ||
417 | /**************************************************************/ | |
418 | /* media file output */ | |
419 | ||
420 | int main(int argc, char **argv) | |
421 | { | |
422 | const char *filename; | |
423 | AVOutputFormat *fmt; | |
424 | AVFormatContext *oc; | |
e70fcf07 | 425 | AVStream *audio_st, *video_st; |
8de65d4f | 426 | double audio_pts, video_pts; |
e70fcf07 FB |
427 | int i; |
428 | ||
8de65d4f FB |
429 | /* initialize libavcodec, and register all codecs and formats */ |
430 | av_register_all(); | |
115329f1 | 431 | |
8de65d4f FB |
432 | if (argc != 2) { |
433 | printf("usage: %s output_file\n" | |
e70fcf07 FB |
434 | "API example program to output a media file with libavformat.\n" |
435 | "The output format is automatically guessed according to the file extension.\n" | |
436 | "Raw images can also be output by using '%%d' in the filename\n" | |
8de65d4f FB |
437 | "\n", argv[0]); |
438 | exit(1); | |
439 | } | |
115329f1 | 440 | |
8de65d4f FB |
441 | filename = argv[1]; |
442 | ||
443 | /* auto detect the output format from the name. default is | |
444 | mpeg. */ | |
445 | fmt = guess_format(NULL, filename, NULL); | |
446 | if (!fmt) { | |
447 | printf("Could not deduce output format from file extension: using MPEG.\n"); | |
448 | fmt = guess_format("mpeg", NULL, NULL); | |
449 | } | |
450 | if (!fmt) { | |
451 | fprintf(stderr, "Could not find suitable output format\n"); | |
452 | exit(1); | |
453 | } | |
115329f1 | 454 | |
8de65d4f | 455 | /* allocate the output media context */ |
bc874dae | 456 | oc = av_alloc_format_context(); |
8de65d4f FB |
457 | if (!oc) { |
458 | fprintf(stderr, "Memory error\n"); | |
459 | exit(1); | |
460 | } | |
461 | oc->oformat = fmt; | |
e70fcf07 | 462 | snprintf(oc->filename, sizeof(oc->filename), "%s", filename); |
8de65d4f FB |
463 | |
464 | /* add the audio and video streams using the default format codecs | |
465 | and initialize the codecs */ | |
466 | video_st = NULL; | |
467 | audio_st = NULL; | |
468 | if (fmt->video_codec != CODEC_ID_NONE) { | |
469 | video_st = add_video_stream(oc, fmt->video_codec); | |
470 | } | |
471 | if (fmt->audio_codec != CODEC_ID_NONE) { | |
472 | audio_st = add_audio_stream(oc, fmt->audio_codec); | |
473 | } | |
474 | ||
e70fcf07 FB |
475 | /* set the output parameters (must be done even if no |
476 | parameters). */ | |
477 | if (av_set_parameters(oc, NULL) < 0) { | |
478 | fprintf(stderr, "Invalid output format parameters\n"); | |
479 | exit(1); | |
480 | } | |
481 | ||
8de65d4f FB |
482 | dump_format(oc, 0, filename, 1); |
483 | ||
e70fcf07 FB |
484 | /* now that all the parameters are set, we can open the audio and |
485 | video codecs and allocate the necessary encode buffers */ | |
486 | if (video_st) | |
487 | open_video(oc, video_st); | |
488 | if (audio_st) | |
489 | open_audio(oc, audio_st); | |
490 | ||
8de65d4f FB |
491 | /* open the output file, if needed */ |
492 | if (!(fmt->flags & AVFMT_NOFILE)) { | |
493 | if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { | |
494 | fprintf(stderr, "Could not open '%s'\n", filename); | |
495 | exit(1); | |
496 | } | |
497 | } | |
115329f1 | 498 | |
8de65d4f FB |
499 | /* write the stream header, if any */ |
500 | av_write_header(oc); | |
115329f1 | 501 | |
8de65d4f FB |
502 | for(;;) { |
503 | /* compute current audio and video time */ | |
504 | if (audio_st) | |
e06d3d55 | 505 | audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; |
8de65d4f FB |
506 | else |
507 | audio_pts = 0.0; | |
115329f1 | 508 | |
8de65d4f | 509 | if (video_st) |
e06d3d55 | 510 | video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; |
8de65d4f FB |
511 | else |
512 | video_pts = 0.0; | |
513 | ||
115329f1 | 514 | if ((!audio_st || audio_pts >= STREAM_DURATION) && |
8de65d4f FB |
515 | (!video_st || video_pts >= STREAM_DURATION)) |
516 | break; | |
115329f1 | 517 | |
8de65d4f | 518 | /* write interleaved audio and video frames */ |
e70fcf07 | 519 | if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { |
8de65d4f FB |
520 | write_audio_frame(oc, audio_st); |
521 | } else { | |
522 | write_video_frame(oc, video_st); | |
523 | } | |
524 | } | |
525 | ||
526 | /* close each codec */ | |
e70fcf07 FB |
527 | if (video_st) |
528 | close_video(oc, video_st); | |
529 | if (audio_st) | |
530 | close_audio(oc, audio_st); | |
8de65d4f FB |
531 | |
532 | /* write the trailer, if any */ | |
533 | av_write_trailer(oc); | |
115329f1 | 534 | |
e70fcf07 FB |
535 | /* free the streams */ |
536 | for(i = 0; i < oc->nb_streams; i++) { | |
8720de5b | 537 | av_freep(&oc->streams[i]->codec); |
e70fcf07 FB |
538 | av_freep(&oc->streams[i]); |
539 | } | |
540 | ||
8de65d4f FB |
541 | if (!(fmt->flags & AVFMT_NOFILE)) { |
542 | /* close the output file */ | |
899681cd | 543 | url_fclose(oc->pb); |
8de65d4f FB |
544 | } |
545 | ||
546 | /* free the stream */ | |
547 | av_free(oc); | |
548 | ||
549 | return 0; | |
550 | } |