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