src/platform/ffmpeg/ffmpeg-encoder.c (view raw)
1#include "ffmpeg-encoder.h"
2
3#include "gba-video.h"
4
5#include <libavutil/imgutils.h>
6
7static void _ffmpegPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer);
8static void _ffmpegPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right);
9
10void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
11 av_register_all();
12
13 encoder->d.postVideoFrame = _ffmpegPostVideoFrame;
14 encoder->d.postAudioFrame = _ffmpegPostAudioFrame;
15
16 FFmpegEncoderSetAudio(encoder, "flac", 0);
17 FFmpegEncoderSetVideo(encoder, "png", 0);
18 encoder->currentAudioSample = 0;
19 encoder->currentAudioFrame = 0;
20 encoder->currentVideoFrame = 0;
21}
22
23bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, unsigned abr) {
24 if (!avcodec_find_encoder_by_name(acodec)) {
25 return false;
26 }
27 encoder->audioCodec = acodec;
28 encoder->audioBitrate = abr;
29 return true;
30}
31
32bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, unsigned vbr) {
33 AVCodec* codec = avcodec_find_encoder_by_name(vcodec);
34 if (!codec) {
35 return false;
36 }
37
38 size_t i;
39 encoder->pixFormat = AV_PIX_FMT_NONE;
40 for (i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; ++i) {
41 if (codec->pix_fmts[i] == AV_PIX_FMT_RGB24) {
42 encoder->pixFormat = AV_PIX_FMT_RGB24;
43 break;
44 }
45 if (codec->pix_fmts[i] == AV_PIX_FMT_BGR0) {
46 encoder->pixFormat = AV_PIX_FMT_BGR0;
47 }
48 }
49 if (encoder->pixFormat == AV_PIX_FMT_NONE) {
50 return false;
51 }
52 encoder->videoCodec = vcodec;
53 encoder->videoBitrate = vbr;
54 return true;
55}
56
57bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
58 AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
59 AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
60 if (!acodec || !vcodec) {
61 return false;
62 }
63
64 avformat_alloc_output_context2(&encoder->context, 0, 0, outfile);
65
66 encoder->audioStream = avformat_new_stream(encoder->context, acodec);
67 encoder->audio = encoder->audioStream->codec;
68 encoder->audio->bit_rate = encoder->audioBitrate;
69 encoder->audio->sample_rate = 0x8000;
70 encoder->audio->channels = 2;
71 encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO;
72 encoder->audio->sample_fmt = AV_SAMPLE_FMT_S16;
73 avcodec_open2(encoder->audio, acodec, 0);
74 encoder->audioFrame = av_frame_alloc();
75 encoder->audioFrame->nb_samples = encoder->audio->frame_size;
76 encoder->audioFrame->format = encoder->audio->sample_fmt;
77 encoder->audioFrame->pts = 0;
78 encoder->audioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0);
79 encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
80 avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->audioBuffer, encoder->audioBufferSize, 0);
81
82 encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
83 encoder->video = encoder->videoStream->codec;
84 encoder->video->bit_rate = encoder->videoBitrate;
85 encoder->video->width = VIDEO_HORIZONTAL_PIXELS;
86 encoder->video->height = VIDEO_VERTICAL_PIXELS;
87 encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY };
88 encoder->video->pix_fmt = encoder->pixFormat;
89 encoder->video->gop_size = 15;
90 encoder->video->max_b_frames = 0;
91 avcodec_open2(encoder->video, vcodec, 0);
92 encoder->videoFrame = av_frame_alloc();
93 encoder->videoFrame->format = encoder->video->pix_fmt;
94 encoder->videoFrame->width = encoder->video->width;
95 encoder->videoFrame->height = encoder->video->height;
96 encoder->videoFrame->pts = 0;
97 av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32);
98
99 if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) {
100 encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER;
101 encoder->video->flags |= CODEC_FLAG_GLOBAL_HEADER;
102 }
103
104 avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE);
105 avformat_write_header(encoder->context, 0);
106
107 return true;
108}
109
110void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
111 av_write_trailer(encoder->context);
112 avio_close(encoder->context->pb);
113
114 av_free(encoder->audioBuffer);
115 av_frame_free(&encoder->audioFrame);
116 avcodec_close(encoder->audio);
117
118 av_frame_free(&encoder->videoFrame);
119 avcodec_close(encoder->video);
120 avformat_free_context(encoder->context);
121}
122
123void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
124 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
125
126 av_frame_make_writable(encoder->audioFrame);
127 encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
128 encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
129 encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base);
130 ++encoder->currentAudioFrame;
131 ++encoder->currentAudioSample;
132
133 if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) {
134 return;
135 }
136 encoder->currentAudioSample = 0;
137
138 AVPacket packet;
139 av_init_packet(&packet);
140 packet.data = 0;
141 packet.size = 0;
142 int gotData;
143 avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData);
144 if (gotData) {
145 packet.stream_index = encoder->audioStream->index;
146 av_interleaved_write_frame(encoder->context, &packet);
147 }
148 av_free_packet(&packet);
149}
150
151void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
152 struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
153 uint32_t* pixels;
154 unsigned stride;
155 renderer->getPixels(renderer, &stride, (void**) &pixels);
156
157 AVPacket packet;
158
159 av_init_packet(&packet);
160 packet.data = 0;
161 packet.size = 0;
162 av_frame_make_writable(encoder->videoFrame);
163 encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base);
164 ++encoder->currentVideoFrame;
165
166 unsigned x, y;
167 if (encoder->videoFrame->format == AV_PIX_FMT_BGR0) {
168 for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
169 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
170 uint32_t pixel = pixels[stride * y + x];
171 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4] = pixel >> 16;
172 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 1] = pixel >> 8;
173 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 2] = pixel;
174 }
175 }
176 } else if (encoder->videoFrame->format == AV_PIX_FMT_RGB24) {
177 for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) {
178 for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) {
179 uint32_t pixel = pixels[stride * y + x];
180 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3] = pixel;
181 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 1] = pixel >> 8;
182 encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 2] = pixel >> 16;
183 }
184 }
185 }
186
187 int gotData;
188 avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData);
189 if (gotData) {
190 if (encoder->videoStream->codec->coded_frame->key_frame) {
191 packet.flags |= AV_PKT_FLAG_KEY;
192 }
193 packet.stream_index = encoder->videoStream->index;
194 av_interleaved_write_frame(encoder->context, &packet);
195 }
196 av_free_packet(&packet);
197}