1 /* rtp_audio_file.cpp
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 /*
11  * RTP samples are stored in "sparse" file. File knows where are silence gaps
12  * and they are handled special way (not stored).
13  *
14  * File uses Frame as piece of information. One Frame match audio of one
15  * decoded packet or audio silence in between them. Frame holds information
16  * about frame type (audio/silence), its length and realtime position and
17  * sample possition (where decoded audio is really stored, with gaps omitted).
18  *
19  * There are three stages of the object use
20  * - writing data by frames during decoding of the stream
21  * - reading data by frames during creating the visual waveform
22  * - reading data by bytes/samples during audio play or audio save
23  *
24  * There is no stage indication in the object, but there are different calls
25  * used by the code. For last stage the object looks like QIODevice therefore
26  * any read of it looks like reading of sequence of bytes.
27  *
28  * If audio starts later than start of the file, first Frame contains silence
29  * record. It is leaved out at some cases.
30  */
31 
32 #include "rtp_audio_file.h"
33 #include <ws_attributes.h>
34 
RtpAudioFile(bool use_disk_for_temp,bool use_disk_for_frames)35 RtpAudioFile::RtpAudioFile(bool use_disk_for_temp, bool use_disk_for_frames):
36       real_pos_(0)
37     , real_size_(0)
38     , sample_pos_(0)
39     , sample_size_(0)
40 {
41     QString tempname;
42 
43     // ReadOnly because we write different way
44     QIODevice::open(QIODevice::ReadOnly);
45 
46     tempname = "memory";
47     if (use_disk_for_temp) {
48         tempname = QString("%1/wireshark_rtp_stream").arg(QDir::tempPath());
49         sample_file_ = new QTemporaryFile(tempname, this);
50     } else {
51         sample_file_ = new QBuffer(this);
52     }
53     if (!sample_file_->open(QIODevice::ReadWrite)) {
54         // We are out of file resources
55         delete sample_file_;
56         qWarning() << "Can't create temp file in " << tempname;
57         throw -1;
58     }
59 
60     tempname = "memory";
61     if (use_disk_for_frames) {
62         tempname = QString("%1/wireshark_rtp_frames").arg(QDir::tempPath());
63         sample_file_frame_ = new QTemporaryFile(tempname, this);
64     } else {
65         sample_file_frame_ = new QBuffer(this);
66     }
67     if (!sample_file_frame_->open(QIODevice::ReadWrite)) {
68         // We are out of file resources
69         delete sample_file_;
70         delete sample_file_frame_;
71         qWarning() << "Can't create frame file in " << tempname;
72         throw -1;
73     }
74 }
75 
~RtpAudioFile()76 RtpAudioFile::~RtpAudioFile()
77 {
78     if (sample_file_) delete sample_file_;
79     if (sample_file_frame_) delete sample_file_frame_;
80 }
81 
82 /*
83  * Functions for writing Frames
84  */
setFrameWriteStage()85 void RtpAudioFile::setFrameWriteStage()
86 {
87     sample_file_->seek(0);
88     sample_file_frame_->seek(0);
89     real_pos_ = 0;
90     real_size_ = 0;
91     sample_pos_ = 0;
92     sample_size_ = 0;
93 }
94 
frameUpdateRealCounters(qint64 written_bytes)95 void RtpAudioFile::frameUpdateRealCounters(qint64 written_bytes)
96 {
97     if (real_pos_ < real_size_) {
98         // We are writing before end, calculate if we are over real_size_
99         qint64 diff = real_pos_ + written_bytes - real_size_;
100 
101         if (diff > 0) {
102             // Update size
103             real_size_ += diff;
104         }
105     } else {
106         real_size_ += written_bytes;
107     }
108     real_pos_ += written_bytes;
109 }
110 
frameUpdateSampleCounters(qint64 written_bytes)111 void RtpAudioFile::frameUpdateSampleCounters(qint64 written_bytes)
112 {
113     if (sample_pos_ < sample_size_) {
114         // We are writing before end, calculate if we are over sample_size_
115         qint64 diff = sample_pos_ + written_bytes - sample_size_;
116 
117         if (diff > 0) {
118             // Update size
119             sample_size_ += diff;
120         }
121     } else {
122         sample_size_ += written_bytes;
123     }
124     sample_pos_ += written_bytes;
125 }
126 
frameWriteFrame(guint32 frame_num,qint64 real_pos,qint64 sample_pos,qint64 len,rtp_frame_type type)127 qint64 RtpAudioFile::frameWriteFrame(guint32 frame_num, qint64 real_pos, qint64 sample_pos, qint64 len, rtp_frame_type type)
128 {
129     rtp_frame_info frame_info;
130 
131     frame_info.real_pos = real_pos;
132     frame_info.sample_pos = sample_pos;
133     frame_info.len = len;
134     frame_info.frame_num = frame_num;
135     frame_info.type = type;
136 
137     return sample_file_frame_->write((char *)&frame_info, sizeof(frame_info));
138 }
139 
frameWriteSilence(guint32 frame_num,qint64 samples)140 void RtpAudioFile::frameWriteSilence(guint32 frame_num, qint64 samples)
141 {
142     if (samples < 1) return;
143 
144     qint64 silence_bytes = samples * SAMPLE_BYTES;
145 
146     frameWriteFrame(frame_num, real_pos_, sample_pos_, silence_bytes, RTP_FRAME_SILENCE);
147     frameUpdateRealCounters(silence_bytes);
148 }
149 
frameWriteSamples(guint32 frame_num,const char * data,qint64 max_size)150 qint64 RtpAudioFile::frameWriteSamples(guint32 frame_num, const char *data, qint64 max_size)
151 {
152     gint64 written;
153 
154     written = sample_file_->write(data, max_size);
155 
156     if (written != -1) {
157         frameWriteFrame(frame_num, real_pos_, sample_pos_, written, RTP_FRAME_AUDIO);
158         frameUpdateRealCounters(written);
159         frameUpdateSampleCounters(written);
160     }
161 
162     return written;
163 }
164 
165 /*
166  * Functions for reading Frames
167  */
168 
setFrameReadStage(qint64 prepend_samples)169 void RtpAudioFile::setFrameReadStage(qint64 prepend_samples)
170 {
171     sample_file_frame_->seek(0);
172     if (prepend_samples > 0) {
173         // Skip first frame which contains openning silence
174         sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_));
175     }
176 }
177 
readFrameSamples(gint32 * read_buff_bytes,SAMPLE ** read_buff,spx_uint32_t * read_len,guint32 * frame_num,rtp_frame_type * type)178 bool RtpAudioFile::readFrameSamples(gint32 *read_buff_bytes, SAMPLE **read_buff, spx_uint32_t *read_len, guint32 *frame_num, rtp_frame_type *type)
179 {
180     rtp_frame_info frame_info;
181     guint64 read_bytes = 0;
182 
183     if (!sample_file_frame_->read((char *)&frame_info, sizeof(frame_info))) {
184         // Can't read frame, some error occurred
185         return false;
186     }
187 
188     *frame_num = frame_info.frame_num;
189     *type = frame_info.type;
190 
191     if (frame_info.type == RTP_FRAME_AUDIO) {
192         // Resize buffer when needed
193         if (frame_info.len > *read_buff_bytes) {
194             while ((frame_info.len > *read_buff_bytes)) {
195                 *read_buff_bytes *= 2;
196             }
197             *read_buff = (SAMPLE *) g_realloc(*read_buff, *read_buff_bytes);
198         }
199 
200         sample_file_->seek(frame_info.sample_pos);
201         read_bytes = sample_file_->read((char *)*read_buff, frame_info.len);
202     } else {
203         // For silence we do nothing
204         read_bytes = frame_info.len;
205     }
206 
207     *read_len = (spx_uint32_t)(read_bytes / SAMPLE_BYTES);
208 
209     return true;
210 }
211 
212 /*
213  * Functions for reading data during play
214  */
setDataReadStage()215 void RtpAudioFile::setDataReadStage()
216 {
217     sample_file_frame_->seek(0);
218     sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_));
219     real_pos_ = cur_frame_.real_pos;
220 }
221 
open(QIODevice::OpenMode mode)222 bool RtpAudioFile::open(QIODevice::OpenMode mode)
223 {
224     if (mode == QIODevice::ReadOnly) {
225        return true;
226     }
227 
228     return false;
229 }
230 
size() const231 qint64 RtpAudioFile::size() const
232 {
233     return real_size_;
234 }
235 
pos() const236 qint64 RtpAudioFile::pos() const
237 {
238     return real_pos_;
239 }
240 
241 /*
242  * Seek starts from beginning of Frames and search one where offset belongs
243  * to. It looks inefficient, but seek is used usually just to jump to 0 or
244  * to skip first Frame where silence is stored.
245  */
seek(qint64 off)246 bool RtpAudioFile::seek(qint64 off)
247 {
248     if (real_size_ <= off) {
249         // Can't seek above end of file
250         return false;
251     }
252 
253     // Search for correct offset from first frame
254     sample_file_frame_->seek(0);
255     while (1) {
256         // Read frame
257         if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) {
258             // Can't read frame, some error occurred
259             return false;
260         }
261 
262         if ((cur_frame_.real_pos + cur_frame_.len) > off) {
263             // We found correct frame
264             // Calculate offset in frame
265             qint64 diff = off - cur_frame_.real_pos;
266             qint64 new_real_pos = cur_frame_.real_pos + diff;
267             qint64 new_sample_pos = cur_frame_.sample_pos + diff;
268 
269             if (cur_frame_.type == RTP_FRAME_AUDIO) {
270                 // For audio frame we should to seek to correct place
271                 if (!sample_file_->seek(new_sample_pos)) {
272                     return false;
273                 }
274                 // Real seek was successful
275                 real_pos_ = new_real_pos;
276                 return true;
277             } else {
278                 // For silence frame we blindly confirm it
279                 real_pos_ = new_real_pos;
280                 return true;
281             }
282         }
283     }
284     return false;
285 }
286 
sampleFileSize()287 qint64 RtpAudioFile::sampleFileSize()
288 {
289     return real_size_;
290 }
291 
seekSample(qint64 samples)292 void RtpAudioFile::seekSample(qint64 samples)
293 {
294     seek(sizeof(SAMPLE) * samples);
295 }
296 
readFrameData(char * data,qint64 want_read)297 qint64 RtpAudioFile::readFrameData(char *data , qint64 want_read)
298 {
299     // Calculate remaining data in frame
300     qint64 remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos);
301     qint64 was_read;
302 
303     if (remaining < want_read) {
304         // Incorrect call, can't read more than is stored in frame
305         return -1;
306     }
307 
308     if (cur_frame_.type == RTP_FRAME_AUDIO) {
309         was_read = sample_file_->read(data, want_read);
310         real_pos_ += was_read;
311     } else {
312         memset(data, 0, want_read);
313         real_pos_ += want_read;
314         was_read = want_read;
315     }
316 
317     return was_read;
318 }
319 
readSample(SAMPLE * sample)320 qint64 RtpAudioFile::readSample(SAMPLE *sample)
321 {
322     return read((char *)sample, sizeof(SAMPLE));
323 }
324 
getTotalSamples()325 qint64 RtpAudioFile::getTotalSamples()
326 {
327     return (real_size_/(qint64)sizeof(SAMPLE));
328 }
329 
getEndOfSilenceSample()330 qint64 RtpAudioFile::getEndOfSilenceSample()
331 {
332     if (cur_frame_.type == RTP_FRAME_SILENCE) {
333         return (cur_frame_.real_pos + cur_frame_.len) / (qint64)sizeof(SAMPLE);
334     } else {
335         return -1;
336     }
337 }
338 
readData(char * data,qint64 maxSize)339 qint64 RtpAudioFile::readData(char *data, qint64 maxSize)
340 {
341     qint64 to_read = maxSize;
342     qint64 can_read;
343     qint64 was_read = 0;
344     qint64 remaining;
345 
346     while (1) {
347         // Calculate remaining data in frame
348         remaining = cur_frame_.len - (real_pos_ - cur_frame_.real_pos);
349         if (remaining > to_read) {
350             // Even we want to read more, we can read just till end of frame
351             can_read = to_read;
352         } else {
353             can_read = remaining;
354         }
355         if (can_read==readFrameData(data, can_read)) {
356             to_read -= can_read;
357             data += can_read;
358             was_read += can_read;
359             if (real_pos_ >= cur_frame_.real_pos + cur_frame_.len) {
360                 // We exhausted the frame, read next one
361                 if (!sample_file_frame_->read((char *)&cur_frame_, sizeof(cur_frame_))) {
362                     // We are at the end of the file
363                     return was_read;
364                 }
365                 if ((cur_frame_.type == RTP_FRAME_AUDIO) && (!sample_file_->seek(cur_frame_.sample_pos))) {
366                     // We tried to seek to correct place, but it failed
367                     return -1;
368                 }
369             }
370             if (to_read == 0) {
371                 return was_read;
372             }
373         } else {
374             return -1;
375         }
376     }
377 }
378 
writeData(const char * data _U_,qint64 maxSize _U_)379 qint64 RtpAudioFile::writeData(const char *data _U_, qint64 maxSize _U_)
380 {
381     // Writing is not supported
382     return -1;
383 }
384 
385