mirror of https://github.com/mgba-emu/mgba.git
Video: Support scaling the output frame
This commit is contained in:
parent
046a1b71ed
commit
b0fdbab77e
|
@ -29,6 +29,7 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
|
||||||
FFmpegEncoderSetAudio(encoder, "flac", 0);
|
FFmpegEncoderSetAudio(encoder, "flac", 0);
|
||||||
FFmpegEncoderSetVideo(encoder, "png", 0);
|
FFmpegEncoderSetVideo(encoder, "png", 0);
|
||||||
FFmpegEncoderSetContainer(encoder, "matroska");
|
FFmpegEncoderSetContainer(encoder, "matroska");
|
||||||
|
FFmpegEncoderSetDimensions(encoder, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
|
||||||
encoder->resampleContext = 0;
|
encoder->resampleContext = 0;
|
||||||
encoder->absf = 0;
|
encoder->absf = 0;
|
||||||
encoder->context = 0;
|
encoder->context = 0;
|
||||||
|
@ -143,6 +144,11 @@ bool FFmpegEncoderSetContainer(struct FFmpegEncoder* encoder, const char* contai
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FFmpegEncoderSetDimensions(struct FFmpegEncoder* encoder, int width, int height) {
|
||||||
|
encoder->width = width > 0 ? width : VIDEO_HORIZONTAL_PIXELS;
|
||||||
|
encoder->height = height > 0 ? height : VIDEO_VERTICAL_PIXELS;
|
||||||
|
}
|
||||||
|
|
||||||
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
|
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) {
|
||||||
AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
|
AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0);
|
||||||
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
|
AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec);
|
||||||
|
@ -218,8 +224,8 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
||||||
encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
|
encoder->videoStream = avformat_new_stream(encoder->context, vcodec);
|
||||||
encoder->video = encoder->videoStream->codec;
|
encoder->video = encoder->videoStream->codec;
|
||||||
encoder->video->bit_rate = encoder->videoBitrate;
|
encoder->video->bit_rate = encoder->videoBitrate;
|
||||||
encoder->video->width = VIDEO_HORIZONTAL_PIXELS;
|
encoder->video->width = encoder->width;
|
||||||
encoder->video->height = VIDEO_VERTICAL_PIXELS;
|
encoder->video->height = encoder->height;
|
||||||
encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY };
|
encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY };
|
||||||
encoder->video->pix_fmt = encoder->pixFormat;
|
encoder->video->pix_fmt = encoder->pixFormat;
|
||||||
encoder->video->gop_size = 15;
|
encoder->video->gop_size = 15;
|
||||||
|
@ -234,8 +240,8 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
||||||
encoder->videoFrame->height = encoder->video->height;
|
encoder->videoFrame->height = encoder->video->height;
|
||||||
encoder->videoFrame->pts = 0;
|
encoder->videoFrame->pts = 0;
|
||||||
encoder->scaleContext = sws_getContext(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, AV_PIX_FMT_0BGR32,
|
encoder->scaleContext = sws_getContext(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, AV_PIX_FMT_0BGR32,
|
||||||
VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, encoder->video->pix_fmt,
|
encoder->videoFrame->width, encoder->videoFrame->height, encoder->video->pix_fmt,
|
||||||
0, 0, 0, 0);
|
SWS_POINT, 0, 0, 0);
|
||||||
av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32);
|
av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32);
|
||||||
|
|
||||||
avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE);
|
avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE);
|
||||||
|
|
|
@ -35,6 +35,8 @@ struct FFmpegEncoder {
|
||||||
struct AVCodecContext* video;
|
struct AVCodecContext* video;
|
||||||
enum AVPixelFormat pixFormat;
|
enum AVPixelFormat pixFormat;
|
||||||
struct AVFrame* videoFrame;
|
struct AVFrame* videoFrame;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
int64_t currentVideoFrame;
|
int64_t currentVideoFrame;
|
||||||
struct SwsContext* scaleContext;
|
struct SwsContext* scaleContext;
|
||||||
struct AVStream* videoStream;
|
struct AVStream* videoStream;
|
||||||
|
@ -44,6 +46,7 @@ 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 FFmpegEncoderSetContainer(struct FFmpegEncoder*, const char* container);
|
||||||
|
void FFmpegEncoderSetDimensions(struct FFmpegEncoder*, int width, int height);
|
||||||
bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder*);
|
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*);
|
||||||
|
|
|
@ -79,6 +79,9 @@ VideoView::VideoView(QWidget* parent)
|
||||||
connect(m_ui.abr, SIGNAL(valueChanged(int)), this, SLOT(setAudioBitrate(int)));
|
connect(m_ui.abr, SIGNAL(valueChanged(int)), this, SLOT(setAudioBitrate(int)));
|
||||||
connect(m_ui.vbr, SIGNAL(valueChanged(int)), this, SLOT(setVideoBitrate(int)));
|
connect(m_ui.vbr, SIGNAL(valueChanged(int)), this, SLOT(setVideoBitrate(int)));
|
||||||
|
|
||||||
|
connect(m_ui.width, SIGNAL(valueChanged(int)), this, SLOT(setWidth(int)));
|
||||||
|
connect(m_ui.height, SIGNAL(valueChanged(int)), this, SLOT(setHeight(int)));
|
||||||
|
|
||||||
connect(m_ui.showAdvanced, SIGNAL(clicked(bool)), this, SLOT(showAdvanced(bool)));
|
connect(m_ui.showAdvanced, SIGNAL(clicked(bool)), this, SLOT(showAdvanced(bool)));
|
||||||
|
|
||||||
FFmpegEncoderInit(&m_encoder);
|
FFmpegEncoderInit(&m_encoder);
|
||||||
|
@ -107,7 +110,7 @@ VideoView::VideoView(QWidget* parent)
|
||||||
.container = "MP4",
|
.container = "MP4",
|
||||||
.vcodec = "h.264",
|
.vcodec = "h.264",
|
||||||
.acodec = "AAC",
|
.acodec = "AAC",
|
||||||
.vbr = 6000,
|
.vbr = 5000,
|
||||||
.abr = 384,
|
.abr = 384,
|
||||||
.width = 1620,
|
.width = 1620,
|
||||||
.height = 1080
|
.height = 1080
|
||||||
|
@ -144,6 +147,8 @@ VideoView::VideoView(QWidget* parent)
|
||||||
setAudioBitrate(m_ui.abr->value());
|
setAudioBitrate(m_ui.abr->value());
|
||||||
setVideoBitrate(m_ui.vbr->value());
|
setVideoBitrate(m_ui.vbr->value());
|
||||||
setContainer(m_ui.container->currentText());
|
setContainer(m_ui.container->currentText());
|
||||||
|
setWidth(m_ui.width->value());
|
||||||
|
setHeight(m_ui.height->value());
|
||||||
|
|
||||||
showAdvanced(false);
|
showAdvanced(false);
|
||||||
}
|
}
|
||||||
|
@ -246,6 +251,24 @@ void VideoView::setVideoBitrate(int br, bool manual) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoView::setWidth(int width, bool manual) {
|
||||||
|
m_width = width;
|
||||||
|
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
|
||||||
|
validateSettings();
|
||||||
|
if (manual) {
|
||||||
|
uncheckIncompatible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoView::setHeight(int height, bool manual) {
|
||||||
|
m_height = height;
|
||||||
|
FFmpegEncoderSetDimensions(&m_encoder, m_width, m_height);
|
||||||
|
validateSettings();
|
||||||
|
if (manual) {
|
||||||
|
uncheckIncompatible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VideoView::showAdvanced(bool show) {
|
void VideoView::showAdvanced(bool show) {
|
||||||
m_ui.advancedBox->setVisible(show);
|
m_ui.advancedBox->setVisible(show);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +313,9 @@ void VideoView::uncheckIncompatible() {
|
||||||
.acodec = m_audioCodec,
|
.acodec = m_audioCodec,
|
||||||
.vcodec = m_videoCodec,
|
.vcodec = m_videoCodec,
|
||||||
.abr = m_abr / 1000,
|
.abr = m_abr / 1000,
|
||||||
.vbr = m_vbr / 1000
|
.vbr = m_vbr / 1000,
|
||||||
|
.width = m_width,
|
||||||
|
.height = m_height
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto iterator = m_presets.constBegin(); iterator != m_presets.constEnd(); ++iterator) {
|
for (auto iterator = m_presets.constBegin(); iterator != m_presets.constEnd(); ++iterator) {
|
||||||
|
@ -371,6 +396,14 @@ void VideoView::setPreset(const Preset& preset) {
|
||||||
setVideoBitrate(preset.vbr, false);
|
setVideoBitrate(preset.vbr, false);
|
||||||
safelySet(m_ui.vbr, preset.vbr);
|
safelySet(m_ui.vbr, preset.vbr);
|
||||||
}
|
}
|
||||||
|
if (preset.width) {
|
||||||
|
setWidth(preset.width, false);
|
||||||
|
safelySet(m_ui.width, preset.width);
|
||||||
|
}
|
||||||
|
if (preset.height) {
|
||||||
|
setHeight(preset.height, false);
|
||||||
|
safelySet(m_ui.height, preset.height);
|
||||||
|
}
|
||||||
|
|
||||||
uncheckIncompatible();
|
uncheckIncompatible();
|
||||||
validateSettings();
|
validateSettings();
|
||||||
|
|
|
@ -52,6 +52,9 @@ private slots:
|
||||||
void setAudioBitrate(int, bool manual = true);
|
void setAudioBitrate(int, bool manual = true);
|
||||||
void setVideoBitrate(int, bool manual = true);
|
void setVideoBitrate(int, bool manual = true);
|
||||||
|
|
||||||
|
void setWidth(int, bool manual = true);
|
||||||
|
void setHeight(int, bool manual = true);
|
||||||
|
|
||||||
void showAdvanced(bool);
|
void showAdvanced(bool);
|
||||||
|
|
||||||
void uncheckIncompatible();
|
void uncheckIncompatible();
|
||||||
|
@ -81,6 +84,9 @@ private:
|
||||||
int m_abr;
|
int m_abr;
|
||||||
int m_vbr;
|
int m_vbr;
|
||||||
|
|
||||||
|
int m_width;
|
||||||
|
int m_height;
|
||||||
|
|
||||||
QMap<QAbstractButton*, Preset> m_presets;
|
QMap<QAbstractButton*, Preset> m_presets;
|
||||||
|
|
||||||
static QMap<QString, QString> s_acodecMap;
|
static QMap<QString, QString> s_acodecMap;
|
||||||
|
|
|
@ -152,9 +152,6 @@
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="preset1080">
|
<widget class="QRadioButton" name="preset1080">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>1080p</string>
|
<string>1080p</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -165,9 +162,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="preset720">
|
<widget class="QRadioButton" name="preset720">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>720p</string>
|
<string>720p</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -178,9 +172,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="preset480">
|
<widget class="QRadioButton" name="preset480">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>480p</string>
|
<string>480p</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -191,9 +182,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="preset160">
|
<widget class="QRadioButton" name="preset160">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>GBA (240x160)</string>
|
<string>GBA (240x160)</string>
|
||||||
</property>
|
</property>
|
||||||
|
@ -423,9 +411,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="3">
|
<item row="0" column="3">
|
||||||
<widget class="QSpinBox" name="height">
|
<widget class="QSpinBox" name="height">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>160</number>
|
<number>160</number>
|
||||||
</property>
|
</property>
|
||||||
|
@ -436,9 +421,6 @@
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="1">
|
||||||
<widget class="QSpinBox" name="width">
|
<widget class="QSpinBox" name="width">
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>240</number>
|
<number>240</number>
|
||||||
</property>
|
</property>
|
||||||
|
|
Loading…
Reference in New Issue