gstengine: Make output format configurable

Add an output format option in playback settings. The options are
Detect, S16LE, and F32LE. Selecting Detect will use the existing
behavior and detect the native format when the pipeline starts. The
other options will set the format when the pipeline is built.
This commit is contained in:
Jim Broadus 2021-04-01 23:08:28 -07:00 committed by John Maguire
parent 15fdad3d51
commit 2804a4d89f
6 changed files with 126 additions and 20 deletions

View File

@ -85,7 +85,11 @@ using std::unique_ptr;
using std::vector;
const char* GstEngine::kSettingsGroup = "GstEngine";
const char* GstEngine::kSettingFormat = "format";
const char* GstEngine::kAutoSink = "autoaudiosink";
const char* GstEngine::kOutFormatDetect = "";
const char* GstEngine::kOutFormatS16LE = "S16LE";
const char* GstEngine::kOutFormatF32LE = "F32LE";
const char* GstEngine::kHypnotoadPipeline =
"audiotestsrc wave=6 ! "
"audioecho intensity=1 delay=50000000 ! "
@ -239,6 +243,8 @@ void GstEngine::ReloadSettings() {
mono_playback_ = s.value("monoplayback", false).toBool();
sample_rate_ = s.value("samplerate", kAutoSampleRate).toInt();
format_ = s.value(GstEngine::kSettingFormat, GstEngine::kOutFormatDetect)
.toString();
}
qint64 GstEngine::position_nanosec() const {
@ -819,6 +825,7 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
ret->set_buffer_min_fill(buffer_min_fill_);
ret->set_mono_playback(mono_playback_);
ret->set_sample_rate(sample_rate_);
ret->set_format(format_);
ret->AddBufferConsumer(this);
for (BufferConsumer* consumer : buffer_consumers_) {

View File

@ -73,7 +73,11 @@ class GstEngine : public Engine::Base, public BufferConsumer {
typedef QList<OutputDetails> OutputDetailsList;
static const int kAutoSampleRate = -1;
static const char* kOutFormatDetect;
static const char* kOutFormatS16LE;
static const char* kOutFormatF32LE;
static const char* kSettingsGroup;
static const char* kSettingFormat;
static const char* kAutoSink;
bool Init();
@ -223,6 +227,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
bool mono_playback_;
int sample_rate_;
QString format_;
mutable bool can_decode_success_;
mutable bool can_decode_last_;

View File

@ -464,6 +464,11 @@ bool GstEnginePipeline::InitAudioBin() {
// Link the trunk to the tee via filter.
gst_element_link_many(tee_src, capsfilter_, tee_, nullptr);
// If the user has selected a format, then set it now.
if (format_ != GstEngine::kOutFormatDetect) {
SetOutputFormat(format_);
}
// Link the analyzer output of the tee
gst_element_link(probe_queue, probe_converter);
@ -884,7 +889,9 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad,
qLog(Debug) << "Initial decoder caps:" << caps_str;
g_free(caps_str);
if (instance->pipeline_is_initialised_) {
if (instance->format_ != GstEngine::kOutFormatDetect) {
// Caps were set when the pipeline was constructed.
} else if (instance->pipeline_is_initialised_) {
qLog(Debug)
<< "Ignoring native format since pipeline is already running.";
} else {
@ -892,11 +899,10 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad,
// The output branch only handles F32LE and S16LE. If the source is S16LE,
// then use that throughout the pipeline. Otherwise, use F32LE.
if (fmt != "S16LE") {
GstCaps* new_caps = gst_caps_new_simple(
"audio/x-raw", "format", G_TYPE_STRING, "F32LE", nullptr);
g_object_set(instance->capsfilter_, "caps", new_caps, nullptr);
gst_caps_unref(new_caps);
if (fmt == GstEngine::kOutFormatS16LE) {
instance->SetOutputFormat(GstEngine::kOutFormatS16LE);
} else {
instance->SetOutputFormat(GstEngine::kOutFormatF32LE);
}
}
gst_caps_unref(caps);
@ -1297,6 +1303,14 @@ void GstEnginePipeline::UpdateVolume() {
g_object_set(G_OBJECT(volume_), "volume", vol, nullptr);
}
void GstEnginePipeline::SetOutputFormat(const QString& format) {
qLog(Debug) << "Setting format to" << format;
GstCaps* new_caps = gst_caps_new_simple(
"audio/x-raw", "format", G_TYPE_STRING, format.toUtf8().data(), nullptr);
g_object_set(capsfilter_, "caps", new_caps, nullptr);
gst_caps_unref(new_caps);
}
void GstEnginePipeline::StartFader(qint64 duration_nanosec,
QTimeLine::Direction direction,
QTimeLine::CurveShape shape,

View File

@ -51,6 +51,7 @@ class GstEnginePipeline : public GstPipelineBase {
void set_buffer_min_fill(int percent);
void set_mono_playback(bool enabled);
void set_sample_rate(int rate);
void set_format(const QString& format) { format_ = format; }
// Creates the pipeline, returns false on error
bool InitFromReq(const MediaPlaybackRequest& req, qint64 end_nanosec);
@ -153,6 +154,7 @@ class GstEnginePipeline : public GstPipelineBase {
void UpdateVolume();
void UpdateEqualizer();
void UpdateStereoBalance();
void SetOutputFormat(const QString& format);
bool ReplaceDecodeBin(GstElement* new_bin);
bool ReplaceDecodeBin(const QUrl& url);
@ -215,6 +217,7 @@ class GstEnginePipeline : public GstPipelineBase {
bool mono_playback_;
int sample_rate_;
QString format_;
// The URL that is currently playing, and the URL that is to be preloaded
// when the current track is close to finishing.

View File

@ -50,6 +50,10 @@ PlaybackSettingsPage::PlaybackSettingsPage(SettingsDialog* dialog)
ui_->sample_rate->setItemData(2, 48000);
ui_->sample_rate->setItemData(3, 96000);
ui_->sample_rate->setItemData(4, 192000);
ui_->output_format->setItemData(0, GstEngine::kOutFormatDetect);
ui_->output_format->setItemData(1, GstEngine::kOutFormatS16LE);
ui_->output_format->setItemData(2, GstEngine::kOutFormatF32LE);
}
PlaybackSettingsPage::~PlaybackSettingsPage() { delete ui_; }
@ -116,6 +120,9 @@ void PlaybackSettingsPage::Load() {
ui_->mono_playback->setChecked(s.value("monoplayback", false).toBool());
ui_->sample_rate->setCurrentIndex(ui_->sample_rate->findData(
s.value("samplerate", GstEngine::kAutoSampleRate).toInt()));
ui_->output_format->setCurrentIndex(ui_->output_format->findData(
s.value(GstEngine::kSettingFormat, GstEngine::kOutFormatDetect)
.toString()));
ui_->buffer_min_fill->setValue(s.value("bufferminfill", 33).toInt());
s.endGroup();
}
@ -153,6 +160,9 @@ void PlaybackSettingsPage::Save() {
s.setValue(
"samplerate",
ui_->sample_rate->itemData(ui_->sample_rate->currentIndex()).toInt());
s.setValue(GstEngine::kSettingFormat,
ui_->output_format->itemData(ui_->output_format->currentIndex())
.toString());
s.setValue("bufferminfill", ui_->buffer_min_fill->value());
s.endGroup();
}

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>596</width>
<height>667</height>
<height>766</height>
</rect>
</property>
<property name="windowTitle">
@ -70,7 +70,16 @@
<item>
<widget class="QWidget" name="fading_options" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -193,7 +202,16 @@
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout_4">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
@ -336,16 +354,6 @@
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="mono_playback">
<property name="toolTip">
<string>Changing mono playback preference will be effective for the next playing songs</string>
</property>
<property name="text">
<string>Mono playback</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="sample_rate_label">
<property name="text">
@ -354,7 +362,7 @@
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<layout class="QHBoxLayout" name="sample_rate_layout">
<item>
<widget class="QComboBox" name="sample_rate">
<property name="sizePolicy">
@ -417,6 +425,65 @@
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="output_format_label">
<property name="text">
<string>Format</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="mono_playback">
<property name="toolTip">
<string>Changing mono playback preference will be effective for the next playing songs</string>
</property>
<property name="text">
<string>Mono playback</string>
</property>
</widget>
</item>
<item row="4" column="1">
<layout class="QHBoxLayout" name="output_format_layout">
<item>
<widget class="QComboBox" name="output_format">
<property name="toolTip">
<string>The format is only updated when a pipeline starts.</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Detect</string>
</property>
</item>
<item>
<property name="text">
<string>S16LE (16-bit)</string>
</property>
</item>
<item>
<property name="text">
<string>F32LE (32-bit)</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>