aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorJiri Novak <j.novak@netsystem.cz>2016-12-13 13:28:30 +0100
committerAnders Broman <a.broman58@gmail.com>2016-12-15 05:16:29 +0000
commit9090afbfe9a4f9a422409c3f9d410bc3c14fc741 (patch)
treeba111a5eee19019502dc31a2095aaf52098d23db /ui
parent1afbab69956c32b341001dd20ff667036fe1f8f5 (diff)
Save RTP audio to file: RTP Stream Analysis dialog allows save audio for non G.711 codecs and mixed codecs
- spaghetti code for save was split into separate functions - code saves G.711 only, all other codecs are saved as silence with correct duration - code is ready to include other codecs - code supports 8000 Hz sampling rate only, other rates are rejected with warning - bidirectional stream (forward and reverse) creates stereo .au file - output is based on timestamps in RTP streams - save operation is slower than before because it is set of seek() - one per each codec sample - code allows align of save audio: - as it is - each stream is saved from its beginning, no aling - to start of each other - later stream is prepended with silence - align saved audio to beginning of capture file - each stream is prepended with silence - save to raw works correctly now - only payload is saved - old code was inserting G.711 silence time to time to raw data Bug: 13242 Change-Id: I74d02a1cc1c75acf9ffe930d078c00a0555cbfb6 Reviewed-on: https://code.wireshark.org/review/19245 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'ui')
-rw-r--r--ui/qt/rtp_analysis_dialog.cpp711
-rw-r--r--ui/qt/rtp_analysis_dialog.h22
-rw-r--r--ui/qt/rtp_analysis_dialog.ui66
-rw-r--r--ui/tap-rtp-analysis.h7
-rw-r--r--ui/tap-rtp-common.c11
5 files changed, 511 insertions, 306 deletions
diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp
index de6aae3f30..d0eeb71418 100644
--- a/ui/qt/rtp_analysis_dialog.cpp
+++ b/ui/qt/rtp_analysis_dialog.cpp
@@ -269,9 +269,17 @@ RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, struct _r
stream_ctx_menu_.addAction(ui->actionGoToPacket);
stream_ctx_menu_.addAction(ui->actionNextProblem);
stream_ctx_menu_.addSeparator();
- stream_ctx_menu_.addAction(ui->actionSaveAudio);
- stream_ctx_menu_.addAction(ui->actionSaveForwardAudio);
- stream_ctx_menu_.addAction(ui->actionSaveReverseAudio);
+ stream_ctx_menu_.addAction(ui->actionSaveAudioUnsync);
+ stream_ctx_menu_.addAction(ui->actionSaveForwardAudioUnsync);
+ stream_ctx_menu_.addAction(ui->actionSaveReverseAudioUnsync);
+ stream_ctx_menu_.addSeparator();
+ stream_ctx_menu_.addAction(ui->actionSaveAudioSyncStream);
+ stream_ctx_menu_.addAction(ui->actionSaveForwardAudioSyncStream);
+ stream_ctx_menu_.addAction(ui->actionSaveReverseAudioSyncStream);
+ stream_ctx_menu_.addSeparator();
+ stream_ctx_menu_.addAction(ui->actionSaveAudioSyncFile);
+ stream_ctx_menu_.addAction(ui->actionSaveForwardAudioSyncFile);
+ stream_ctx_menu_.addAction(ui->actionSaveReverseAudioSyncFile);
stream_ctx_menu_.addSeparator();
stream_ctx_menu_.addAction(ui->actionSaveCsv);
stream_ctx_menu_.addAction(ui->actionSaveForwardCsv);
@@ -332,15 +340,29 @@ RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, struct _r
if (fwd_tempfile_->error() != QFile::NoError || rev_tempfile_->error() != QFile::NoError) {
err_str_ = tr("Unable to save RTP data.");
- ui->actionSaveAudio->setEnabled(false);
- ui->actionSaveForwardAudio->setEnabled(false);
- ui->actionSaveReverseAudio->setEnabled(false);
+ ui->actionSaveAudioUnsync->setEnabled(false);
+ ui->actionSaveForwardAudioUnsync->setEnabled(false);
+ ui->actionSaveReverseAudioUnsync->setEnabled(false);
+ ui->actionSaveAudioSyncStream->setEnabled(false);
+ ui->actionSaveForwardAudioSyncStream->setEnabled(false);
+ ui->actionSaveReverseAudioSyncStream->setEnabled(false);
+ ui->actionSaveAudioSyncFile->setEnabled(false);
+ ui->actionSaveForwardAudioSyncFile->setEnabled(false);
+ ui->actionSaveReverseAudioSyncFile->setEnabled(false);
}
QMenu *save_menu = new QMenu();
- save_menu->addAction(ui->actionSaveAudio);
- save_menu->addAction(ui->actionSaveForwardAudio);
- save_menu->addAction(ui->actionSaveReverseAudio);
+ save_menu->addAction(ui->actionSaveAudioUnsync);
+ save_menu->addAction(ui->actionSaveForwardAudioUnsync);
+ save_menu->addAction(ui->actionSaveReverseAudioUnsync);
+ save_menu->addSeparator();
+ save_menu->addAction(ui->actionSaveAudioSyncStream);
+ save_menu->addAction(ui->actionSaveForwardAudioSyncStream);
+ save_menu->addAction(ui->actionSaveReverseAudioSyncStream);
+ save_menu->addSeparator();
+ save_menu->addAction(ui->actionSaveAudioSyncFile);
+ save_menu->addAction(ui->actionSaveForwardAudioSyncFile);
+ save_menu->addAction(ui->actionSaveReverseAudioSyncFile);
save_menu->addSeparator();
save_menu->addAction(ui->actionSaveCsv);
save_menu->addAction(ui->actionSaveForwardCsv);
@@ -432,11 +454,17 @@ void RtpAnalysisDialog::updateWidgets()
hint.append(tr(" G: Go to packet, N: Next problem packet"));
}
- bool enable_save_fwd_audio = fwd_tempfile_->isOpen() && (save_payload_error_ == TAP_RTP_NO_ERROR);
- bool enable_save_rev_audio = rev_tempfile_->isOpen() && (save_payload_error_ == TAP_RTP_NO_ERROR);
- ui->actionSaveAudio->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveForwardAudio->setEnabled(enable_save_fwd_audio);
- ui->actionSaveReverseAudio->setEnabled(enable_save_rev_audio);
+ bool enable_save_fwd_audio = fwd_statinfo_.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR);
+ bool enable_save_rev_audio = rev_statinfo_.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR);
+ ui->actionSaveAudioUnsync->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveForwardAudioUnsync->setEnabled(enable_save_fwd_audio);
+ ui->actionSaveReverseAudioUnsync->setEnabled(enable_save_rev_audio);
+ ui->actionSaveAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveForwardAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveReverseAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveAudioSyncFile->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
+ ui->actionSaveForwardAudioSyncFile->setEnabled(enable_save_fwd_audio);
+ ui->actionSaveReverseAudioSyncFile->setEnabled(enable_save_rev_audio);
bool enable_save_fwd_csv = ui->forwardTreeWidget->topLevelItemCount() > 0;
bool enable_save_rev_csv = ui->reverseTreeWidget->topLevelItemCount() > 0;
@@ -533,19 +561,49 @@ void RtpAnalysisDialog::on_rDeltaCheckBox_toggled(bool checked)
updateGraph();
}
-void RtpAnalysisDialog::on_actionSaveAudio_triggered()
+void RtpAnalysisDialog::on_actionSaveAudioUnsync_triggered()
+{
+ saveAudio(dir_both_, sync_unsync_);
+}
+
+void RtpAnalysisDialog::on_actionSaveForwardAudioUnsync_triggered()
+{
+ saveAudio(dir_forward_, sync_unsync_);
+}
+
+void RtpAnalysisDialog::on_actionSaveReverseAudioUnsync_triggered()
+{
+ saveAudio(dir_reverse_, sync_unsync_);
+}
+
+void RtpAnalysisDialog::on_actionSaveAudioSyncStream_triggered()
+{
+ saveAudio(dir_both_, sync_sync_stream_);
+}
+
+void RtpAnalysisDialog::on_actionSaveForwardAudioSyncStream_triggered()
+{
+ saveAudio(dir_forward_, sync_sync_stream_);
+}
+
+void RtpAnalysisDialog::on_actionSaveReverseAudioSyncStream_triggered()
+{
+ saveAudio(dir_reverse_, sync_sync_stream_);
+}
+
+void RtpAnalysisDialog::on_actionSaveAudioSyncFile_triggered()
{
- saveAudio(dir_both_);
+ saveAudio(dir_both_, sync_sync_file_);
}
-void RtpAnalysisDialog::on_actionSaveForwardAudio_triggered()
+void RtpAnalysisDialog::on_actionSaveForwardAudioSyncFile_triggered()
{
- saveAudio(dir_forward_);
+ saveAudio(dir_forward_, sync_sync_file_);
}
-void RtpAnalysisDialog::on_actionSaveReverseAudio_triggered()
+void RtpAnalysisDialog::on_actionSaveReverseAudioSyncFile_triggered()
{
- saveAudio(dir_reverse_);
+ saveAudio(dir_reverse_, sync_sync_file_);
}
void RtpAnalysisDialog::on_actionSaveCsv_triggered()
@@ -730,10 +788,6 @@ void RtpAnalysisDialog::addPacket(bool forward, packet_info *pinfo, const _rtp_i
}
-// rtp_analysis.c:rtp_packet_save_payload
-const unsigned int max_silence_ticks_ = 1000000;
-const guint8 silence_pcmu_ = 0xff;
-const guint8 silence_pcma_ = 0x55;
void RtpAnalysisDialog::savePayload(QTemporaryFile *tmpfile, tap_rtp_stat_t *statinfo, packet_info *pinfo, const _rtp_info *rtpinfo)
{
/* Is this the first packet we got in this direction? */
@@ -776,51 +830,12 @@ void RtpAnalysisDialog::savePayload(QTemporaryFile *tmpfile, tap_rtp_stat_t *sta
return;
}
- /* Do we need to insert some silence? */
- if ((rtpinfo->info_marker_set) &&
- !(statinfo->flags & STAT_FLAG_FIRST) &&
- !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
- (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)))
- {
- /* the amount of silence should be the difference between
- * the last timestamp and the current one minus x
- * x should equal the amount of information in the last frame
- * XXX not done yet */
- for (unsigned int i = 0;
- (i < (statinfo->delta_timestamp - rtpinfo->info_payload_len - rtpinfo->info_padding_count))
- && (i < max_silence_ticks_);
- i++) {
- guint8 tmp;
- size_t nchars;
-
- switch (statinfo->reg_pt) {
- case PT_PCMU:
- tmp = silence_pcmu_;
- break;
- case PT_PCMA:
- tmp = silence_pcma_;
- break;
- default:
- tmp = 0;
- break;
- }
- nchars = tmpfile->write((char *)&tmp, 1);
- if (nchars != 1) {
- /* Write error or short write */
- tmpfile->close();
- err_str_ = tr("Can't save in a file: File I/O problem.");
- save_payload_error_ = TAP_RTP_FILE_IO_ERROR;
- return;
- }
- }
- }
-
-
if ((rtpinfo->info_payload_type == PT_CN) ||
(rtpinfo->info_payload_type == PT_CN_OLD)) {
} else { /* All other payloads */
const char *data;
size_t nchars;
+ tap_rtp_save_data_t save_data;
if (!rtpinfo->info_all_data_present) {
/* Not all the data was captured. */
@@ -834,14 +849,29 @@ void RtpAnalysisDialog::savePayload(QTemporaryFile *tmpfile, tap_rtp_stat_t *sta
* payload, that is, at the beginning of the RTP data
* plus the offset of the payload from the beginning
* of the RTP data */
- data = (const char *) rtpinfo->info_data + rtpinfo->info_payload_offset;
- nchars = tmpfile->write(data, rtpinfo->info_payload_len - rtpinfo->info_padding_count);
- if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
- /* Write error or short write */
- err_str_ = tr("Can't save in a file: File I/O problem.");
- save_payload_error_ = TAP_RTP_FILE_IO_ERROR;
- tmpfile->close();
- return;
+ data = (const char *) rtpinfo->info_data + rtpinfo->info_payload_offset;
+
+ /* Store information about timestamp, payload_type and payload in file */
+ save_data.timestamp = statinfo->timestamp;
+ save_data.payload_type = rtpinfo->info_payload_type;
+ save_data.payload_len = rtpinfo->info_payload_len - rtpinfo->info_padding_count;
+ nchars = tmpfile->write((char *)&save_data, sizeof(save_data));
+ if (nchars != sizeof(save_data)) {
+ /* Write error or short write */
+ err_str_ = tr("Can't save in a file: File I/O problem.");
+ save_payload_error_ = TAP_RTP_FILE_IO_ERROR;
+ tmpfile->close();
+ return;
+ }
+ if (save_data.payload_len > 0) {
+ nchars = tmpfile->write(data, save_data.payload_len);
+ if (nchars != save_data.payload_len) {
+ /* Write error or short write */
+ err_str_ = tr("Can't save in a file: File I/O problem.");
+ save_payload_error_ = TAP_RTP_FILE_IO_ERROR;
+ tmpfile->close();
+ return;
+ }
}
return;
}
@@ -1045,9 +1075,321 @@ void RtpAnalysisDialog::showPlayer()
#endif // QT_MULTIMEDIA_LIB
}
+/* Convert one packet payload to samples in row */
+/* It supports G.711 now, but can be extended to any other codecs */
+size_t RtpAnalysisDialog::convert_payload_to_samples(unsigned int payload_type, QTemporaryFile *tempfile, gchar *pd_out, size_t payload_len)
+{
+ size_t sample_count;
+ char f_rawvalue;
+ gint16 sample;
+ gchar pd[4];
+
+ sample_count = 0;
+ if (payload_type == PT_PCMU) {
+ /* Output sample count is same as input sample count for G.711 */
+ sample_count = payload_len;
+ for(size_t i = 0; i < payload_len; i++) {
+ tempfile->read((char *)&f_rawvalue, sizeof(f_rawvalue));
+ sample = ulaw2linear((unsigned char)f_rawvalue);
+ phton16(pd, sample);
+ pd_out[2*i] = pd[0];
+ pd_out[2*i+1] = pd[1];
+ }
+ } else if (payload_type == PT_PCMA) {
+ /* Output sample count is same as input sample count for G.711 */
+ sample_count = payload_len;
+ for(size_t i = 0; i < payload_len; i++) {
+ tempfile->read((char *)&f_rawvalue, sizeof(f_rawvalue));
+ sample = alaw2linear((unsigned char)f_rawvalue);
+ phton16(pd, sample);
+ pd_out[2*i] = pd[0];
+ pd_out[2*i+1] = pd[1];
+ }
+ } else {
+ /* Read payload, but ignore it */
+ sample_count = 0;
+ for(size_t i = 0; i < payload_len; i++) {
+ tempfile->read((char *)&f_rawvalue, sizeof(f_rawvalue));
+ }
+ }
+
+ return sample_count;
+}
+
+gboolean RtpAnalysisDialog::saveAudioAUSilence(size_t total_len, QFile *save_file, gboolean *stop_flag)
+{
+ size_t nchars;
+ gchar pd_out[2*4000];
+ gint16 silence;
+ gchar pd[4];
+
+ silence = 0x0000;
+ phton16(pd, silence);
+ pd_out[0] = pd[0];
+ pd_out[1] = pd[1];
+ /* Fill whole file with silence */
+ for(size_t i=0; i<total_len; i++) {
+ if (*stop_flag) {
+ return FALSE;
+ }
+ nchars = save_file->write((const char *)pd_out, 2);
+ if (nchars < 2) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean RtpAnalysisDialog::saveAudioAUUnidir(tap_rtp_stat_t statinfo, QTemporaryFile *tempfile, QFile *save_file, size_t header_end, gboolean *stop_flag, gboolean interleave, size_t prefix_silence)
+{
+ size_t nchars;
+ gchar pd_out[2*4000];
+ gchar pd[4];
+ tap_rtp_save_data_t save_data;
+
+ while (sizeof(save_data) == tempfile->read((char *)&save_data,sizeof(save_data))) {
+ size_t sample_count;
+
+ if (*stop_flag) {
+ return FALSE;
+ }
+ ui->progressFrame->setValue(tempfile->pos() * 100 / tempfile->size());
+
+ sample_count=convert_payload_to_samples(save_data.payload_type, tempfile ,pd_out, save_data.payload_len);
+
+ if (sample_count > 0 ) {
+ nchars = 0;
+ /* Save payload samples with optional interleaving */
+ for (size_t i = 0; i < sample_count; i++) {
+ pd[0] = pd_out[ 2 * i ];
+ pd[1] = pd_out[ 2 * i + 1 ];
+ if (interleave) {
+ save_file->seek(header_end+(prefix_silence + (guint32_wraparound_diff(save_data.timestamp, statinfo.first_timestamp) + i)) * 4);
+ } else {
+ save_file->seek(header_end+(prefix_silence + (guint32_wraparound_diff(save_data.timestamp, statinfo.first_timestamp) + i)) * 2);
+ }
+ nchars += save_file->write((const char *)pd, 2);
+ }
+ if (nchars < sample_count*2) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean RtpAnalysisDialog::saveAudioAUBidir(tap_rtp_stat_t fwd_statinfo, tap_rtp_stat_t rev_statinfo, QTemporaryFile *fwd_tempfile, QTemporaryFile *rev_tempfile, QFile *save_file, size_t header_end, gboolean *stop_flag, size_t prefix_silence_fwd, size_t prefix_silence_rev)
+{
+ if (! saveAudioAUUnidir(fwd_statinfo, fwd_tempfile, save_file, header_end, stop_flag, TRUE, prefix_silence_fwd))
+ {
+ return FALSE;
+ }
+ if (! saveAudioAUUnidir(rev_statinfo, rev_tempfile, save_file, header_end+2, stop_flag, TRUE, prefix_silence_rev))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean RtpAnalysisDialog::saveAudioAU(StreamDirection direction, QFile *save_file, gboolean *stop_flag, RtpAnalysisDialog::SyncType sync)
+{
+ gchar pd[4];
+ size_t nchars;
+ size_t header_end;
+ size_t fwd_total_len;
+ size_t rev_total_len;
+ size_t total_len;
+
+ /* First we write the .au header. XXX Hope this is endian independent */
+ /* the magic word 0x2e736e64 == .snd */
+ phton32(pd, 0x2e736e64);
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+ /* header offset == 24 bytes */
+ phton32(pd, 24);
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+ /* total length; it is permitted to set this to 0xffffffff */
+ phton32(pd, 0xffffffff);
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+ /* encoding format == 16-bit linear PCM */
+ phton32(pd, 3);
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+ /* sample rate == 8000 Hz */
+ phton32(pd, 8000);
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+ /* channels == 1 or == 2 */
+ switch (direction) {
+ case dir_forward_: {
+ phton32(pd, 1);
+ break;
+ }
+ case dir_reverse_: {
+ phton32(pd, 1);
+ break;
+ }
+ case dir_both_: {
+ phton32(pd, 2);
+ break;
+ }
+ }
+ nchars = save_file->write((const char *)pd, 4);
+ if (nchars != 4)
+ return FALSE;
+
+ header_end=save_file->pos();
+
+ bool two_channels = rev_statinfo_.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR);
+ double t_min = MIN(fwd_statinfo_.start_time, rev_statinfo_.start_time);
+ double t_fwd_diff = fwd_statinfo_.start_time - t_min;
+ double t_rev_diff = rev_statinfo_.start_time - t_min;
+ size_t fwd_samples_diff = 0;
+ size_t rev_samples_diff = 0;
+ size_t bidir_samples_diff = 0;
+
+ switch (sync) {
+ case sync_unsync_: {
+ fwd_samples_diff = 0;
+ rev_samples_diff = 0;
+ bidir_samples_diff = 0;
+ break;
+ }
+ case sync_sync_stream_: {
+ if (! two_channels) {
+ /* Only forward channel */
+ /* This branch should not be reached ever */
+ QMessageBox::warning(this, tr("Warning"), tr("Can't synchronize when only one channel is selected"));
+ return FALSE;
+ } else {
+ /* Two channels */
+ fwd_samples_diff = t_fwd_diff*8000/1000;
+ rev_samples_diff = t_rev_diff*8000/1000;
+ bidir_samples_diff = 0;
+ }
+ break;
+ }
+ case sync_sync_file_: {
+ if (! two_channels) {
+ /* Only forward channel */
+ fwd_samples_diff = t_fwd_diff*8000/1000;
+ rev_samples_diff = 0;
+ bidir_samples_diff = fwd_samples_diff;
+ } else {
+ /* Two channels */
+ fwd_samples_diff = t_fwd_diff*8000/1000;
+ rev_samples_diff = t_rev_diff*8000/1000;
+ bidir_samples_diff = t_min*8000/1000;
+ }
+ break;
+ }
+ }
+
+ switch (direction) {
+ /* Only forward direction */
+ case dir_forward_: {
+ fwd_total_len = guint32_wraparound_diff(fwd_statinfo_.timestamp, fwd_statinfo_.first_timestamp) + fwd_statinfo_.last_payload_len;
+ if (! saveAudioAUSilence(fwd_total_len + fwd_samples_diff + bidir_samples_diff, save_file, stop_flag))
+ {
+ return FALSE;
+ }
+ if (! saveAudioAUUnidir(fwd_statinfo_, fwd_tempfile_, save_file, header_end, stop_flag, FALSE, fwd_samples_diff + bidir_samples_diff))
+ {
+ return FALSE;
+ }
+ break;
+ }
+ /* Only reverse direction */
+ case dir_reverse_: {
+ rev_total_len = guint32_wraparound_diff(rev_statinfo_.timestamp, rev_statinfo_.first_timestamp) + rev_statinfo_.last_payload_len;
+ if (! saveAudioAUSilence(rev_total_len + rev_samples_diff + bidir_samples_diff, save_file, stop_flag))
+ {
+ return FALSE;
+ }
+ if (! saveAudioAUUnidir(rev_statinfo_, rev_tempfile_, save_file, header_end, stop_flag, FALSE, rev_samples_diff + bidir_samples_diff))
+ {
+ return FALSE;
+ }
+ break;
+ }
+ /* Both directions */
+ case dir_both_: {
+ fwd_total_len = guint32_wraparound_diff(fwd_statinfo_.timestamp, fwd_statinfo_.first_timestamp) + fwd_statinfo_.last_payload_len;
+ rev_total_len = guint32_wraparound_diff(rev_statinfo_.timestamp, rev_statinfo_.first_timestamp) + rev_statinfo_.last_payload_len;
+ total_len = MAX(fwd_total_len + fwd_samples_diff, rev_total_len + rev_samples_diff);
+ if (! saveAudioAUSilence((total_len + bidir_samples_diff) * 2, save_file, stop_flag))
+ {
+ return FALSE;
+ }
+ if (! saveAudioAUBidir(fwd_statinfo_, rev_statinfo_, fwd_tempfile_, rev_tempfile_, save_file, header_end, stop_flag, fwd_samples_diff + bidir_samples_diff, rev_samples_diff + bidir_samples_diff))
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean RtpAnalysisDialog::saveAudioRAW(StreamDirection direction, QFile *save_file, gboolean *stop_flag)
+{
+ QFile *tempfile;
+ tap_rtp_save_data_t save_data;
+
+ switch (direction) {
+ /* Only forward direction */
+ case dir_forward_: {
+ tempfile = fwd_tempfile_;
+ break;
+ }
+ /* Only reversed direction */
+ case dir_reverse_: {
+ tempfile = rev_tempfile_;
+ break;
+ }
+ default: {
+ return FALSE;
+ }
+ }
+
+ /* Copy just payload */
+ while (sizeof(save_data) == tempfile->read((char *)&save_data,sizeof(save_data))) {
+ char f_rawvalue;
+
+ if (*stop_flag) {
+ return FALSE;
+ }
+
+ ui->progressFrame->setValue(tempfile->pos() * 100 / fwd_tempfile_->size());
+
+ if (save_data.payload_len > 0) {
+ for(size_t i = 0; i < save_data.payload_len; i++) {
+ if (sizeof(f_rawvalue) != tempfile->read((char *)&f_rawvalue, sizeof(f_rawvalue))) {
+ return FALSE;
+ }
+ if (sizeof(f_rawvalue) != save_file->write((char *)&f_rawvalue, sizeof(f_rawvalue))) {
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
// rtp_analysis.c:copy_file
enum { save_audio_none_, save_audio_au_, save_audio_raw_ };
-void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction)
+void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction, RtpAnalysisDialog::SyncType sync)
{
if (!fwd_tempfile_->isOpen() || !rev_tempfile_->isOpen()) return;
@@ -1062,7 +1404,7 @@ void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction)
break;
case dir_both_:
default:
- caption = tr("Save audio");
+ caption = tr("Save forward and reverse stream audio");
break;
}
@@ -1093,21 +1435,8 @@ void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction)
return;
}
- if (save_format == save_audio_au_) {
- if ((((direction == dir_forward_) || (direction == dir_both_)) &&
- (fwd_statinfo_.pt != PT_PCMU) && (fwd_statinfo_.pt != PT_PCMA)) ||
- (((direction == dir_reverse_) || (direction == dir_both_)) &&
- (rev_statinfo_.pt != PT_PCMU) && (rev_statinfo_.pt != PT_PCMA))) {
- QMessageBox::warning(this, tr("Warning"), tr("Can't save in a file: saving in au format supported only for alaw/ulaw streams"));
- return;
- }
- }
-
QFile save_file(file_path);
- gint16 sample;
- gchar pd[4];
gboolean stop_flag = FALSE;
- size_t nchars;
save_file.open(QIODevice::WriteOnly);
fwd_tempfile_->seek(0);
@@ -1121,210 +1450,20 @@ void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction)
ui->hintLabel->setText(tr("Saving %1" UTF8_HORIZONTAL_ELLIPSIS).arg(save_file.fileName()));
ui->progressFrame->showProgress(true, true, &stop_flag);
- if (save_format == save_audio_au_) { /* au format */
- /* First we write the .au header. XXX Hope this is endian independent */
- /* the magic word 0x2e736e64 == .snd */
- phton32(pd, 0x2e736e64);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
- /* header offset == 24 bytes */
- phton32(pd, 24);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
- /* total length; it is permitted to set this to 0xffffffff */
- phton32(pd, 0xffffffff);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
- /* encoding format == 16-bit linear PCM */
- phton32(pd, 3);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
- /* sample rate == 8000 Hz */
- phton32(pd, 8000);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
- /* channels == 1 */
- phton32(pd, 1);
- nchars = save_file.write((const char *)pd, 4);
- if (nchars != 4)
- goto copy_file_err;
-
- switch (direction) {
- /* Only forward direction */
- case dir_forward_:
- {
- char f_rawvalue;
- while (fwd_tempfile_->getChar(&f_rawvalue)) {
- if (stop_flag) {
- break;
- }
- ui->progressFrame->setValue(fwd_tempfile_->pos() * 100 / fwd_tempfile_->size());
-
- if (fwd_statinfo_.pt == PT_PCMU) {
- sample = ulaw2linear((unsigned char)f_rawvalue);
- phton16(pd, sample);
- } else if (fwd_statinfo_.pt == PT_PCMA) {
- sample = alaw2linear((unsigned char)f_rawvalue);
- phton16(pd, sample);
- } else {
- goto copy_file_err;
- }
-
- nchars = save_file.write((const char *)pd, 2);
- if (nchars < 2) {
- goto copy_file_err;
- }
- }
- break;
- }
- /* Only reverse direction */
- case dir_reverse_:
- {
- char r_rawvalue;
- while (rev_tempfile_->getChar(&r_rawvalue)) {
- if (stop_flag) {
- break;
- }
- ui->progressFrame->setValue(rev_tempfile_->pos() * 100 / rev_tempfile_->size());
-
- if (rev_statinfo_.pt == PT_PCMU) {
- sample = ulaw2linear((unsigned char)r_rawvalue);
- phton16(pd, sample);
- } else if (rev_statinfo_.pt == PT_PCMA) {
- sample = alaw2linear((unsigned char)r_rawvalue);
- phton16(pd, sample);
- } else {
- goto copy_file_err;
- }
-
- nchars = save_file.write((const char *)pd, 2);
- if (nchars < 2) {
- goto copy_file_err;
- }
- }
- break;
- }
- /* Both directions */
- case dir_both_:
- {
- char f_rawvalue, r_rawvalue;
- guint32 f_write_silence = 0;
- guint32 r_write_silence = 0;
- /* since conversation in one way can start later than in the other one,
- * we have to write some silence information for one channel */
- if (fwd_statinfo_.start_time > rev_statinfo_.start_time) {
- f_write_silence = (guint32)
- ((fwd_statinfo_.start_time - rev_statinfo_.start_time)
- * (8000/1000));
- } else if (fwd_statinfo_.start_time < rev_statinfo_.start_time) {
- r_write_silence = (guint32)
- ((rev_statinfo_.start_time - fwd_statinfo_.start_time)
- * (8000/1000));
- }
- for (;;) {
- if (stop_flag) {
- break;
- }
- int fwd_pct = (fwd_tempfile_->size() > 0 ? fwd_tempfile_->pos() * 100 / fwd_tempfile_->size() : 0);
- int rev_pct = (rev_tempfile_->size() > 0 ? rev_tempfile_->pos() * 100 / rev_tempfile_->size() : 0);
- ui->progressFrame->setValue(qMin(fwd_pct, rev_pct));
-
- if (f_write_silence > 0) {
- rev_tempfile_->getChar(&r_rawvalue);
- switch (fwd_statinfo_.reg_pt) {
- case PT_PCMU:
- f_rawvalue = silence_pcmu_;
- break;
- case PT_PCMA:
- f_rawvalue = silence_pcma_;
- break;
- default:
- f_rawvalue = 0;
- break;
- }
- f_write_silence--;
- } else if (r_write_silence > 0) {
- fwd_tempfile_->getChar(&f_rawvalue);
- switch (rev_statinfo_.reg_pt) {
- case PT_PCMU:
- r_rawvalue = silence_pcmu_;
- break;
- case PT_PCMA:
- r_rawvalue = silence_pcma_;
- break;
- default:
- r_rawvalue = 0;
- break;
- }
- r_write_silence--;
- } else {
- fwd_tempfile_->getChar(&f_rawvalue);
- rev_tempfile_->getChar(&r_rawvalue);
- }
- if (fwd_tempfile_->atEnd() && rev_tempfile_->atEnd())
- break;
- if ((fwd_statinfo_.pt == PT_PCMU)
- && (rev_statinfo_.pt == PT_PCMU)) {
- sample = (ulaw2linear((unsigned char)r_rawvalue)
- + ulaw2linear((unsigned char)f_rawvalue)) / 2;
- phton16(pd, sample);
- }
- else if ((fwd_statinfo_.pt == PT_PCMA)
- && (rev_statinfo_.pt == PT_PCMA)) {
- sample = (alaw2linear((unsigned char)r_rawvalue)
- + alaw2linear((unsigned char)f_rawvalue)) / 2;
- phton16(pd, sample);
- } else {
- goto copy_file_err;
- }
-
- nchars = save_file.write((const char *)pd, 2);
- if (nchars < 2) {
- goto copy_file_err;
- }
+ if (save_format == save_audio_au_) { /* au format */
+ if ((fwd_statinfo_.clock_rate != 8000) ||
+ ((rev_statinfo_.clock_rate != 0) && (rev_statinfo_.clock_rate != 8000))
+ ) {
+ QMessageBox::warning(this, tr("Warning"), tr("Can save audio with 8000 Hz clock rate only"));
+ } else {
+ if (! saveAudioAU(direction, &save_file, &stop_flag, sync)) {
+ goto copy_file_err;
}
}
- }
} else if (save_format == save_audio_raw_) { /* raw format */
- QFile *tempfile;
- int progress_pct;
-
- switch (direction) {
- /* Only forward direction */
- case dir_forward_: {
- progress_pct = fwd_tempfile_->pos() * 100 / fwd_tempfile_->size();
- tempfile = fwd_tempfile_;
- break;
- }
- /* only reversed direction */
- case dir_reverse_: {
- progress_pct = rev_tempfile_->pos() * 100 / rev_tempfile_->size();
- tempfile = rev_tempfile_;
- break;
- }
- default: {
+ if (! saveAudioRAW(direction, &save_file, &stop_flag)) {
goto copy_file_err;
}
- }
-
- int chunk_size = 65536;
- /* XXX how do you just copy the file? */
- while (chunk_size > 0) {
- if (stop_flag)
- break;
- QByteArray bytes = tempfile->read(chunk_size);
- ui->progressFrame->setValue(progress_pct);
-
- if (!save_file.write(bytes)) {
- goto copy_file_err;
- }
- chunk_size = bytes.length();
- }
}
copy_file_err:
diff --git a/ui/qt/rtp_analysis_dialog.h b/ui/qt/rtp_analysis_dialog.h
index b1dfddf097..c24de02d09 100644
--- a/ui/qt/rtp_analysis_dialog.h
+++ b/ui/qt/rtp_analysis_dialog.h
@@ -33,6 +33,7 @@
#include <QAbstractButton>
#include <QMenu>
+#include <QFile>
#include "wireshark_dialog.h"
@@ -73,9 +74,15 @@ private slots:
void on_rJitterCheckBox_toggled(bool checked);
void on_rDiffCheckBox_toggled(bool checked);
void on_rDeltaCheckBox_toggled(bool checked);
- void on_actionSaveAudio_triggered();
- void on_actionSaveForwardAudio_triggered();
- void on_actionSaveReverseAudio_triggered();
+ void on_actionSaveAudioUnsync_triggered();
+ void on_actionSaveForwardAudioUnsync_triggered();
+ void on_actionSaveReverseAudioUnsync_triggered();
+ void on_actionSaveAudioSyncStream_triggered();
+ void on_actionSaveForwardAudioSyncStream_triggered();
+ void on_actionSaveReverseAudioSyncStream_triggered();
+ void on_actionSaveAudioSyncFile_triggered();
+ void on_actionSaveForwardAudioSyncFile_triggered();
+ void on_actionSaveReverseAudioSyncFile_triggered();
void on_actionSaveCsv_triggered();
void on_actionSaveForwardCsv_triggered();
void on_actionSaveReverseCsv_triggered();
@@ -88,6 +95,7 @@ private slots:
private:
Ui::RtpAnalysisDialog *ui;
enum StreamDirection { dir_both_, dir_forward_, dir_reverse_ };
+ enum SyncType { sync_unsync_, sync_sync_stream_, sync_sync_file_ };
// XXX These are copied to and from rtp_stream_info_t structs. Should
// we just have a pair of those instead?
@@ -153,7 +161,13 @@ private:
void showPlayer();
- void saveAudio(StreamDirection direction);
+ size_t convert_payload_to_samples(unsigned int payload_type, QTemporaryFile *tempfile, gchar *pd_out, size_t expected_nchars);
+ gboolean saveAudioAUSilence(size_t total_len, QFile *save_file, gboolean *stop_flag);
+ gboolean saveAudioAUUnidir(tap_rtp_stat_t statinfo, QTemporaryFile *tempfile, QFile *save_file, size_t header_end, gboolean *stop_flag, gboolean interleave, size_t prefix_silence);
+ gboolean saveAudioAUBidir(tap_rtp_stat_t fwd_statinfo, tap_rtp_stat_t rev_statinfo, QTemporaryFile *fwd_tempfile, QTemporaryFile *rev_tempfile, QFile *save_file, size_t header_end, gboolean *stop_flag, size_t prefix_silence_fwd, size_t prefix_silence_rev);
+ gboolean saveAudioAU(StreamDirection direction, QFile *save_file, gboolean *stop_flag, RtpAnalysisDialog::SyncType sync);
+ gboolean saveAudioRAW(StreamDirection direction, QFile *save_file, gboolean *stop_flag);
+ void saveAudio(StreamDirection direction, RtpAnalysisDialog::SyncType sync);
void saveCsv(StreamDirection direction);
guint32 processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar* proto_field, bool *ok);
diff --git a/ui/qt/rtp_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui
index d68d0d2669..37066f22ac 100644
--- a/ui/qt/rtp_analysis_dialog.ui
+++ b/ui/qt/rtp_analysis_dialog.ui
@@ -328,28 +328,28 @@
</widget>
</item>
</layout>
- <action name="actionSaveAudio">
+ <action name="actionSaveAudioUnsync">
<property name="text">
- <string>Audio</string>
+ <string>Unsynchronized Forward and Reverse Audio</string>
</property>
<property name="toolTip">
- <string>Save the audio data for both channels.</string>
+ <string>Save the unsynchronized audio data for both channels.</string>
</property>
</action>
- <action name="actionSaveForwardAudio">
+ <action name="actionSaveForwardAudioUnsync">
<property name="text">
- <string>Forward Stream Audio</string>
+ <string>Unsynchronized Forward Stream Audio</string>
</property>
<property name="toolTip">
- <string>Save the forward stream audio data.</string>
+ <string>Save the unsynchronized forward stream audio data.</string>
</property>
</action>
- <action name="actionSaveReverseAudio">
+ <action name="actionSaveReverseAudioUnsync">
<property name="text">
- <string>Reverse Stream Audio</string>
+ <string>Unsynchronized Reverse Stream Audio</string>
</property>
<property name="toolTip">
- <string>Save the reverse stream audio data.</string>
+ <string>Save the unsynchronized reverse stream audio data.</string>
</property>
</action>
<action name="actionSaveCsv">
@@ -406,6 +406,54 @@
<string>N</string>
</property>
</action>
+ <action name="actionSaveAudioSyncStream">
+ <property name="text">
+ <string>Stream Synchronized Forward and Reverse Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the audio data for both channels synchronized to start of earlier stream.</string>
+ </property>
+ </action>
+ <action name="actionSaveForwardAudioSyncStream">
+ <property name="text">
+ <string>Stream Synchronized Forward Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the forward stream audio data synchronized to start of earlier stream.</string>
+ </property>
+ </action>
+ <action name="actionSaveReverseAudioSyncStream">
+ <property name="text">
+ <string>Stream Synchronized Reverse Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the reverse stream audio data synchronized to start of earlier stream.</string>
+ </property>
+ </action>
+ <action name="actionSaveAudioSyncFile">
+ <property name="text">
+ <string>File Synchronized Forward and Reverse Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the audio data for both channels synchronized to start of file.</string>
+ </property>
+ </action>
+ <action name="actionSaveForwardAudioSyncFile">
+ <property name="text">
+ <string>File Synchronized Forward Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the forward stream audio data synchronized to start of file.</string>
+ </property>
+ </action>
+ <action name="actionSaveReverseAudioSyncFile">
+ <property name="text">
+ <string>File Synchronized Reverse Stream Audio</string>
+ </property>
+ <property name="toolTip">
+ <string>Save the reverse stream audio data synchronized to start of file.</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/ui/tap-rtp-analysis.h b/ui/tap-rtp-analysis.h
index af41ff1291..2c782a36f0 100644
--- a/ui/tap-rtp-analysis.h
+++ b/ui/tap-rtp-analysis.h
@@ -107,8 +107,15 @@ typedef struct _tap_rtp_stat_t {
guint16 pt;
int reg_pt;
guint32 first_packet_num;
+ guint last_payload_len;
} tap_rtp_stat_t;
+typedef struct _tap_rtp_save_data_t {
+ guint32 timestamp;
+ unsigned int payload_type;
+ size_t payload_len;
+} tap_rtp_save_data_t;
+
#define PT_UNDEFINED -1
/* status flags for the flags parameter in tap_rtp_stat_t */
diff --git a/ui/tap-rtp-common.c b/ui/tap-rtp-common.c
index 75da3edf47..4f39c51891 100644
--- a/ui/tap-rtp-common.c
+++ b/ui/tap-rtp-common.c
@@ -39,6 +39,7 @@
#include <epan/addr_resolv.h>
#include <epan/proto_data.h>
#include <epan/dissectors/packet-rtp.h>
+#include <wsutil/pint.h>
#include "rtp_stream.h"
#include "tap-rtp-common.h"
@@ -591,12 +592,7 @@ rtp_packet_analyse(tap_rtp_stat_t *statinfo,
/* Handle wraparound ? */
arrivaltime = current_time - statinfo->start_time;
- if (statinfo->first_timestamp > rtpinfo->info_timestamp){
- /* Handle wraparound */
- nominaltime = (double)(rtpinfo->info_timestamp + 0xffffffff - statinfo->first_timestamp + 1);
- }else{
- nominaltime = (double)(rtpinfo->info_timestamp - statinfo->first_timestamp);
- }
+ nominaltime = (double)(guint32_wraparound_diff(rtpinfo->info_timestamp, statinfo->first_timestamp));
/* Can only analyze defined sampling rates */
if (clock_rate != 0) {
@@ -669,7 +665,7 @@ rtp_packet_analyse(tap_rtp_stat_t *statinfo,
/* Is it a packet with the mark bit set? */
if (rtpinfo->info_marker_set) {
- statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
+ statinfo->delta_timestamp = guint32_wraparound_diff(rtpinfo->info_timestamp, statinfo->timestamp);
if (rtpinfo->info_timestamp > statinfo->timestamp){
statinfo->flags |= STAT_FLAG_MARKER;
}
@@ -714,6 +710,7 @@ rtp_packet_analyse(tap_rtp_stat_t *statinfo,
statinfo->timestamp = rtpinfo->info_timestamp;
statinfo->stop_seq_nr = rtpinfo->info_seq_num;
statinfo->total_nr++;
+ statinfo->last_payload_len = rtpinfo->info_payload_len - rtpinfo->info_padding_count;
return;
}