all repos — mgba @ e51285a0db00d1ec3cbd352e87d496df788d590d

mGBA Game Boy Advance Emulator

src/platform/qt/VideoView.cpp (view raw)

  1#include "VideoView.h"
  2
  3#ifdef USE_FFMPEG
  4
  5#include <QFileDialog>
  6#include <QMap>
  7
  8using namespace QGBA;
  9
 10QMap<QString, QString> VideoView::s_acodecMap;
 11QMap<QString, QString> VideoView::s_vcodecMap;
 12QMap<QString, QString> VideoView::s_containerMap;
 13
 14VideoView::VideoView(QWidget* parent)
 15	: QWidget(parent)
 16	, m_audioCodecCstr(nullptr)
 17	, m_videoCodecCstr(nullptr)
 18	, m_containerCstr(nullptr)
 19{
 20	m_ui.setupUi(this);
 21
 22	if (s_acodecMap.empty()) {
 23		s_acodecMap["aac"] = "libfaac";
 24		s_acodecMap["mp3"] = "libmp3lame";
 25		s_acodecMap["uncompressed"] = "pcm_s16le";
 26	}
 27	if (s_vcodecMap.empty()) {
 28		s_vcodecMap["dirac"] = "libschroedinger";
 29		s_vcodecMap["h264"] = "libx264";
 30		s_vcodecMap["hevc"] = "libx265";
 31		s_vcodecMap["theora"] = "libtheora";
 32		s_vcodecMap["vp8"] = "libvpx";
 33		s_vcodecMap["vp9"] = "libvpx-vp9";
 34		s_vcodecMap["xvid"] = "libxvid";
 35	}
 36	if (s_containerMap.empty()) {
 37		s_containerMap["mkv"] = "matroska";
 38	}
 39
 40	connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close()));
 41	connect(m_ui.start, SIGNAL(clicked()), this, SLOT(startRecording()));
 42	connect(m_ui.stop, SIGNAL(clicked()), this, SLOT(stopRecording()));
 43
 44	connect(m_ui.selectFile, SIGNAL(clicked()), this, SLOT(selectFile()));
 45	connect(m_ui.filename, SIGNAL(textChanged(const QString&)), this, SLOT(setFilename(const QString&)));
 46
 47	connect(m_ui.audio, SIGNAL(activated(const QString&)), this, SLOT(setAudioCodec(const QString&)));
 48	connect(m_ui.video, SIGNAL(activated(const QString&)), this, SLOT(setVideoCodec(const QString&)));
 49	connect(m_ui.container, SIGNAL(activated(const QString&)), this, SLOT(setContainer(const QString&)));
 50	connect(m_ui.audio, SIGNAL(editTextChanged(const QString&)), this, SLOT(setAudioCodec(const QString&)));
 51	connect(m_ui.video, SIGNAL(editTextChanged(const QString&)), this, SLOT(setVideoCodec(const QString&)));
 52	connect(m_ui.container, SIGNAL(editTextChanged(const QString&)), this, SLOT(setContainer(const QString&)));
 53
 54	connect(m_ui.abr, SIGNAL(valueChanged(int)), this, SLOT(setAudioBitrate(int)));
 55	connect(m_ui.vbr, SIGNAL(valueChanged(int)), this, SLOT(setVideoBitrate(int)));
 56
 57	FFmpegEncoderInit(&m_encoder);
 58
 59	setAudioCodec(m_ui.audio->currentText());
 60	setVideoCodec(m_ui.video->currentText());
 61	setAudioBitrate(m_ui.abr->value());
 62	setVideoBitrate(m_ui.vbr->value());
 63	setContainer(m_ui.container->currentText());
 64}
 65
 66VideoView::~VideoView() {
 67	stopRecording();
 68	free(m_audioCodecCstr);
 69	free(m_videoCodecCstr);
 70	free(m_containerCstr);
 71}
 72
 73void VideoView::startRecording() {
 74	if (!validateSettings()) {
 75		return;
 76	}
 77	if (!FFmpegEncoderOpen(&m_encoder, m_filename.toLocal8Bit().constData())) {
 78		return;
 79	}
 80	m_ui.start->setEnabled(false);
 81	m_ui.stop->setEnabled(true);
 82	emit recordingStarted(&m_encoder.d);
 83}
 84
 85void VideoView::stopRecording() {
 86	emit recordingStopped();
 87	FFmpegEncoderClose(&m_encoder);
 88	m_ui.stop->setEnabled(false);
 89	validateSettings();
 90}
 91
 92void VideoView::selectFile() {
 93	QString filename = QFileDialog::getSaveFileName(this, tr("Select output file"));
 94	if (!filename.isEmpty()) {
 95		m_ui.filename->setText(filename);
 96	}
 97}
 98
 99void VideoView::setFilename(const QString& fname) {
100	m_filename = fname;
101	validateSettings();
102}
103
104void VideoView::setAudioCodec(const QString& codec) {
105	free(m_audioCodecCstr);
106	m_audioCodec = sanitizeCodec(codec);
107	if (s_acodecMap.contains(m_audioCodec)) {
108		m_audioCodec = s_acodecMap[m_audioCodec];
109	}
110	m_audioCodecCstr = strdup(m_audioCodec.toLocal8Bit().constData());
111	if (!FFmpegEncoderSetAudio(&m_encoder, m_audioCodecCstr, m_abr)) {
112		free(m_audioCodecCstr);
113		m_audioCodecCstr = nullptr;
114	}
115	validateSettings();
116}
117
118void VideoView::setVideoCodec(const QString& codec) {
119	free(m_videoCodecCstr);
120	m_videoCodec = sanitizeCodec(codec);
121	if (s_vcodecMap.contains(m_videoCodec)) {
122		m_videoCodec = s_vcodecMap[m_videoCodec];
123	}
124	m_videoCodecCstr = strdup(m_videoCodec.toLocal8Bit().constData());
125	if (!FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr)) {
126		free(m_videoCodecCstr);
127		m_videoCodecCstr = nullptr;
128	}
129	validateSettings();
130}
131
132void VideoView::setContainer(const QString& container) {
133	free(m_containerCstr);
134	m_container = sanitizeCodec(container);
135	if (s_containerMap.contains(m_container)) {
136		m_container = s_containerMap[m_container];
137	}
138	m_containerCstr = strdup(m_container.toLocal8Bit().constData());
139	if (!FFmpegEncoderSetContainer(&m_encoder, m_containerCstr)) {
140		free(m_containerCstr);
141		m_containerCstr = nullptr;
142	}
143	validateSettings();
144}
145
146void VideoView::setAudioBitrate(int br) {
147	m_abr = br * 1000;
148	FFmpegEncoderSetAudio(&m_encoder, m_audioCodecCstr, m_abr);
149	validateSettings();
150}
151
152void VideoView::setVideoBitrate(int br) {
153	m_vbr = br * 1000;
154	FFmpegEncoderSetVideo(&m_encoder, m_videoCodecCstr, m_vbr);
155	validateSettings();
156}
157
158bool VideoView::validateSettings() {
159	bool valid = true;
160	if (!m_audioCodecCstr) {
161		valid = false;
162		m_ui.audio->setStyleSheet("QComboBox { color: red; }");
163	} else {
164		m_ui.audio->setStyleSheet("");
165	}
166
167	if (!m_videoCodecCstr) {
168		valid = false;
169		m_ui.video->setStyleSheet("QComboBox { color: red; }");
170	} else {
171		m_ui.video->setStyleSheet("");
172	}
173
174	if (!m_containerCstr) {
175		valid = false;
176		m_ui.container->setStyleSheet("QComboBox { color: red; }");
177	} else {
178		m_ui.container->setStyleSheet("");
179	}
180
181	// This |valid| check is necessary as if one of the cstrs
182	// is null, the encoder likely has a dangling pointer
183	if (valid && !FFmpegEncoderVerifyContainer(&m_encoder)) {
184		valid = false;
185	}
186
187	m_ui.start->setEnabled(valid && !m_filename.isNull());
188	return valid;
189}
190
191QString VideoView::sanitizeCodec(const QString& codec) {
192	QString sanitized = codec.toLower();
193	return sanitized.remove(QChar('.'));
194}
195
196#endif