diff options
author | Jiri Novak <j.novak@netsystem.cz> | 2016-12-13 13:28:30 +0100 |
---|---|---|
committer | Anders Broman <a.broman58@gmail.com> | 2016-12-15 05:16:29 +0000 |
commit | 9090afbfe9a4f9a422409c3f9d410bc3c14fc741 (patch) | |
tree | ba111a5eee19019502dc31a2095aaf52098d23db /ui | |
parent | 1afbab69956c32b341001dd20ff667036fe1f8f5 (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.cpp | 711 | ||||
-rw-r--r-- | ui/qt/rtp_analysis_dialog.h | 22 | ||||
-rw-r--r-- | ui/qt/rtp_analysis_dialog.ui | 66 | ||||
-rw-r--r-- | ui/tap-rtp-analysis.h | 7 | ||||
-rw-r--r-- | ui/tap-rtp-common.c | 11 |
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; } |