diff --git a/src/platform/ffmpeg/ffmpeg-encoder.c b/src/platform/ffmpeg/ffmpeg-encoder.c
index b4d33e462..65925e986 100644
--- a/src/platform/ffmpeg/ffmpeg-encoder.c
+++ b/src/platform/ffmpeg/ffmpeg-encoder.c
@@ -49,6 +49,12 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
{ AV_SAMPLE_FMT_DBL, 4 },
{ AV_SAMPLE_FMT_DBLP, 4 }
};
+
+ if (!acodec) {
+ encoder->audioCodec = 0;
+ return true;
+ }
+
AVCodec* codec = avcodec_find_encoder_by_name(acodec);
if (!codec) {
return false;
@@ -106,6 +112,8 @@ bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, un
{ AV_PIX_FMT_RGB0, 3 },
{ AV_PIX_FMT_0BGR, 3 },
{ AV_PIX_FMT_0RGB, 3 },
+ { AV_PIX_FMT_RGB8, 3 },
+ { AV_PIX_FMT_BGR8, 3 },
{ AV_PIX_FMT_YUV422P, 4 },
{ AV_PIX_FMT_YUV444P, 5 },
{ AV_PIX_FMT_YUV420P, 6 }
@@ -153,10 +161,10 @@ 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) {
+ if ((encoder->audioCodec && !acodec) || !vcodec || !oformat) {
return false;
}
- if (!avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
+ if (encoder->audioCodec && !avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
return false;
}
if (!avformat_query_codec(oformat, vcodec->id, FF_COMPLIANCE_EXPERIMENTAL)) {
@@ -168,7 +176,7 @@ bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec);
- if (!acodec || !vcodec || !FFmpegEncoderVerifyContainer(encoder)) {
+ if ((encoder->audioCodec && !acodec) || !vcodec || !FFmpegEncoderVerifyContainer(encoder)) {
return false;
}
@@ -181,44 +189,46 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
encoder->context->oformat = av_guess_format(encoder->containerFormat, 0, 0);
- encoder->audioStream = avformat_new_stream(encoder->context, acodec);
- encoder->audio = encoder->audioStream->codec;
- encoder->audio->bit_rate = encoder->audioBitrate;
- encoder->audio->channels = 2;
- encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO;
- encoder->audio->sample_rate = encoder->sampleRate;
- encoder->audio->sample_fmt = encoder->sampleFormat;
- AVDictionary* opts = 0;
- av_dict_set(&opts, "strict", "-2", 0);
- if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) {
- encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER;
- }
- avcodec_open2(encoder->audio, acodec, &opts);
- av_dict_free(&opts);
- encoder->audioFrame = av_frame_alloc();
- encoder->audioFrame->nb_samples = encoder->audio->frame_size;
- encoder->audioFrame->format = encoder->audio->sample_fmt;
- encoder->audioFrame->pts = 0;
- encoder->resampleContext = avresample_alloc_context();
- av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
- av_opt_set_int(encoder->resampleContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
- av_opt_set_int(encoder->resampleContext, "in_sample_rate", PREFERRED_SAMPLE_RATE, 0);
- av_opt_set_int(encoder->resampleContext, "out_sample_rate", encoder->sampleRate, 0);
- av_opt_set_int(encoder->resampleContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
- av_opt_set_int(encoder->resampleContext, "out_sample_fmt", encoder->sampleFormat, 0);
- avresample_open(encoder->resampleContext);
- encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4;
- encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
- encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0);
- encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize);
- avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0);
+ if (acodec) {
+ encoder->audioStream = avformat_new_stream(encoder->context, acodec);
+ encoder->audio = encoder->audioStream->codec;
+ encoder->audio->bit_rate = encoder->audioBitrate;
+ encoder->audio->channels = 2;
+ encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO;
+ encoder->audio->sample_rate = encoder->sampleRate;
+ encoder->audio->sample_fmt = encoder->sampleFormat;
+ AVDictionary* opts = 0;
+ av_dict_set(&opts, "strict", "-2", 0);
+ if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) {
+ encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER;
+ }
+ avcodec_open2(encoder->audio, acodec, &opts);
+ av_dict_free(&opts);
+ encoder->audioFrame = av_frame_alloc();
+ encoder->audioFrame->nb_samples = encoder->audio->frame_size;
+ encoder->audioFrame->format = encoder->audio->sample_fmt;
+ encoder->audioFrame->pts = 0;
+ encoder->resampleContext = avresample_alloc_context();
+ av_opt_set_int(encoder->resampleContext, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0);
+ av_opt_set_int(encoder->resampleContext, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0);
+ av_opt_set_int(encoder->resampleContext, "in_sample_rate", PREFERRED_SAMPLE_RATE, 0);
+ av_opt_set_int(encoder->resampleContext, "out_sample_rate", encoder->sampleRate, 0);
+ av_opt_set_int(encoder->resampleContext, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
+ av_opt_set_int(encoder->resampleContext, "out_sample_fmt", encoder->sampleFormat, 0);
+ avresample_open(encoder->resampleContext);
+ encoder->audioBufferSize = (encoder->audioFrame->nb_samples * PREFERRED_SAMPLE_RATE / encoder->sampleRate) * 4;
+ encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
+ encoder->postaudioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0);
+ encoder->postaudioBuffer = av_malloc(encoder->postaudioBufferSize);
+ avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->postaudioBuffer, encoder->postaudioBufferSize, 0);
- if (encoder->audio->codec->id == AV_CODEC_ID_AAC &&
- (strcasecmp(encoder->containerFormat, "mp4") ||
- strcasecmp(encoder->containerFormat, "m4v") ||
- strcasecmp(encoder->containerFormat, "mov"))) {
- // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out
- encoder->absf = av_bitstream_filter_init("aac_adtstoasc");
+ if (encoder->audio->codec->id == AV_CODEC_ID_AAC &&
+ (strcasecmp(encoder->containerFormat, "mp4") ||
+ strcasecmp(encoder->containerFormat, "m4v") ||
+ strcasecmp(encoder->containerFormat, "mov"))) {
+ // MP4 container doesn't support the raw ADTS AAC format that the encoder spits out
+ encoder->absf = av_bitstream_filter_init("aac_adtstoasc");
+ }
}
encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
@@ -268,25 +278,27 @@ void FFmpegEncoderClose(struct FFmpegEncoder* encoder) {
av_write_trailer(encoder->context);
avio_close(encoder->context->pb);
- av_free(encoder->postaudioBuffer);
- if (encoder->audioBuffer) {
- av_free(encoder->audioBuffer);
+ if (encoder->audioCodec) {
+ av_free(encoder->postaudioBuffer);
+ if (encoder->audioBuffer) {
+ av_free(encoder->audioBuffer);
+ }
+ av_frame_free(&encoder->audioFrame);
+ avcodec_close(encoder->audio);
+
+ if (encoder->resampleContext) {
+ avresample_close(encoder->resampleContext);
+ }
+
+ if (encoder->absf) {
+ av_bitstream_filter_close(encoder->absf);
+ encoder->absf = 0;
+ }
}
- av_frame_free(&encoder->audioFrame);
- avcodec_close(encoder->audio);
av_frame_free(&encoder->videoFrame);
avcodec_close(encoder->video);
- if (encoder->resampleContext) {
- avresample_close(encoder->resampleContext);
- }
-
- if (encoder->absf) {
- av_bitstream_filter_close(encoder->absf);
- encoder->absf = 0;
- }
-
sws_freeContext(encoder->scaleContext);
avformat_free_context(encoder->context);
@@ -299,7 +311,7 @@ bool FFmpegEncoderIsOpen(struct FFmpegEncoder* encoder) {
void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) {
struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream;
- if (!encoder->context) {
+ if (!encoder->context || !encoder->audioCodec) {
return;
}
diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp
index 0f36b6d58..e996ed005 100644
--- a/src/platform/qt/VideoView.cpp
+++ b/src/platform/qt/VideoView.cpp
@@ -167,6 +167,16 @@ VideoView::VideoView(QWidget* parent)
.height = 160,
});
+ addPreset(m_ui.presetGIF, (Preset) {
+ .container = "GIF",
+ .vcodec = "GIF",
+ .acodec = "None",
+ .vbr = 0,
+ .abr = 0,
+ .width = 240,
+ .height = 160,
+ });
+
setAudioCodec(m_ui.audio->currentText());
setVideoCodec(m_ui.video->currentText());
setAudioBitrate(m_ui.abr->value());
@@ -219,10 +229,15 @@ void VideoView::setFilename(const QString& fname) {
void VideoView::setAudioCodec(const QString& codec, bool manual) {
free(m_audioCodecCstr);
m_audioCodec = sanitizeCodec(codec, s_acodecMap);
- m_audioCodecCstr = strdup(m_audioCodec.toLocal8Bit().constData());
+ if (m_audioCodec == "none") {
+ m_audioCodecCstr = nullptr;
+ } else {
+ m_audioCodecCstr = strdup(m_audioCodec.toLocal8Bit().constData());
+ }
if (!FFmpegEncoderSetAudio(&m_encoder, m_audioCodecCstr, m_abr)) {
free(m_audioCodecCstr);
m_audioCodecCstr = nullptr;
+ m_audioCodec = QString();
}
validateSettings();
if (manual) {
@@ -237,6 +252,7 @@ void VideoView::setVideoCodec(const QString& codec, bool manual) {
if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) {
free(m_videoCodecCstr);
m_videoCodecCstr = nullptr;
+ m_videoCodec = QString();
}
validateSettings();
if (manual) {
@@ -251,6 +267,7 @@ void VideoView::setContainer(const QString& container, bool manual) {
if (!FFmpegEncoderSetContainer(&m_encoder, m_containerCstr)) {
free(m_containerCstr);
m_containerCstr = nullptr;
+ m_container = QString();
}
validateSettings();
if (manual) {
@@ -316,21 +333,21 @@ void VideoView::showAdvanced(bool show) {
bool VideoView::validateSettings() {
bool valid = !m_filename.isNull() && !FFmpegEncoderIsOpen(&m_encoder);
- if (!m_audioCodecCstr) {
+ if (m_audioCodec.isNull()) {
valid = false;
m_ui.audio->setStyleSheet("QComboBox { color: red; }");
} else {
m_ui.audio->setStyleSheet("");
}
- if (!m_videoCodecCstr) {
+ if (m_videoCodec.isNull()) {
valid = false;
m_ui.video->setStyleSheet("QComboBox { color: red; }");
} else {
m_ui.video->setStyleSheet("");
}
- if (!m_containerCstr) {
+ if (m_container.isNull()) {
valid = false;
m_ui.container->setStyleSheet("QComboBox { color: red; }");
} else {
diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui
index 98bfb4034..f66889b22 100644
--- a/src/platform/qt/VideoView.ui
+++ b/src/platform/qt/VideoView.ui
@@ -146,6 +146,16 @@
+ -
+
+
+ GIF
+
+
+ presets
+
+
+
-