mirror of https://github.com/mgba-emu/mgba.git
Enhance usability and resiliency of ffmpeg encoder
This commit is contained in:
parent
21ea47ea9e
commit
281f190ae6
|
@ -13,11 +13,16 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
|
||||||
encoder->d.postVideoFrame = _ffmpegPostVideoFrame;
|
encoder->d.postVideoFrame = _ffmpegPostVideoFrame;
|
||||||
encoder->d.postAudioFrame = _ffmpegPostAudioFrame;
|
encoder->d.postAudioFrame = _ffmpegPostAudioFrame;
|
||||||
|
|
||||||
|
encoder->audioCodec = 0;
|
||||||
|
encoder->videoCodec = 0;
|
||||||
|
encoder->containerFormat = 0;
|
||||||
FFmpegEncoderSetAudio(encoder, "flac", 0);
|
FFmpegEncoderSetAudio(encoder, "flac", 0);
|
||||||
FFmpegEncoderSetVideo(encoder, "png", 0);
|
FFmpegEncoderSetVideo(encoder, "png", 0);
|
||||||
|
FFmpegEncoderSetContainer(encoder, "matroska");
|
||||||
encoder->currentAudioSample = 0;
|
encoder->currentAudioSample = 0;
|
||||||
encoder->currentAudioFrame = 0;
|
encoder->currentAudioFrame = 0;
|
||||||
encoder->currentVideoFrame = 0;
|
encoder->currentVideoFrame = 0;
|
||||||
|
encoder->context = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, unsigned abr) {
|
bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, unsigned abr) {
|
||||||
|
@ -54,15 +59,42 @@ bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, un
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FFmpegEncoderSetContainer(struct FFmpegEncoder* encoder, const char* container) {
|
||||||
|
AVOutputFormat* oformat = av_guess_format(container, 0, 0);
|
||||||
|
if (!oformat) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
encoder->containerFormat = container;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
|
||||||
|
AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
|
||||||
|
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
|
||||||
|
AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
|
||||||
|
if (!acodec || !vcodec || !oformat) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!avformat_query_codec(oformat, vcodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
||||||
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
|
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
|
||||||
AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
|
AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
|
||||||
if (!acodec || !vcodec) {
|
if (!acodec || !vcodec || !FFmpegEncoderVerifyContainer(encoder)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
avformat_alloc_output_context2(&encoder->context, 0, 0, outfile);
|
avformat_alloc_output_context2(&encoder->context, 0, 0, outfile);
|
||||||
|
|
||||||
|
encoder->context->oformat = av_guess_format(encoder->containerFormat, 0, 0);
|
||||||
|
|
||||||
encoder->audioStream = avformat_new_stream(encoder->context, acodec);
|
encoder->audioStream = avformat_new_stream(encoder->context, acodec);
|
||||||
encoder->audio = encoder->audioStream->codec;
|
encoder->audio = encoder->audioStream->codec;
|
||||||
encoder->audio->bit_rate = encoder->audioBitrate;
|
encoder->audio->bit_rate = encoder->audioBitrate;
|
||||||
|
@ -108,6 +140,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
||||||
|
if (!encoder->context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
av_write_trailer(encoder->context);
|
av_write_trailer(encoder->context);
|
||||||
avio_close(encoder->context->pb);
|
avio_close(encoder->context->pb);
|
||||||
|
|
||||||
|
@ -118,10 +153,14 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
|
||||||
av_frame_free(&encoder->videoFrame);
|
av_frame_free(&encoder->videoFrame);
|
||||||
avcodec_close(encoder->video);
|
avcodec_close(encoder->video);
|
||||||
avformat_free_context(encoder->context);
|
avformat_free_context(encoder->context);
|
||||||
|
encoder->context = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
|
void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
|
||||||
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
|
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
|
||||||
|
if (!encoder->context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
av_frame_make_writable(encoder->audioFrame);
|
av_frame_make_writable(encoder->audioFrame);
|
||||||
encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
|
encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
|
||||||
|
@ -150,6 +189,9 @@ void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t rig
|
||||||
|
|
||||||
void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
|
void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) {
|
||||||
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
|
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
|
||||||
|
if (!encoder->context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uint32_t* pixels;
|
uint32_t* pixels;
|
||||||
unsigned stride;
|
unsigned stride;
|
||||||
renderer->getPixels(renderer, &stride, (void**) &pixels);
|
renderer->getPixels(renderer, &stride, (void**) &pixels);
|
||||||
|
|
|
@ -16,6 +16,8 @@ struct FFmpegEncoder {
|
||||||
unsigned videoBitrate;
|
unsigned videoBitrate;
|
||||||
const char* videoCodec;
|
const char* videoCodec;
|
||||||
|
|
||||||
|
const char* containerFormat;
|
||||||
|
|
||||||
AVCodecContext* audio;
|
AVCodecContext* audio;
|
||||||
uint16_t* audioBuffer;
|
uint16_t* audioBuffer;
|
||||||
size_t audioBufferSize;
|
size_t audioBufferSize;
|
||||||
|
@ -34,6 +36,8 @@ struct FFmpegEncoder {
|
||||||
void FFmpegEncoderInit(struct FFmpegEncoder*);
|
void FFmpegEncoderInit(struct FFmpegEncoder*);
|
||||||
bool FFmpegEncoderSetAudio(struct FFmpegEncoder*, const char* acodec, unsigned abr);
|
bool FFmpegEncoderSetAudio(struct FFmpegEncoder*, const char* acodec, unsigned abr);
|
||||||
bool FFmpegEncoderSetVideo(struct FFmpegEncoder*, const char* vcodec, unsigned vbr);
|
bool FFmpegEncoderSetVideo(struct FFmpegEncoder*, const char* vcodec, unsigned vbr);
|
||||||
|
bool FFmpegEncoderSetContainer(struct FFmpegEncoder*, const char* container);
|
||||||
|
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder*);
|
||||||
bool FFmpegEncoderOpen(struct FFmpegEncoder*, const char* outfile);
|
bool FFmpegEncoderOpen(struct FFmpegEncoder*, const char* outfile);
|
||||||
void FFmpegEncoderClose(struct FFmpegEncoder*);
|
void FFmpegEncoderClose(struct FFmpegEncoder*);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue