forked from osmocom/wireshark
Add jitter logic to RtpAudioStream.
Copy the jitter logic from rtp_player.c to rtp_audio_stream.cpp. This still isn't correct but the RTP player should now be complete enough to start looking at the bug list at the top of rtp_player_dialog.cpp. Disable timing and jitter controls while we're playing while we're here. Fixes bug 11635. Bug: 11635 Change-Id: Ie583ade522702cbe1bbcea4475a535caa1d74fa2 Reviewed-on: https://code.wireshark.org/review/11295 Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
parent
25de4422c6
commit
2ccb9d2d95
|
@ -47,7 +47,6 @@ static const spx_int16_t visual_sample_rate_ = 1000;
|
|||
|
||||
RtpAudioStream::RtpAudioStream(QObject *parent, _rtp_stream_info *rtp_stream) :
|
||||
QObject(parent),
|
||||
last_sequence_(0),
|
||||
decoders_hash_(rtp_decoder_hash_table_new()),
|
||||
global_start_rel_time_(0.0),
|
||||
start_abs_offset_(0.0),
|
||||
|
@ -57,7 +56,9 @@ RtpAudioStream::RtpAudioStream(QObject *parent, _rtp_stream_info *rtp_stream) :
|
|||
audio_resampler_(0),
|
||||
audio_output_(0),
|
||||
max_sample_val_(1),
|
||||
color_(0)
|
||||
color_(0),
|
||||
jitter_buffer_size_(50),
|
||||
timing_mode_(RtpAudioStream::JitterBuffer)
|
||||
{
|
||||
copy_address(&src_addr_, &rtp_stream->src_addr);
|
||||
src_port_ = rtp_stream->src_port;
|
||||
|
@ -148,7 +149,6 @@ void RtpAudioStream::addRtpPacket(const struct _packet_info *pinfo, const struct
|
|||
|
||||
void RtpAudioStream::reset(double start_rel_time)
|
||||
{
|
||||
last_sequence_ = 0;
|
||||
global_start_rel_time_ = start_rel_time;
|
||||
stop_rel_time_ = start_rel_time_;
|
||||
audio_out_rate_ = 0;
|
||||
|
@ -156,6 +156,7 @@ void RtpAudioStream::reset(double start_rel_time)
|
|||
packet_timestamps_.clear();
|
||||
visual_samples_.clear();
|
||||
out_of_seq_timestamps_.clear();
|
||||
jitter_drop_timestamps_.clear();
|
||||
|
||||
if (audio_resampler_) {
|
||||
ws_codec_resampler_reset_mem(audio_resampler_);
|
||||
|
@ -167,22 +168,39 @@ void RtpAudioStream::reset(double start_rel_time)
|
|||
}
|
||||
|
||||
static const int sample_bytes_ = sizeof(SAMPLE) / sizeof(char);
|
||||
/* Fix for bug 4119/5902: don't insert too many silence frames.
|
||||
* XXX - is there a better thing to do here?
|
||||
*/
|
||||
static const int max_silence_samples_ = 240000;
|
||||
void RtpAudioStream::decode()
|
||||
{
|
||||
if (rtp_packets_.size() < 1) return;
|
||||
|
||||
// gtk/rtp_player.c:decode_rtp_stream
|
||||
// XXX This is more messy than it should be.
|
||||
|
||||
SAMPLE *decode_buff = NULL;
|
||||
gsize resample_buff_len = 0x1000;
|
||||
SAMPLE *resample_buff = (SAMPLE *) g_malloc(resample_buff_len);
|
||||
spx_uint32_t cur_in_rate, visual_out_rate;
|
||||
char *write_buff;
|
||||
qint64 write_bytes;
|
||||
unsigned channels;
|
||||
unsigned sample_rate;
|
||||
spx_uint32_t cur_in_rate = 0, visual_out_rate = 0;
|
||||
char *write_buff = NULL;
|
||||
qint64 write_bytes = 0;
|
||||
unsigned channels = 0;
|
||||
unsigned sample_rate = 0;
|
||||
int last_sequence = 0;
|
||||
|
||||
for (int i = 0; i < rtp_packets_.size(); i++) {
|
||||
rtp_packet_t *rtp_packet = rtp_packets_[i];
|
||||
double rtp_time_prev = 0.0;
|
||||
double arrive_time_prev = 0.0;
|
||||
double pack_period = 0.0;
|
||||
double start_time = 0.0;
|
||||
double start_rtp_time = 0.0;
|
||||
guint32 start_timestamp = 0;
|
||||
|
||||
size_t decoded_bytes_prev = 0;
|
||||
|
||||
for (int cur_packet = 0; cur_packet < rtp_packets_.size(); cur_packet++) {
|
||||
SAMPLE *decode_buff = NULL;
|
||||
// XXX The GTK+ UI updates a progress bar here.
|
||||
rtp_packet_t *rtp_packet = rtp_packets_[cur_packet];
|
||||
|
||||
stop_rel_time_ = start_rel_time_ + rtp_packet->arrive_offset;
|
||||
ws_codec_resampler_get_rate(visual_resampler_, &cur_in_rate, &visual_out_rate);
|
||||
|
@ -197,33 +215,118 @@ void RtpAudioStream::decode()
|
|||
payload_names_ << payload_name;
|
||||
}
|
||||
|
||||
if (cur_packet < 1) { // First packet
|
||||
start_timestamp = rtp_packet->info->info_timestamp;
|
||||
start_rtp_time = 0;
|
||||
rtp_time_prev = 0;
|
||||
last_sequence = rtp_packet->info->info_seq_num - 1;
|
||||
}
|
||||
|
||||
size_t decoded_bytes = decode_rtp_packet(rtp_packet, &decode_buff, decoders_hash_, &channels, &sample_rate);
|
||||
if (decoded_bytes == 0)
|
||||
continue; /* Didn't decode anything */
|
||||
|
||||
if (decoded_bytes == 0 || sample_rate == 0) {
|
||||
// We didn't decode anything. Clean up and prep for the next packet.
|
||||
last_sequence = rtp_packet->info->info_seq_num;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (audio_out_rate_ == 0) { // First non-zero wins
|
||||
audio_out_rate_ = sample_rate;
|
||||
RTP_STREAM_DEBUG("Audio sample rate is %u", audio_out_rate_);
|
||||
|
||||
// Prepend silence to match our sibling streams.
|
||||
tempfile_->seek(0);
|
||||
int prepend_samples = (start_rel_time_ - global_start_rel_time_) * audio_out_rate_;
|
||||
if (prepend_samples > 0) {
|
||||
writeSilence(prepend_samples);
|
||||
}
|
||||
}
|
||||
|
||||
if (rtp_packet->info->info_seq_num != last_sequence+1) {
|
||||
out_of_seq_timestamps_.append(stop_rel_time_);
|
||||
}
|
||||
last_sequence = rtp_packet->info->info_seq_num;
|
||||
|
||||
double rtp_time = (double)(rtp_packet->info->info_timestamp-start_timestamp)/sample_rate - start_rtp_time;
|
||||
double arrive_time;
|
||||
if (timing_mode_ == Uninterrupted) {
|
||||
arrive_time = rtp_time;
|
||||
} else {
|
||||
arrive_time = (double)rtp_packet->arrive_offset/1000 - start_time;
|
||||
}
|
||||
|
||||
double diff = qAbs(arrive_time - rtp_time);
|
||||
if (diff*1000 > jitter_buffer_size_ && timing_mode_ == Uninterrupted) {
|
||||
// rtp_player.c:628
|
||||
|
||||
jitter_drop_timestamps_.append(stop_rel_time_);
|
||||
RTP_STREAM_DEBUG("Packet drop by jitter buffer exceeded %f > %d", diff*1000, jitter_buffer_size_);
|
||||
|
||||
/* if there was a silence period (more than two packetization period) resync the source */
|
||||
if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
|
||||
int silence_samples;
|
||||
RTP_STREAM_DEBUG("Resync...");
|
||||
|
||||
silence_samples = (int)((arrive_time - arrive_time_prev)*sample_rate - decoded_bytes_prev / sample_bytes_);
|
||||
/* Fix for bug 4119/5902: don't insert too many silence frames.
|
||||
* XXX - is there a better thing to do here?
|
||||
*/
|
||||
silence_samples = qMin(silence_samples, max_silence_samples_);
|
||||
writeSilence(silence_samples);
|
||||
silence_timestamps_.append(stop_rel_time_);
|
||||
|
||||
decoded_bytes_prev = 0;
|
||||
/* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
|
||||
/* XXX: if timestamps (RTP) are missing/ignored try use packet arrive time only (see also "rtp_time") */
|
||||
start_timestamp = rtp_packet->info->info_timestamp;
|
||||
start_rtp_time = 0;
|
||||
start_time = (double)rtp_packet->arrive_offset/1000;
|
||||
rtp_time_prev = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
// rtp_player.c:664
|
||||
/* Add silence if it is necessary */
|
||||
int silence_samples;
|
||||
|
||||
if (timing_mode_ == Uninterrupted) {
|
||||
silence_samples = 0;
|
||||
} else {
|
||||
silence_samples = (int)((rtp_time - rtp_time_prev)*sample_rate - decoded_bytes_prev / sample_bytes_);
|
||||
}
|
||||
|
||||
if (silence_samples != 0) {
|
||||
wrong_timestamp_timestamps_.append(stop_rel_time_);
|
||||
}
|
||||
|
||||
if (silence_samples > 0) {
|
||||
/* Fix for bug 4119/5902: don't insert too many silence frames.
|
||||
* XXX - is there a better thing to do here?
|
||||
*/
|
||||
silence_samples = qMin(silence_samples, max_silence_samples_);
|
||||
writeSilence(silence_samples);
|
||||
silence_timestamps_.append(stop_rel_time_);
|
||||
}
|
||||
|
||||
// XXX rtp_player.c:696 adds audio here.
|
||||
|
||||
rtp_time_prev = rtp_time;
|
||||
pack_period = (double) decoded_bytes / sample_bytes_ / sample_rate;
|
||||
decoded_bytes_prev = decoded_bytes;
|
||||
arrive_time_prev = arrive_time;
|
||||
}
|
||||
|
||||
// Write samples to our file.
|
||||
write_buff = (char *) decode_buff;
|
||||
write_bytes = rtp_packet->info->info_payload_len * sample_bytes_;
|
||||
|
||||
if (tempfile_->pos() == 0) {
|
||||
// First packet. Let it determine our sample rate.
|
||||
audio_out_rate_ = sample_rate;
|
||||
|
||||
last_sequence_ = rtp_packet->info->info_seq_num - 1;
|
||||
|
||||
// Prepend silence to match our sibling streams.
|
||||
int prepend_samples = (start_rel_time_ - global_start_rel_time_) * audio_out_rate_;
|
||||
if (prepend_samples > 0) {
|
||||
int prepend_bytes = prepend_samples * sample_bytes_;
|
||||
char *prepend_buff = (char *) g_malloc(prepend_bytes);
|
||||
SAMPLE silence = 0;
|
||||
memccpy(prepend_buff, &silence, prepend_samples, sample_bytes_);
|
||||
tempfile_->write(prepend_buff, prepend_bytes);
|
||||
}
|
||||
} else if (audio_out_rate_ != sample_rate) {
|
||||
if (audio_out_rate_ != sample_rate) {
|
||||
// Resample the audio to match our previous output rate.
|
||||
if (!audio_resampler_) {
|
||||
audio_resampler_ = ws_codec_resampler_init(1, sample_rate, audio_out_rate_, 10, NULL);
|
||||
ws_codec_resampler_skip_zeros(audio_resampler_);
|
||||
// RTP_STREAM_DEBUG("Started resampling from %u to (out) %u Hz.", sample_rate, audio_out_rate_);
|
||||
RTP_STREAM_DEBUG("Started resampling from %u to (out) %u Hz.", sample_rate, audio_out_rate_);
|
||||
} else {
|
||||
spx_uint32_t audio_out_rate;
|
||||
ws_codec_resampler_get_rate(audio_resampler_, &cur_in_rate, &audio_out_rate);
|
||||
|
@ -232,7 +335,7 @@ void RtpAudioStream::decode()
|
|||
if (sample_rate != cur_in_rate) {
|
||||
ws_codec_resampler_set_rate(audio_resampler_, sample_rate, audio_out_rate);
|
||||
ws_codec_resampler_set_rate(visual_resampler_, sample_rate, visual_out_rate);
|
||||
// RTP_STREAM_DEBUG("Changed input rate from %u to %u Hz. Out is %u.", cur_in_rate, sample_rate, audio_out_rate_);
|
||||
RTP_STREAM_DEBUG("Changed input rate from %u to %u Hz. Out is %u.", cur_in_rate, sample_rate, audio_out_rate_);
|
||||
}
|
||||
}
|
||||
spx_uint32_t in_len = (spx_uint32_t)rtp_packet->info->info_payload_len;
|
||||
|
@ -248,12 +351,6 @@ void RtpAudioStream::decode()
|
|||
write_bytes = out_len * sample_bytes_;
|
||||
}
|
||||
|
||||
if (rtp_packet->info->info_seq_num != last_sequence_+1) {
|
||||
out_of_seq_timestamps_.append(stop_rel_time_);
|
||||
// XXX Add silence to tempfile_ and visual_samples_
|
||||
}
|
||||
last_sequence_ = rtp_packet->info->info_seq_num;
|
||||
|
||||
// Write the decoded, possibly-resampled audio to our temp file.
|
||||
tempfile_->write(write_buff, write_bytes);
|
||||
|
||||
|
@ -328,13 +425,76 @@ const QVector<double> RtpAudioStream::outOfSequenceTimestamps(bool relative)
|
|||
const QVector<double> RtpAudioStream::outOfSequenceSamples(int y_offset)
|
||||
{
|
||||
QVector<double> adj_samples;
|
||||
double scaled_offset = y_offset * stack_offset_;
|
||||
double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence
|
||||
for (int i = 0; i < out_of_seq_timestamps_.size(); i++) {
|
||||
adj_samples.append(scaled_offset);
|
||||
}
|
||||
return adj_samples;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::jitterDroppedTimestamps(bool relative)
|
||||
{
|
||||
if (relative) return jitter_drop_timestamps_;
|
||||
|
||||
QVector<double> adj_timestamps;
|
||||
for (int i = 0; i < jitter_drop_timestamps_.size(); i++) {
|
||||
adj_timestamps.append(jitter_drop_timestamps_[i] + start_abs_offset_);
|
||||
}
|
||||
return adj_timestamps;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::jitterDroppedSamples(int y_offset)
|
||||
{
|
||||
QVector<double> adj_samples;
|
||||
double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence
|
||||
for (int i = 0; i < jitter_drop_timestamps_.size(); i++) {
|
||||
adj_samples.append(scaled_offset);
|
||||
}
|
||||
return adj_samples;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::wrongTimestampTimestamps(bool relative)
|
||||
{
|
||||
if (relative) return wrong_timestamp_timestamps_;
|
||||
|
||||
QVector<double> adj_timestamps;
|
||||
for (int i = 0; i < wrong_timestamp_timestamps_.size(); i++) {
|
||||
adj_timestamps.append(wrong_timestamp_timestamps_[i] + start_abs_offset_);
|
||||
}
|
||||
return adj_timestamps;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::wrongTimestampSamples(int y_offset)
|
||||
{
|
||||
QVector<double> adj_samples;
|
||||
double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence
|
||||
for (int i = 0; i < wrong_timestamp_timestamps_.size(); i++) {
|
||||
adj_samples.append(scaled_offset);
|
||||
}
|
||||
return adj_samples;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::insertedSilenceTimestamps(bool relative)
|
||||
{
|
||||
if (relative) return silence_timestamps_;
|
||||
|
||||
QVector<double> adj_timestamps;
|
||||
for (int i = 0; i < silence_timestamps_.size(); i++) {
|
||||
adj_timestamps.append(silence_timestamps_[i] + start_abs_offset_);
|
||||
}
|
||||
return adj_timestamps;
|
||||
}
|
||||
|
||||
const QVector<double> RtpAudioStream::insertedSilenceSamples(int y_offset)
|
||||
{
|
||||
QVector<double> adj_samples;
|
||||
double scaled_offset = y_offset * stack_offset_; // XXX Should be different for seq, jitter, wrong & silence
|
||||
for (int i = 0; i < silence_timestamps_.size(); i++) {
|
||||
adj_samples.append(scaled_offset);
|
||||
}
|
||||
return adj_samples;
|
||||
}
|
||||
|
||||
quint32 RtpAudioStream::nearestPacket(double timestamp, bool is_relative)
|
||||
{
|
||||
if (packet_timestamps_.keys().count() < 1) return 0;
|
||||
|
@ -385,6 +545,23 @@ void RtpAudioStream::stopPlaying()
|
|||
emit finishedPlaying();
|
||||
}
|
||||
|
||||
void RtpAudioStream::writeSilence(int samples)
|
||||
{
|
||||
if (samples < 1 || audio_out_rate_ == 0) return;
|
||||
|
||||
unsigned silence_bytes = samples * sample_bytes_;
|
||||
char *silence_buff = (char *) g_malloc(silence_bytes);
|
||||
SAMPLE silence = 0;
|
||||
|
||||
RTP_STREAM_DEBUG("Writing %u silence samples", samples);
|
||||
memccpy(silence_buff, &silence, samples, sample_bytes_);
|
||||
tempfile_->write(silence_buff, silence_bytes);
|
||||
g_free(silence_buff);
|
||||
|
||||
QVector<qint16> visual_fill(samples * visual_sample_rate_ / audio_out_rate_, 0);
|
||||
visual_samples_ += visual_fill;
|
||||
}
|
||||
|
||||
void RtpAudioStream::outputStateChanged()
|
||||
{
|
||||
if (!audio_output_) return;
|
||||
|
|
|
@ -48,6 +48,8 @@ class RtpAudioStream : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum TimingMode { JitterBuffer, RtpTimestamp, Uninterrupted };
|
||||
|
||||
explicit RtpAudioStream(QObject *parent, struct _rtp_stream_info *rtp_stream);
|
||||
~RtpAudioStream();
|
||||
bool isMatch(const struct _rtp_stream_info *rtp_stream) const;
|
||||
|
@ -88,6 +90,45 @@ public:
|
|||
*/
|
||||
const QVector<double> outOfSequenceSamples(int y_offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Return a list of jitter dropped timestamps.
|
||||
* @return A set of timestamps suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> jitterDroppedTimestamps(bool relative = true);
|
||||
int jitterDropped() { return jitter_drop_timestamps_.size(); }
|
||||
/**
|
||||
* @brief Return a list of jitter dropped samples. Y value is constant.
|
||||
* @param y_offset Y axis offset to be used for stacking graphs.
|
||||
* @return A set of values suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> jitterDroppedSamples(int y_offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Return a list of wrong timestamps.
|
||||
* @return A set of timestamps suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> wrongTimestampTimestamps(bool relative = true);
|
||||
int wrongTimestamps() { return wrong_timestamp_timestamps_.size(); }
|
||||
/**
|
||||
* @brief Return a list of wrong timestamp samples. Y value is constant.
|
||||
* @param y_offset Y axis offset to be used for stacking graphs.
|
||||
* @return A set of values suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> wrongTimestampSamples(int y_offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Return a list of inserted silence timestamps.
|
||||
* @return A set of timestamps suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> insertedSilenceTimestamps(bool relative = true);
|
||||
int insertedSilences() { return silence_timestamps_.size(); }
|
||||
/**
|
||||
* @brief Return a list of wrong timestamp samples. Y value is constant.
|
||||
* @param y_offset Y axis offset to be used for stacking graphs.
|
||||
* @return A set of values suitable for passing to QCPGraph::setData.
|
||||
*/
|
||||
const QVector<double> insertedSilenceSamples(int y_offset = 0);
|
||||
|
||||
quint32 nearestPacket(double timestamp, bool is_relative = true);
|
||||
|
||||
QRgb color() { return color_; }
|
||||
|
@ -95,6 +136,9 @@ public:
|
|||
|
||||
QAudio::State outputState() const;
|
||||
|
||||
void setJitterBufferSize(int jitter_buffer_size) { jitter_buffer_size_ = jitter_buffer_size; }
|
||||
void setTimingMode(TimingMode timing_mode) { timing_mode_ = timing_mode; }
|
||||
|
||||
signals:
|
||||
void startedPlaying();
|
||||
void processedSecs(double secs);
|
||||
|
@ -114,7 +158,6 @@ private:
|
|||
quint32 ssrc_;
|
||||
|
||||
QVector<struct _rtp_packet *>rtp_packets_;
|
||||
int last_sequence_;
|
||||
QTemporaryFile *tempfile_;
|
||||
struct _GHashTable *decoders_hash_;
|
||||
QList<const struct _rtp_stream_info *>rtp_streams_;
|
||||
|
@ -130,9 +173,17 @@ private:
|
|||
QMap<double, quint32> packet_timestamps_;
|
||||
QVector<qint16> visual_samples_;
|
||||
QVector<double> out_of_seq_timestamps_;
|
||||
QVector<double> jitter_drop_timestamps_;
|
||||
QVector<double> wrong_timestamp_timestamps_;
|
||||
QVector<double> silence_timestamps_;
|
||||
qint16 max_sample_val_;
|
||||
QRgb color_;
|
||||
|
||||
int jitter_buffer_size_;
|
||||
TimingMode timing_mode_;
|
||||
|
||||
void writeSilence(int samples);
|
||||
|
||||
private slots:
|
||||
void outputStateChanged();
|
||||
void outputNotify();
|
||||
|
|
|
@ -183,7 +183,7 @@ void RtpPlayerDialog::retapPackets()
|
|||
cap_file_.retapPackets();
|
||||
remove_tap_listener(this);
|
||||
|
||||
rescanPackets();
|
||||
rescanPackets(true);
|
||||
}
|
||||
|
||||
void RtpPlayerDialog::rescanPackets(bool rescale_axes)
|
||||
|
@ -209,6 +209,21 @@ void RtpPlayerDialog::rescanPackets(bool rescale_axes)
|
|||
RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
|
||||
int y_offset = row_count - row - 1;
|
||||
|
||||
audio_stream->setJitterBufferSize((int) ui->jitterSpinBox->value());
|
||||
|
||||
RtpAudioStream::TimingMode timing_mode = RtpAudioStream::JitterBuffer;
|
||||
switch (ui->timingComboBox->currentIndex()) {
|
||||
case RtpAudioStream::RtpTimestamp:
|
||||
timing_mode = RtpAudioStream::RtpTimestamp;
|
||||
break;
|
||||
case RtpAudioStream::Uninterrupted:
|
||||
timing_mode = RtpAudioStream::Uninterrupted;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
audio_stream->setTimingMode(timing_mode);
|
||||
|
||||
audio_stream->decode();
|
||||
|
||||
// Waveform
|
||||
|
@ -246,6 +261,51 @@ void RtpPlayerDialog::rescanPackets(bool rescale_axes)
|
|||
seq_graph->removeFromLegend();
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_stream->jitterDropped() > 0) {
|
||||
// Jitter drops
|
||||
QCPGraph *seq_graph = ui->audioPlot->addGraph();
|
||||
seq_graph->setLineStyle(QCPGraph::lsNone);
|
||||
seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, tango_scarlet_red_5, Qt::white, 4)); // Arbitrary
|
||||
seq_graph->setSelectable(false);
|
||||
seq_graph->setData(audio_stream->jitterDroppedTimestamps(relative_timestamps), audio_stream->jitterDroppedSamples(y_offset));
|
||||
if (row < 1) {
|
||||
seq_graph->setName(tr("Jitter Drops"));
|
||||
show_legend = true;
|
||||
} else {
|
||||
seq_graph->removeFromLegend();
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_stream->wrongTimestamps() > 0) {
|
||||
// Wrong timestamps
|
||||
QCPGraph *seq_graph = ui->audioPlot->addGraph();
|
||||
seq_graph->setLineStyle(QCPGraph::lsNone);
|
||||
seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDiamond, tango_sky_blue_5, Qt::white, 4)); // Arbitrary
|
||||
seq_graph->setSelectable(false);
|
||||
seq_graph->setData(audio_stream->wrongTimestampTimestamps(relative_timestamps), audio_stream->wrongTimestampSamples(y_offset));
|
||||
if (row < 1) {
|
||||
seq_graph->setName(tr("Wrong Timestamps"));
|
||||
show_legend = true;
|
||||
} else {
|
||||
seq_graph->removeFromLegend();
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_stream->insertedSilences() > 0) {
|
||||
// Inserted silence
|
||||
QCPGraph *seq_graph = ui->audioPlot->addGraph();
|
||||
seq_graph->setLineStyle(QCPGraph::lsNone);
|
||||
seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, tango_butter_5, Qt::white, 4)); // Arbitrary
|
||||
seq_graph->setSelectable(false);
|
||||
seq_graph->setData(audio_stream->insertedSilenceTimestamps(relative_timestamps), audio_stream->insertedSilenceSamples(y_offset));
|
||||
if (row < 1) {
|
||||
seq_graph->setName(tr("Inserted Silence"));
|
||||
show_legend = true;
|
||||
} else {
|
||||
seq_graph->removeFromLegend();
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->audioPlot->legend->setVisible(show_legend);
|
||||
|
||||
|
@ -381,6 +441,7 @@ void RtpPlayerDialog::updateWidgets()
|
|||
{
|
||||
bool enable_play = true;
|
||||
bool enable_stop = false;
|
||||
bool enable_timing = true;
|
||||
|
||||
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
|
||||
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
|
||||
|
@ -389,12 +450,18 @@ void RtpPlayerDialog::updateWidgets()
|
|||
if (audio_stream->outputState() != QAudio::IdleState) {
|
||||
enable_play = false;
|
||||
enable_stop = true;
|
||||
enable_timing = false;
|
||||
}
|
||||
}
|
||||
|
||||
ui->playButton->setEnabled(enable_play);
|
||||
ui->stopButton->setEnabled(enable_stop);
|
||||
cur_play_pos_->setVisible(enable_stop);
|
||||
|
||||
ui->jitterSpinBox->setEnabled(enable_timing);
|
||||
ui->timingComboBox->setEnabled(enable_timing);
|
||||
ui->todCheckBox->setEnabled(enable_timing);
|
||||
|
||||
ui->audioPlot->replot();
|
||||
}
|
||||
|
||||
|
@ -442,7 +509,7 @@ void RtpPlayerDialog::resetXAxis()
|
|||
|
||||
void RtpPlayerDialog::setPlayPosition(double secs)
|
||||
{
|
||||
secs+= ui->audioPlot->xAxis->range().lower;
|
||||
secs+= start_rel_time_;
|
||||
double cur_secs = cur_play_pos_->point1->key();
|
||||
if (secs > cur_secs) {
|
||||
cur_play_pos_->point1->setCoords(secs, 0.0);
|
||||
|
@ -514,7 +581,7 @@ void RtpPlayerDialog::panXAxis(int x_pixels)
|
|||
|
||||
void RtpPlayerDialog::on_playButton_clicked()
|
||||
{
|
||||
double left = ui->audioPlot->xAxis->range().lower;
|
||||
double left = start_rel_time_;
|
||||
cur_play_pos_->point1->setCoords(left, 0.0);
|
||||
cur_play_pos_->point2->setCoords(left, 1.0);
|
||||
cur_play_pos_->setVisible(true);
|
||||
|
@ -624,12 +691,22 @@ int RtpPlayerDialog::getHoveredPacket()
|
|||
return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked());
|
||||
}
|
||||
|
||||
void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double)
|
||||
{
|
||||
rescanPackets();
|
||||
}
|
||||
|
||||
void RtpPlayerDialog::on_timingComboBox_currentIndexChanged(int)
|
||||
{
|
||||
rescanPackets();
|
||||
}
|
||||
|
||||
void RtpPlayerDialog::on_todCheckBox_toggled(bool)
|
||||
{
|
||||
QCPAxis *x_axis = ui->audioPlot->xAxis;
|
||||
double old_lowest = getLowestTimestamp();
|
||||
|
||||
rescanPackets(false);
|
||||
rescanPackets();
|
||||
x_axis->moveRange(getLowestTimestamp() - old_lowest);
|
||||
ui->audioPlot->replot();
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ private slots:
|
|||
void retapPackets();
|
||||
/** Clear, decode, and redraw each stream.
|
||||
*/
|
||||
void rescanPackets(bool rescale_axes = true);
|
||||
void rescanPackets(bool rescale_axes = false);
|
||||
void updateWidgets();
|
||||
void graphClicked(QMouseEvent *event);
|
||||
void mouseMoved(QMouseEvent *);
|
||||
|
@ -104,6 +104,8 @@ private slots:
|
|||
void on_actionMoveRight1_triggered();
|
||||
void on_actionGoToPacket_triggered();
|
||||
void on_streamTreeWidget_itemSelectionChanged();
|
||||
void on_jitterSpinBox_valueChanged(double);
|
||||
void on_timingComboBox_currentIndexChanged(int);
|
||||
void on_todCheckBox_toggled(bool checked);
|
||||
void on_buttonBox_helpRequested();
|
||||
|
||||
|
@ -129,7 +131,6 @@ private:
|
|||
int getHoveredPacket();
|
||||
|
||||
#else // QT_MULTIMEDIA_LIB
|
||||
|
||||
private:
|
||||
Ui::RtpPlayerDialog *ui;
|
||||
#endif // QT_MULTIMEDIA_LIB
|
||||
|
|
|
@ -164,10 +164,13 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="doubleSpinBox">
|
||||
<widget class="QDoubleSpinBox" name="jitterSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The simulated jitter buffer in milliseconds.</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>500.000000000000000</double>
|
||||
</property>
|
||||
|
@ -200,7 +203,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="comboBox">
|
||||
<widget class="QComboBox" name="timingComboBox">
|
||||
<property name="toolTip">
|
||||
<string><strong>Jitter Buffer</strong>: Use jitter buffer to simulate the RTP stream as heard by the end user.
|
||||
<br/>
|
||||
|
|
|
@ -110,7 +110,7 @@ struct _rtpstream_tapinfo {
|
|||
gboolean is_registered; /**< if the tap listener is currently registered or not */
|
||||
};
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
#define RTP_STREAM_DEBUG(...) { \
|
||||
char *RTP_STREAM_DEBUG_MSG = g_strdup_printf(__VA_ARGS__); \
|
||||
g_warning("rtp_stream: %s:%d %s", G_STRFUNC, __LINE__, RTP_STREAM_DEBUG_MSG); \
|
||||
|
|
Loading…
Reference in New Issue