1 /* rtp_player_dialog.h
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #ifndef RTP_PLAYER_DIALOG_H
11 #define RTP_PLAYER_DIALOG_H
12 
13 #include "config.h"
14 
15 #include <glib.h>
16 #include <mutex>
17 
18 #include "ui/rtp_stream.h"
19 
20 #include "wireshark_dialog.h"
21 #include "rtp_audio_stream.h"
22 
23 #include <QWidget>
24 #include <QMap>
25 #include <QMultiHash>
26 #include <QTreeWidgetItem>
27 #include <QMetaType>
28 #include <ui/qt/widgets/qcustomplot.h>
29 #include <QAudioDeviceInfo>
30 
31 namespace Ui {
32 class RtpPlayerDialog;
33 }
34 
35 class QCPItemStraightLine;
36 class QDialogButtonBox;
37 class QMenu;
38 class RtpAudioStream;
39 class QCPAxisTicker;
40 class QCPAxisTickerDateTime;
41 
42 typedef enum {
43     save_audio_none,
44     save_audio_au,
45     save_audio_wav
46 } save_audio_t;
47 
48 typedef enum {
49     save_payload_none,
50     save_payload_data
51 } save_payload_t;
52 
53 typedef enum {
54     save_mode_from_cursor,
55     save_mode_sync_stream,
56     save_mode_sync_file
57 } save_mode_t;
58 
59 // Singleton by https://refactoring.guru/design-patterns/singleton/cpp/example#example-1
60 class RtpPlayerDialog : public WiresharkDialog
61 {
62     Q_OBJECT
63 #ifdef QT_MULTIMEDIA_LIB
64     Q_PROPERTY(QString currentOutputDeviceName READ currentOutputDeviceName)
65 #endif
66 
67 public:
68     /**
69      * Returns singleton
70      */
71     static RtpPlayerDialog *openRtpPlayerDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list, bool capture_running);
72 
73     /**
74      * Should not be clonnable and assignable
75      */
76     RtpPlayerDialog(RtpPlayerDialog &other) = delete;
77     void operator=(const RtpPlayerDialog &) = delete;
78 
79     /**
80      * @brief Common routine to add a "Play call" button to a QDialogButtonBox.
81      * @param button_box Caller's QDialogButtonBox.
82      * @return The new "Play call" button.
83      */
84     static QToolButton *addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog);
85 
86 #ifdef QT_MULTIMEDIA_LIB
87     void accept();
88     void reject();
89 
90     void setMarkers();
91 
92     /** Replace/Add/Remove an RTP streams to play.
93      * Requires array of rtpstream_info_t.
94      * Each item must have filled items: src_addr, src_port, dest_addr,
95      *  dest_port, ssrc, packet_count, setup_frame_number, and start_rel_time.
96      *
97      * @param stream_ids struct with rtpstream info
98      */
99     void replaceRtpStreams(QVector<rtpstream_id_t *> stream_ids);
100     void addRtpStreams(QVector<rtpstream_id_t *> stream_ids);
101     void removeRtpStreams(QVector<rtpstream_id_t *> stream_ids);
102 
103 signals:
104     // Tells the packet list to redraw. An alternative might be to add a
105     // cf_packet_marked callback to file.[ch] but that's synchronous and
106     // might incur too much overhead.
107     void packetsMarked();
108     void updateFilter(QString filter, bool force = false);
109     void goToPacket(int packet_num);
110     void rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *> stream_infos);
111     void rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *> stream_infos);
112     void rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *> stream_infos);
113 
114 public slots:
115     void rtpAnalysisReplace();
116     void rtpAnalysisAdd();
117     void rtpAnalysisRemove();
118 
119 protected:
120     explicit RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_running);
121     ~RtpPlayerDialog();
122 
123     virtual void showEvent(QShowEvent *);
124     void contextMenuEvent(QContextMenuEvent *event);
125     bool eventFilter(QObject *obj, QEvent *event);
126 
127 private slots:
128     /** Retap the capture file, reading RTP packets that match the
129      * streams added using ::addRtpStream.
130      */
131     void retapPackets();
132     void captureEvent(CaptureEvent e);
133     /** Clear, decode, and redraw each stream.
134      */
135     void rescanPackets(bool rescale_axes = false);
136     void createPlot(bool rescale_axes = false);
137     void updateWidgets();
138     void itemEntered(QTreeWidgetItem *item, int column);
139     void mouseMovePlot(QMouseEvent *event);
140     void graphClicked(QMouseEvent *event);
141     void graphDoubleClicked(QMouseEvent *event);
142     void plotClicked(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event);
143     void updateHintLabel();
144     void resetXAxis();
145     void updateGraphs();
146     void playFinished(RtpAudioStream *stream, QAudio::Error error);
147 
148     void setPlayPosition(double secs);
149     void setPlaybackError(const QString playback_error);
150     void changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting new_audio_routing);
151     void changeAudioRouting(AudioRouting new_audio_routing);
152     void invertAudioMutingOnItem(QTreeWidgetItem *ti);
153     void on_playButton_clicked();
154     void on_pauseButton_clicked();
155     void on_stopButton_clicked();
156     void on_actionReset_triggered();
157     void on_actionZoomIn_triggered();
158     void on_actionZoomOut_triggered();
159     void on_actionMoveLeft10_triggered();
160     void on_actionMoveRight10_triggered();
161     void on_actionMoveLeft1_triggered();
162     void on_actionMoveRight1_triggered();
163     void on_actionGoToPacket_triggered();
164     void on_actionGoToSetupPacketPlot_triggered();
165     void on_actionGoToSetupPacketTree_triggered();
166     void on_actionRemoveStream_triggered();
167     void on_actionAudioRoutingP_triggered();
168     void on_actionAudioRoutingL_triggered();
169     void on_actionAudioRoutingLR_triggered();
170     void on_actionAudioRoutingR_triggered();
171     void on_actionAudioRoutingMute_triggered();
172     void on_actionAudioRoutingUnmute_triggered();
173     void on_actionAudioRoutingMuteInvert_triggered();
174     void on_streamTreeWidget_itemSelectionChanged();
175     void on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, const int column);
176     void on_outputDeviceComboBox_currentIndexChanged(const QString &);
177     void on_outputAudioRate_currentIndexChanged(const QString &);
178     void on_jitterSpinBox_valueChanged(double);
179     void on_timingComboBox_currentIndexChanged(int);
180     void on_todCheckBox_toggled(bool checked);
181     void on_buttonBox_helpRequested();
182     void on_actionSelectAll_triggered();
183     void on_actionSelectInvert_triggered();
184     void on_actionSelectNone_triggered();
185     void outputNotify();
186     void on_actionPlay_triggered();
187     void on_actionStop_triggered();
188     void on_actionSaveAudioFromCursor_triggered();
189     void on_actionSaveAudioSyncStream_triggered();
190     void on_actionSaveAudioSyncFile_triggered();
191     void on_actionSavePayload_triggered();
192     void on_actionSelectInaudible_triggered();
193     void on_actionDeselectInaudible_triggered();
194     void on_actionPrepareFilter_triggered();
195     void on_actionReadCapture_triggered();
196 
197 private:
198     static RtpPlayerDialog *pinstance_;
199     static std::mutex mutex_;
200 
201     Ui::RtpPlayerDialog *ui;
202     QMenu *graph_ctx_menu_;
203     QMenu *list_ctx_menu_;
204     double first_stream_rel_start_time_;  // Relative start time of first stream
205     double first_stream_abs_start_time_;  // Absolute start time of first stream
206     double first_stream_rel_stop_time_;  // Relative end time of first stream (ued for streams_length_ calculation
207     double streams_length_;  // Difference between start of first stream and end of last stream
208     double start_marker_time_;    // Always relative time to start of the capture
209     double start_marker_time_play_;    // Copy when play started
210     QCPItemStraightLine *cur_play_pos_;
211     QCPItemStraightLine *start_marker_pos_;
212     QString playback_error_;
213     QSharedPointer<QCPAxisTicker> number_ticker_;
214     QSharedPointer<QCPAxisTickerDateTime> datetime_ticker_;
215     bool stereo_available_;
216     QList<RtpAudioStream *> playing_streams_;
217     QAudioOutput *marker_stream_;
218     quint32 marker_stream_requested_out_rate_;
219     QTreeWidgetItem *last_ti_;
220     bool listener_removed_;
221     QPushButton *read_btn_;
222     QToolButton *inaudible_btn_;
223     QToolButton *analyze_btn_;
224     QPushButton *prepare_btn_;
225     QPushButton *export_btn_;
226     QMultiHash<guint, RtpAudioStream *> stream_hash_;
227     bool block_redraw_;
228     int lock_ui_;
229     bool read_capture_enabled_;
230     double silence_skipped_time_;
231 
232 //    const QString streamKey(const rtpstream_info_t *rtpstream);
233 //    const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo);
234 
235     // Tap callbacks
236 //    static void tapReset(void *tapinfo_ptr);
237     static tap_packet_status tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr);
238     static void tapDraw(void *tapinfo_ptr);
239 
240     void addPacket(packet_info *pinfo, const struct _rtp_info *rtpinfo);
241     void zoomXAxis(bool in);
242     void panXAxis(int x_pixels);
243     const QString getFormatedTime(double f_time);
244     const QString getFormatedHoveredTime();
245     int getHoveredPacket();
246     QString currentOutputDeviceName();
247     double getStartPlayMarker();
248     void drawStartPlayMarker();
249     void setStartPlayMarker(double new_time);
250     void updateStartStopTime(rtpstream_info_t *rtpstream, bool is_first);
251     void formatAudioRouting(QTreeWidgetItem *ti, AudioRouting audio_routing);
252     bool isStereoAvailable();
253     QAudioOutput *getSilenceAudioOutput();
254     QAudioDeviceInfo getCurrentDeviceInfo();
255     QTreeWidgetItem *findItemByCoords(QPoint point);
256     QTreeWidgetItem *findItem(QCPAbstractPlottable *plottable);
257     void handleItemHighlight(QTreeWidgetItem *ti, bool scroll);
258     void highlightItem(QTreeWidgetItem *ti, bool highlight);
259     void invertSelection();
260     void handleGoToSetupPacket(QTreeWidgetItem *ti);
261     void addSingleRtpStream(rtpstream_id_t *id);
262     void removeRow(QTreeWidgetItem *ti);
263     void fillAudioRateMenu();
264     void cleanupMarkerStream();
265 
266     qint64 saveAudioHeaderAU(QFile *save_file, int channels, unsigned audio_rate);
267     qint64 saveAudioHeaderWAV(QFile *save_file, int channels, unsigned audio_rate, qint64 samples);
268     bool writeAudioSilenceSamples(QFile *out_file, qint64 samples, int stream_count);
269     bool writeAudioStreamsSamples(QFile *out_file, QVector<RtpAudioStream *> streams, bool swap_bytes);
270     save_audio_t selectFileAudioFormatAndName(QString *file_path);
271     save_payload_t selectFilePayloadFormatAndName(QString *file_path);
272     QVector<RtpAudioStream *>getSelectedAudibleNonmutedAudioStreams();
273     void saveAudio(save_mode_t save_mode);
274     void savePayload();
275     void lockUI();
276     void unlockUI();
277     void selectInaudible(bool select);
278     QVector<rtpstream_id_t *>getSelectedRtpStreamIDs();
279     void fillTappedColumns();
280 
281 #else // QT_MULTIMEDIA_LIB
282 private:
283     Ui::RtpPlayerDialog *ui;
284 #endif // QT_MULTIMEDIA_LIB
285 };
286 
287 #endif // RTP_PLAYER_DIALOG_H
288