aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorJirka Novak <j.novak@netsystem.cz>2021-05-01 13:51:31 +0200
committerWireshark GitLab Utility <gerald+gitlab-utility@wireshark.org>2021-05-03 21:38:48 +0000
commit4c7c377d42fecd1b9c1624c5cb6e56a118a5e9fb (patch)
tree6865f4128141fce5121a0c6009e54f7f1c7d3a63 /ui
parent71f66bee3b9fd378bdf1c8f5f62bf769b20fd7e4 (diff)
RTP Player: Player is able to skip silence during playback
Code is NOT able to do VAD (Voice Activity Detection) so audio silence (sequence of equal samples) nor noise are not recognized as silence. Just missing RTP (Confort Noise, interupted RTP, ...) and muted streams are recognized as silence for this feature. User can control duration of shortest silence to skip. Updated documentation.
Diffstat (limited to 'ui')
-rw-r--r--ui/qt/rtp_audio_stream.cpp12
-rw-r--r--ui/qt/rtp_audio_stream.h4
-rw-r--r--ui/qt/rtp_player_dialog.cpp58
-rw-r--r--ui/qt/rtp_player_dialog.h1
-rw-r--r--ui/qt/rtp_player_dialog.ui104
-rw-r--r--ui/qt/utils/rtp_audio_file.cpp9
-rw-r--r--ui/qt/utils/rtp_audio_file.h1
7 files changed, 179 insertions, 10 deletions
diff --git a/ui/qt/rtp_audio_stream.cpp b/ui/qt/rtp_audio_stream.cpp
index 994894d37b..27c973a06a 100644
--- a/ui/qt/rtp_audio_stream.cpp
+++ b/ui/qt/rtp_audio_stream.cpp
@@ -772,6 +772,18 @@ void RtpAudioStream::stopPlaying()
}
}
+void RtpAudioStream::seekPlaying(qint64 samples _U_)
+{
+ if (audio_routing_.isMuted())
+ return;
+
+ if (audio_output_) {
+ audio_output_->suspend();
+ audio_file_->seekSample(samples);
+ audio_output_->resume();
+ }
+}
+
void RtpAudioStream::outputStateChanged(QAudio::State new_state)
{
if (!audio_output_) return;
diff --git a/ui/qt/rtp_audio_stream.h b/ui/qt/rtp_audio_stream.h
index a9e8f05ed3..6c2fbfd12c 100644
--- a/ui/qt/rtp_audio_stream.h
+++ b/ui/qt/rtp_audio_stream.h
@@ -139,6 +139,7 @@ public:
void startPlaying();
void pausePlaying();
void stopPlaying();
+ void seekPlaying(qint64 samples);
void setStereoRequired(bool stereo_required) { stereo_required_ = stereo_required; }
qint16 getMaxSampleValue() { return max_sample_val_; }
void setMaxSampleValue(gint16 max_sample_val) { max_sample_val_used_ = max_sample_val; }
@@ -146,6 +147,9 @@ public:
qint64 readSample(SAMPLE *sample);
qint64 getLeadSilenceSamples() { return prepend_samples_; }
qint64 getTotalSamples() { return (audio_file_->getTotalSamples()); }
+ qint64 getEndOfSilenceSample() { return (audio_file_->getEndOfSilenceSample()); }
+ double getEndOfSilenceTime() { return (double)getEndOfSilenceSample() / (double)playRate(); }
+ qint64 convertTimeToSamples(double time) { return (qint64)(time * playRate()); }
bool savePayload(QIODevice *file);
guint getHash() { return rtpstream_id_to_hash(&(id_)); }
rtpstream_id_t *getID() { return &(id_); }
diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp
index c8bd4694c4..f7eb294ff5 100644
--- a/ui/qt/rtp_player_dialog.cpp
+++ b/ui/qt/rtp_player_dialog.cpp
@@ -150,6 +150,7 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_
, block_redraw_(false)
, lock_ui_(0)
, read_capture_enabled_(capture_running)
+ , silence_skipped_time_(0.0)
{
ui->setupUi(this);
loadGeometry(parent.width(), parent.height());
@@ -217,6 +218,9 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_
ui->pauseButton->setVisible(false);
ui->stopButton->setIcon(StockIcon("media-playback-stop"));
ui->stopButton->setEnabled(false);
+ ui->skipSilenceButton->setIcon(StockIcon("media-seek-forward"));
+ ui->skipSilenceButton->setCheckable(true);
+ ui->skipSilenceButton->setEnabled(false);
read_btn_ = ui->buttonBox->addButton(ui->actionReadCapture->text(), QDialogButtonBox::ActionRole);
read_btn_->setToolTip(ui->actionReadCapture->toolTip());
@@ -259,6 +263,8 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_
ui->playButton->setEnabled(false);
ui->pauseButton->setEnabled(false);
ui->stopButton->setEnabled(false);
+ ui->skipSilenceButton->setEnabled(false);
+ ui->minSilenceSpinBox->setEnabled(false);
ui->outputDeviceComboBox->addItem(tr("No devices available"));
ui->outputAudioRate->setEnabled(false);
} else {
@@ -939,8 +945,14 @@ void RtpPlayerDialog::updateWidgets()
int count = ui->streamTreeWidget->topLevelItemCount();
int selected = ui->streamTreeWidget->selectedItems().count();
- if (count < 1)
+ if (count < 1) {
enable_play = false;
+ ui->skipSilenceButton->setEnabled(false);
+ ui->minSilenceSpinBox->setEnabled(false);
+ } else {
+ ui->skipSilenceButton->setEnabled(true);
+ ui->minSilenceSpinBox->setEnabled(true);
+ }
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
@@ -1283,6 +1295,7 @@ void RtpPlayerDialog::on_playButton_clicked()
// Protect start time against move of marker during the play
start_marker_time_play_ = start_marker_time_;
+ silence_skipped_time_ = 0.0;
cur_play_pos_->point1->setCoords(start_marker_time_play_, 0.0);
cur_play_pos_->point2->setCoords(start_marker_time_play_, 1.0);
cur_play_pos_->setVisible(true);
@@ -1364,12 +1377,55 @@ QAudioOutput *RtpPlayerDialog::getSilenceAudioOutput()
void RtpPlayerDialog::outputNotify()
{
+ double new_current_pos = 0.0;
+ double current_pos = 0.0;
+
double secs = marker_stream_->processedUSecs() / 1000000.0;
+
+ if (ui->skipSilenceButton->isChecked()) {
+ // We should check whether we can skip some silence
+ // We must calculate in time domain as every stream can use different
+ // play rate
+ double min_silence = playing_streams_[0]->getEndOfSilenceTime();
+ for( int i = 1; i<playing_streams_.count(); ++i ) {
+ qint64 cur_silence = playing_streams_[i]->getEndOfSilenceTime();
+ if (cur_silence < min_silence) {
+ min_silence = cur_silence;
+ }
+ }
+
+ if (min_silence > 0.0) {
+ double silence_duration;
+
+ // Calculate silence duration we can skip
+ new_current_pos = first_stream_rel_start_time_ + min_silence;
+ if (ui->todCheckBox->isChecked()) {
+ current_pos = secs + start_marker_time_play_ + first_stream_rel_start_time_;
+ } else {
+ current_pos = secs + start_marker_time_play_;
+ }
+ silence_duration = new_current_pos - current_pos;
+
+ if (silence_duration >= ui->minSilenceSpinBox->value()) {
+ // Skip silence gap and update cursor difference
+ for( int i = 0; i<playing_streams_.count(); ++i ) {
+ // Convert silence from time domain to samples
+ qint64 skip_samples = playing_streams_[i]->convertTimeToSamples(min_silence);
+ playing_streams_[i]->seekPlaying(skip_samples);
+ }
+ silence_skipped_time_ = silence_duration;
+ }
+ }
+ }
+
+ // Calculate new cursor position
if (ui->todCheckBox->isChecked()) {
secs += start_marker_time_play_;
+ secs += silence_skipped_time_;
} else {
secs += start_marker_time_play_;
secs -= first_stream_rel_start_time_;
+ secs += silence_skipped_time_;
}
setPlayPosition(secs);
}
diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h
index 4002f977ac..fe797d9c79 100644
--- a/ui/qt/rtp_player_dialog.h
+++ b/ui/qt/rtp_player_dialog.h
@@ -211,6 +211,7 @@ private:
bool block_redraw_;
int lock_ui_;
bool read_capture_enabled_;
+ double silence_skipped_time_;
// const QString streamKey(const rtpstream_info_t *rtpstream);
// const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo);
diff --git a/ui/qt/rtp_player_dialog.ui b/ui/qt/rtp_player_dialog.ui
index 94925e4655..0b1cbbd381 100644
--- a/ui/qt/rtp_player_dialog.ui
+++ b/ui/qt/rtp_player_dialog.ui
@@ -122,12 +122,15 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,1">
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0,0,0,0,0,1,0,0">
<item>
<widget class="QToolButton" name="playButton">
<property name="text">
<string/>
</property>
+ <property name="toolTip">
+ <string>Start playback of all unmuted streams</string>
+ </property>
</widget>
</item>
<item>
@@ -135,6 +138,9 @@
<property name="text">
<string/>
</property>
+ <property name="toolTip">
+ <string>Pause/unpause playback</string>
+ </property>
</widget>
</item>
<item>
@@ -142,6 +148,9 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
@@ -155,6 +164,9 @@
<property name="text">
<string/>
</property>
+ <property name="toolTip">
+ <string>Stop playback</string>
+ </property>
</widget>
</item>
<item>
@@ -162,6 +174,9 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
@@ -171,38 +186,109 @@
</spacer>
</item>
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QToolButton" name="skipSilenceButton">
<property name="text">
- <string>Output Device:</string>
+ <string/>
+ </property>
+ <property name="toolTip">
+ <string>Enable/disable skipping of silence during playback</string>
</property>
</widget>
</item>
<item>
- <widget class="QComboBox" name="outputDeviceComboBox"/>
+ <spacer name="horizontalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
</item>
<item>
- <widget class="QLabel" name="label_4">
+ <widget class="QLabel" name="label">
<property name="text">
- <string>Output Audio Rate:</string>
+ <string>Min silence:</string>
</property>
</widget>
</item>
<item>
- <widget class="QComboBox" name="outputAudioRate"/>
+ <widget class="QDoubleSpinBox" name="minSilenceSpinBox">
+ <property name="toolTip">
+ <string>Minimum silence duration to skip in seconds</string>
+ </property>
+ <property name="decimals">
+ <number>0</number>
+ </property>
+ <property name="minimum">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>1.000000000000000</double>
+ </property>
+ <property name="value">
+ <double>2.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Output Device:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="outputDeviceComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
- <height>20</height>
+ <width>20</width>
+ <height>10</height>
</size>
</property>
</spacer>
</item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Output Audio Rate:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="outputAudioRate"/>
+ </item>
</layout>
</item>
<item>
diff --git a/ui/qt/utils/rtp_audio_file.cpp b/ui/qt/utils/rtp_audio_file.cpp
index 6e17bdc4fb..4aa12b4c1a 100644
--- a/ui/qt/utils/rtp_audio_file.cpp
+++ b/ui/qt/utils/rtp_audio_file.cpp
@@ -327,6 +327,15 @@ qint64 RtpAudioFile::getTotalSamples()
return (real_size_/(qint64)sizeof(SAMPLE));
}
+qint64 RtpAudioFile::getEndOfSilenceSample()
+{
+ if (cur_frame_.type == RTP_FRAME_SILENCE) {
+ return (cur_frame_.real_pos + cur_frame_.len) / (qint64)sizeof(SAMPLE);
+ } else {
+ return -1;
+ }
+}
+
qint64 RtpAudioFile::readData(char *data, qint64 maxSize)
{
qint64 to_read = maxSize;
diff --git a/ui/qt/utils/rtp_audio_file.h b/ui/qt/utils/rtp_audio_file.h
index d19638ae00..15a1b2929c 100644
--- a/ui/qt/utils/rtp_audio_file.h
+++ b/ui/qt/utils/rtp_audio_file.h
@@ -69,6 +69,7 @@ public:
void seekSample(qint64 samples);
qint64 readSample(SAMPLE *sample);
qint64 getTotalSamples();
+ qint64 getEndOfSilenceSample();
protected:
// Functions for reading data during play