src/feature/ffmpeg/ffmpeg-encoder.c (view raw)
1/* Copyright (c) 2013-2015 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "ffmpeg-encoder.h"
7
8#include <mgba/core/core.h>
9#include <mgba/internal/gba/video.h>
10
11#include <libavcodec/version.h>
12#include <libavcodec/avcodec.h>
13
14#include <libavutil/version.h>
15#if LIBAVUTIL_VERSION_MAJOR >= 53
16#include <libavutil/buffer.h>
17#endif
18#include <libavutil/imgutils.h>
19#include <libavutil/mathematics.h>
20#include <libavutil/opt.h>
21
22#include <libavresample/avresample.h>
23#include <libswscale/swscale.h>
24
25static void _ffmpegPostVideoFrame(struct mAVStream*, const color_t* pixels, size_t stride);
26static void _ffmpegPostAudioFrame(struct mAVStream*, int16_t left, int16_t right);
27static void _ffmpegSetVideoDimensions(struct mAVStream*, unsigned width, unsigned height);
28static void _ffmpegSetVideoFrameRate(struct mAVStream*, unsigned numerator, unsigned denominator);
29
30enum {
31 PREFERRED_SAMPLE_RATE = 0x8000
32};
33
34void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
35 av_register_all();
36
37 encoder->d.videoDimensionsChanged = _ffmpegSetVideoDimensions;
38 encoder->d.postVideoFrame = _ffmpegPostVideoFrame;
39 encoder->d.postAudioFrame = _ffmpegPostAudioFrame;
40 encoder->d.postAudioBuffer = 0;
41 encoder->d.videoFrameRateChanged = _ffmpegSetVideoFrameRate;
42
43 encoder->audioCodec = 0;
44 encoder->videoCodec = 0;
45 encoder->audio = NULL;
46 encoder->video = NULL;
47 encoder->containerFormat = 0;
48 FFmpegEncoderSetAudio(encoder, "flac", 0);
49 FFmpegEncoderSetVideo(encoder, "png", 0);
50 FFmpegEncoderSetContainer(encoder, "matroska");
51 FFmpegEncoderSetDimensions(encoder, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
52 encoder->iwidth = VIDEO_HORIZONTAL_PIXELS;
53 encoder->iheight = VIDEO_VERTICAL_PIXELS;
54 encoder->frameCycles = VIDEO_TOTAL_LENGTH;
55 encoder->cycles = GBA_ARM7TDMI_FREQUENCY;
56 encoder->resampleContext = 0;
57 encoder->absf = 0;
58 encoder->context = 0;
59 encoder->scaleContext = NULL;
60}
61
62bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, unsigned abr) {
63 static const struct {
64 int format;
65 int priority;
66 } priorities[] = {
67 { AV_SAMPLE_FMT_S16, 0 },
68 { AV_SAMPLE_FMT_S16P, 1 },
69 { AV_SAMPLE_FMT_S32, 2 },
70 { AV_SAMPLE_FMT_S32P, 2 },
71 { AV_SAMPLE_FMT_FLT, 3 },
72 { AV_SAMPLE_FMT_FLTP, 3 },
73 { AV_SAMPLE_FMT_DBL, 4 },
74 { AV_SAMPLE_FMT_DBLP, 4 }
75 };
76
77 if (!acodec) {
78 encoder->audioCodec = 0;
79 return true;
80 }
81
82 AVCodec* codec = avcodec_find_encoder_by_name(acodec);
83 if (!codec) {
84 return false;
85 }
86
87 if (!codec->sample_fmts) {
88 return false;
89 }
90 size_t i;
91 size_t j;
92 int priority = INT_MAX;
93 encoder->sampleFormat = AV_SAMPLE_FMT_NONE;
94 for (i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i) {
95 for (j = 0; j < sizeof(priorities) / sizeof(*priorities); ++j) {
96 if (codec->sample_fmts[i] == priorities[j].format && priority > priorities[j].priority) {
97 priority = priorities[j].priority;
98 encoder->sampleFormat = codec->sample_fmts[i];
99 }
100 }
101 }
102 if (encoder->sampleFormat == AV_SAMPLE_FMT_NONE) {
103 return false;
104 }
105 encoder->sampleRate = PREFERRED_SAMPLE_RATE;
106 if (codec->supported_samplerates) {
107 for (i = 0; codec->supported_samplerates[i]; ++i) {
108 if (codec->supported_samplerates[i] < PREFERRED_SAMPLE_RATE) {
109 continue;
110 }
111 if (encoder->sampleRate == PREFERRED_SAMPLE_RATE || encoder->sampleRate > codec->supported_samplerates[i]) {
112 encoder->sampleRate = codec->supported_samplerates[i];
113 }
114 }
115 } else if (codec->id == AV_CODEC_ID_AAC) {
116 // HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
117 encoder->sampleRate = 44100;
118 }
119 encoder->audioCodec = acodec;
120 encoder->audioBitrate = abr;
121 return true;
122}
123
124bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, unsigned vbr) {
125 static const struct {
126 enum AVPixelFormat format;
127 int priority;
128 } priorities[] = {
129 { AV_PIX_FMT_RGB555, 0 },
130 { AV_PIX_FMT_BGR555, 0 },
131 { AV_PIX_FMT_RGB565, 1 },
132 { AV_PIX_FMT_BGR565, 1 },
133 { AV_PIX_FMT_RGB24, 2 },
134 { AV_PIX_FMT_BGR24, 2 },
135#ifndef USE_LIBAV
136 { AV_PIX_FMT_BGR0, 3 },
137 { AV_PIX_FMT_RGB0, 3 },
138 { AV_PIX_FMT_0BGR, 3 },
139 { AV_PIX_FMT_0RGB, 3 },
140#endif
141 { AV_PIX_FMT_YUV422P, 4 },
142 { AV_PIX_FMT_YUV444P, 5 },
143 { AV_PIX_FMT_YUV420P, 6 }
144 };
145 AVCodec* codec = avcodec_find_encoder_by_name(vcodec);
146 if (!codec) {
147 return false;
148 }
149
150 size_t i;
151 size_t j;
152 int priority = INT_MAX;
153 encoder->pixFormat = AV_PIX_FMT_NONE;
154 for (i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; ++i) {
155 for (j = 0; j < sizeof(priorities) / sizeof(*priorities); ++j) {
156 if (codec->pix_fmts[i] == priorities[j].format && priority > priorities[j].priority) {
157 priority = priorities[j].priority;
158 encoder->pixFormat = codec->pix_fmts[i];
159 }
160 }
161 }
162 if (encoder->pixFormat == AV_PIX_FMT_NONE) {
163 return false;
164 }
165 encoder->videoCodec = vcodec;
166 encoder->videoBitrate = vbr;
167 return true;
168}
169
170bool FFmpegEncoderSetContainer(struct FFmpegEncoder* encoder, const char* container) {
171 AVOutputFormat* oformat = av_guess_format(container, 0, 0);
172 if (!oformat) {
173 return false;
174 }
175 encoder->containerFormat = container;
176 return true;
177}
178
179void FFmpegEncoderSetDimensions(struct FFmpegEncoder* encoder, int width, int height) {
180 encoder->width = width > 0 ? width : VIDEO_HORIZONTAL_PIXELS;
181 encoder->height = height > 0 ? height : VIDEO_VERTICAL_PIXELS;
182}
183
184bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
185 AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
186 AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
187 AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
188 if ((encoder->audioCodec && !acodec) || !vcodec || !oformat) {
189 return false;
190 }
191 if (encoder->audioCodec && !avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
192 return false;
193 }
194 if (!avformat_query_codec(oformat, vcodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
195 return false;
196 }
197 return true;
198}
199
200bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
201 AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
202 AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
203 if ((encoder->audioCodec && !acodec) || !vcodec || !FFmpegEncoderVerifyContainer(encoder)) {
204 return false;
205 }
206
207 encoder->currentAudioSample = 0;
208 encoder->currentAudioFrame = 0;
209 encoder->currentVideoFrame = 0;
210 encoder->nextAudioPts = 0;
211
212 AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
213#ifndef USE_LIBAV
214 avformat_alloc_output_context2(&encoder->context, oformat, 0, outfile);
215#else
216 encoder->context = avformat_alloc_context();
217 strncpy(encoder->context->filename, outfile, sizeof(encoder->context->filename) - 1);
218 encoder->context->filename[sizeof(encoder->context->filename) - 1] = '\0';
219 encoder->context->oformat = oformat;
220#endif
221
222 if (acodec) {
223#ifdef FFMPEG_USE_CODECPAR
224 encoder->audioStream = avformat_new_stream(encoder->context, NULL);
225 encoder->audio = avcodec_alloc_context3(acodec);
226#else
227 encoder->audioStream = avformat_new_stream(encoder->context, acodec);
228 encoder->audio = encoder->audioStream->codec;
229#endif
230 encoder->audio->bit_rate = encoder->audioBitrate;
231 encoder->audio->channels = 2;
232 encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO;
233 encoder->audio->sample_rate = encoder->sampleRate;
234 encoder->audio->sample_fmt = encoder->sampleFormat;
235 AVDictionary* opts = 0;
236 av_dict_set(&opts, "strict", "-2", 0);
237 if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) {
238#ifdef AV_CODEC_FLAG_GLOBAL_HEADER
239 encoder->audio->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
240#else
241 encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER;
242#endif
243 }
244 avcodec_open2(encoder->audio, acodec, &opts);
245 av_dict_free(&opts);
246#if LIBAVCODEC_VERSION_MAJOR >= 55
247 encoder->audioFrame = av_frame_alloc();
248#else
249 encoder->audioFrame = avcodec_alloc_frame();
250#endif
251 if (!encoder->audio->frame_size) {
252 encoder->audio->frame_size = 1;
253 }
254 encoder->audioFrame->nb_samples = encoder->audio->frame_size;
255 encoder->audioFrame->format = encoder->audio->sample_fmt;
256 encoder->audioFrame->pts = 0;
257 encoder->resampleContext = avresample_alloc_context();
258 av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
259 av_opt_set_int(encoder->resampleContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
260 av_opt_set_int(encoder->resampleContext, "in_sample_rate", PREFERRED_SAMPLE_RATE, 0);
261 av_opt_set_int(encoder->resampleContext, "out_sample_rate", encoder->sampleRate, 0);
262 av_opt_set_int(encoder->resampleContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
263 av_opt_set_int(encoder->resampleContext, "out_sample_fmt", encoder->sampleFormat, 0);
264 avresample_open(encoder->resampleContext);
265 encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4;
266 encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
267 encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0);
268 encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize);
269 avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0);
270
271 if (encoder->audio->codec->id == AV_CODEC_ID_AAC &&
272 (strcasecmp(encoder->containerFormat, "mp4") ||
273 strcasecmp(encoder->containerFormat, "m4v") ||
274 strcasecmp(encoder->containerFormat, "mov"))) {
275 // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out
276#ifdef FFMPEG_USE_NEW_BSF
277 av_bsf_alloc(av_bsf_get_by_name("aac_adtstoasc"), &encoder->absf);
278 avcodec_parameters_from_context(encoder->absf->par_in, encoder->audio);
279 av_bsf_init(encoder->absf);
280#else
281 encoder->absf = av_bitstream_filter_init("aac_adtstoasc");
282#endif
283 }
284#ifdef FFMPEG_USE_CODECPAR
285 avcodec_parameters_from_context(encoder->audioStream->codecpar, encoder->audio);
286#endif
287 }
288
289#ifdef FFMPEG_USE_CODECPAR
290 encoder->videoStream = avformat_new_stream(encoder->context, NULL);
291 encoder->video = avcodec_alloc_context3(vcodec);
292#else
293 encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
294 encoder->video = encoder->videoStream->codec;
295#endif
296 encoder->video->bit_rate = encoder->videoBitrate;
297 encoder->video->width = encoder->width;
298 encoder->video->height = encoder->height;
299 encoder->videoStream->time_base = (AVRational) { encoder->frameCycles, encoder->cycles };
300 encoder->video->time_base = encoder->videoStream->time_base;
301 encoder->video->pix_fmt = encoder->pixFormat;
302 encoder->video->gop_size = 60;
303 encoder->video->max_b_frames = 3;
304 if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) {
305#ifdef AV_CODEC_FLAG_GLOBAL_HEADER
306 encoder->video->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
307#else
308 encoder->video->flags |= CODEC_FLAG_GLOBAL_HEADER;
309#endif
310 }
311 if (strcmp(vcodec->name, "libx264") == 0) {
312 // Try to adaptively figure out when you can use a slower encoder
313 if (encoder->width * encoder->height > 1000000) {
314 av_opt_set(encoder->video->priv_data, "preset", "superfast", 0);
315 } else if (encoder->width * encoder->height > 500000) {
316 av_opt_set(encoder->video->priv_data, "preset", "veryfast", 0);
317 } else {
318 av_opt_set(encoder->video->priv_data, "preset", "faster", 0);
319 }
320 av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 0);
321 }
322
323 if (encoder->video->codec->id == AV_CODEC_ID_H264 &&
324 (strcasecmp(encoder->containerFormat, "mp4") ||
325 strcasecmp(encoder->containerFormat, "m4v") ||
326 strcasecmp(encoder->containerFormat, "mov"))) {
327 // QuickTime and a few other things require YUV420
328 encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
329 }
330 avcodec_open2(encoder->video, vcodec, 0);
331#if LIBAVCODEC_VERSION_MAJOR >= 55
332 encoder->videoFrame = av_frame_alloc();
333#else
334 encoder->videoFrame = avcodec_alloc_frame();
335#endif
336 encoder->videoFrame->format = encoder->video->pix_fmt;
337 encoder->videoFrame->width = encoder->video->width;
338 encoder->videoFrame->height = encoder->video->height;
339 encoder->videoFrame->pts = 0;
340 _ffmpegSetVideoDimensions(&encoder->d, encoder->iwidth, encoder->iheight);
341 av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32);
342#ifdef FFMPEG_USE_CODECPAR
343 avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
344#endif
345
346 if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0) {
347 return false;
348 }
349 return avformat_write_header(encoder->context, 0) >= 0;
350}
351
352void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
353 if (!encoder->context) {
354 return;
355 }
356 av_write_trailer(encoder->context);
357 avio_close(encoder->context->pb);
358
359 if (encoder->audioCodec) {
360 av_free(encoder->postaudioBuffer);
361 if (encoder->audioBuffer) {
362 av_free(encoder->audioBuffer);
363 }
364#if LIBAVCODEC_VERSION_MAJOR >= 55
365 av_frame_free(&encoder->audioFrame);
366#else
367 avcodec_free_frame(&encoder->audioFrame);
368#endif
369 avcodec_close(encoder->audio);
370 encoder->audio = NULL;
371
372 if (encoder->resampleContext) {
373 avresample_close(encoder->resampleContext);
374 }
375
376 if (encoder->absf) {
377#ifdef FFMPEG_USE_NEW_BSF
378 av_bsf_free(&encoder->absf);
379#else
380 av_bitstream_filter_close(encoder->absf);
381 encoder->absf = 0;
382#endif
383 }
384 }
385
386#if LIBAVCODEC_VERSION_MAJOR >= 55
387 av_frame_free(&encoder->videoFrame);
388#else
389 avcodec_free_frame(&encoder->videoFrame);
390#endif
391 avcodec_close(encoder->video);
392 encoder->video = NULL;
393
394 sws_freeContext(encoder->scaleContext);
395 encoder->scaleContext = NULL;
396
397 avformat_free_context(encoder->context);
398 encoder->context = 0;
399}
400
401bool FFmpegEncoderIsOpen(struct FFmpegEncoder* encoder) {
402 return !!encoder->context;
403}
404
405void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right) {
406 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
407 if (!encoder->context || !encoder->audioCodec) {
408 return;
409 }
410
411 if (encoder->absf && !left) {
412 // XXX: AVBSF doesn't like silence. Figure out why.
413 left = 1;
414 }
415
416 encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
417 encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
418
419 ++encoder->currentAudioSample;
420
421 if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) {
422 return;
423 }
424
425 int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
426 avresample_convert(encoder->resampleContext, 0, 0, 0,
427 (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
428
429 encoder->currentAudioSample = 0;
430 if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
431 return;
432 }
433#if LIBAVCODEC_VERSION_MAJOR >= 55
434 av_frame_make_writable(encoder->audioFrame);
435#endif
436 int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
437
438 encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base);
439 encoder->currentAudioFrame += samples;
440
441 AVPacket packet;
442 av_init_packet(&packet);
443 packet.data = 0;
444 packet.size = 0;
445 packet.pts = encoder->audioFrame->pts;
446
447 int gotData;
448#ifdef FFMPEG_USE_PACKETS
449 avcodec_send_frame(encoder->audio, encoder->audioFrame);
450 gotData = avcodec_receive_packet(encoder->audio, &packet);
451 gotData = (gotData == 0) && packet.size;
452#else
453 avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData);
454#endif
455 if (gotData) {
456 if (encoder->absf) {
457 AVPacket tempPacket;
458
459#ifdef FFMPEG_USE_NEW_BSF
460 int success = av_bsf_send_packet(encoder->absf, &packet);
461 if (success >= 0) {
462 success = av_bsf_receive_packet(encoder->absf, &tempPacket);
463 }
464#else
465 int success = av_bitstream_filter_filter(encoder->absf, encoder->audio, 0,
466 &tempPacket.data, &tempPacket.size,
467 packet.data, packet.size, 0);
468#endif
469
470 if (success >= 0) {
471#if LIBAVUTIL_VERSION_MAJOR >= 53
472 tempPacket.buf = av_buffer_create(tempPacket.data, tempPacket.size, av_buffer_default_free, 0, 0);
473#endif
474
475#ifdef FFMPEG_USE_PACKET_UNREF
476 av_packet_move_ref(&packet, &tempPacket);
477#else
478 av_free_packet(&packet);
479 packet = tempPacket;
480#endif
481
482 packet.stream_index = encoder->audioStream->index;
483 av_interleaved_write_frame(encoder->context, &packet);
484 }
485 } else {
486 packet.stream_index = encoder->audioStream->index;
487 av_interleaved_write_frame(encoder->context, &packet);
488 }
489 }
490#ifdef FFMPEG_USE_PACKET_UNREF
491 av_packet_unref(&packet);
492#else
493 av_free_packet(&packet);
494#endif
495}
496
497void _ffmpegPostVideoFrame(struct mAVStream* stream, const color_t* pixels, size_t stride) {
498 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
499 if (!encoder->context) {
500 return;
501 }
502 stride *= BYTES_PER_PIXEL;
503
504 AVPacket packet;
505
506 av_init_packet(&packet);
507 packet.data = 0;
508 packet.size = 0;
509#if LIBAVCODEC_VERSION_MAJOR >= 55
510 av_frame_make_writable(encoder->videoFrame);
511#endif
512 encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base);
513 packet.pts = encoder->videoFrame->pts;
514 ++encoder->currentVideoFrame;
515
516 sws_scale(encoder->scaleContext, (const uint8_t* const*) &pixels, (const int*) &stride, 0, encoder->iheight, encoder->videoFrame->data, encoder->videoFrame->linesize);
517
518 int gotData;
519#ifdef FFMPEG_USE_PACKETS
520 avcodec_send_frame(encoder->video, encoder->videoFrame);
521 gotData = avcodec_receive_packet(encoder->video, &packet) == 0;
522#else
523 avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData);
524#endif
525 if (gotData) {
526#ifndef FFMPEG_USE_PACKET_UNREF
527 if (encoder->video->coded_frame->key_frame) {
528 packet.flags |= AV_PKT_FLAG_KEY;
529 }
530#endif
531 packet.stream_index = encoder->videoStream->index;
532 av_interleaved_write_frame(encoder->context, &packet);
533 }
534#ifdef FFMPEG_USE_PACKET_UNREF
535 av_packet_unref(&packet);
536#else
537 av_free_packet(&packet);
538#endif
539}
540
541static void _ffmpegSetVideoDimensions(struct mAVStream* stream, unsigned width, unsigned height) {
542 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
543 encoder->iwidth = width;
544 encoder->iheight = height;
545 if (encoder->video) {
546 if (encoder->scaleContext) {
547 sws_freeContext(encoder->scaleContext);
548 }
549 encoder->scaleContext = sws_getContext(encoder->iwidth, encoder->iheight,
550#ifdef COLOR_16_BIT
551#ifdef COLOR_5_6_5
552 AV_PIX_FMT_RGB565,
553#else
554 AV_PIX_FMT_BGR555,
555#endif
556#else
557#ifndef USE_LIBAV
558 AV_PIX_FMT_0BGR32,
559#else
560 AV_PIX_FMT_BGR32,
561#endif
562#endif
563 encoder->videoFrame->width, encoder->videoFrame->height, encoder->video->pix_fmt,
564 SWS_POINT, 0, 0, 0);
565 }
566}
567
568static void _ffmpegSetVideoFrameRate(struct mAVStream* stream, unsigned numerator, unsigned denominator) {
569 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
570 FFmpegEncoderSetInputFrameRate(encoder, numerator, denominator);
571}
572
573void FFmpegEncoderSetInputFrameRate(struct FFmpegEncoder* encoder, unsigned numerator, unsigned denominator) {
574 encoder->frameCycles = numerator;
575 encoder->cycles = denominator;
576 if (encoder->video) {
577 encoder->video->time_base = (AVRational) { numerator, denominator };
578 }
579}