4 * Jester - a tester of the Opal jitter buffer
6 * Copyright (c) 2006 Derek J Smithies
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
18 * The Original Code is Jester
20 * The Initial Developer of the Original Code is Derek J Smithies
22 * Contributor(s): ______________________________________.
25 * Revision 1.14 2007/02/24 09:29:08 dereksmithies
26 * Add ability to turn off the first packet in an audio burst. Test the jitter buffer
27 * can cope with missing the first packet.
29 * Revision 1.13 2007/01/14 22:18:35 dereksmithies
30 * MOdify the period when doing silence detect, to be 69sec off, 6 sec on.
32 * Revision 1.12 2007/01/14 20:52:32 dereksmithies
33 * Report available audio devices if it fails to open the specified device.
35 * Revision 1.11 2007/01/13 00:05:40 rjongbloed
36 * Fixed compilation on DevStudio 2003
38 * Revision 1.10 2007/01/12 10:00:57 dereksmithies
39 * bring it up to date so it compiles.
41 * Revision 1.9 2007/01/11 09:20:41 dereksmithies
42 * Use the new OpalJitterBufer class, allowing easy access to the jitter buffer's internal
43 * variables. Play output audio to the specified sound device.
45 * Revision 1.8 2006/12/08 09:00:20 dereksmithies
46 * Add mutex protection of a pointer.
47 * Change default to send packets at a non uniform rate, so they go at 0 20 60 80 120 140 180 etc.
48 * Add ability to suppress the sending of packets, so we simulate silence suppression.
50 * Revision 1.7 2006/12/02 07:31:00 dereksmithies
51 * Add more options - duration of each packet.
53 * Revision 1.6 2006/12/02 04:16:13 dereksmithies
54 * Get it to terminate correctly.
55 * Report on when each frame is commited, and when each frame is received.
57 * Revision 1.5 2006/11/23 07:55:15 rjongbloed
58 * Fixed sample app build due to RTP session class API breakage.
60 * Revision 1.4 2006/10/02 13:30:51 rjongbloed
63 * Revision 1.3 2006/08/25 06:04:44 dereksmithies
64 * Add to the docs on the functions. Add a new thread to generate the frames,
65 * which helps make the operation of the jitterbuffer clearer.
67 * Revision 1.2 2006/07/29 13:42:20 rjongbloed
68 * Fixed compiler warning
70 * Revision 1.1 2006/06/19 09:32:09 dereksmithies
71 * Initial cut of a program to test the jitter buffer in OPAL.
77 #pragma implementation "main.h"
83 #include <opal/buildopts.h>
87 #include "../../version.h"
93 PCREATE_PROCESS(JesterProcess
);
97 ///////////////////////////////////////////////////////////////
98 JesterJitterBuffer::JesterJitterBuffer():
104 JesterJitterBuffer::~JesterJitterBuffer()
109 void JesterJitterBuffer::Close(BOOL
/*reading */ )
114 /////////////////////////////////////////////////////////////////////////////
116 JesterProcess::JesterProcess()
117 : PProcess("Derek Smithies Code Factory", "Jester",
118 1, 1, ReleaseCode
, 0)
123 void JesterProcess::Main()
125 // Get and parse all of the command line arguments.
126 PArgList
& args
= GetArguments();
142 if (args
.HasOption('h') ) {
143 cout
<< "Usage : " << GetName() << " [options] \n"
146 " -a --audiodevice : audio device to play the output on\n"
147 " -i --iterations # : number of packets to ask for (default is 80)\n"
148 " -s --silence : simulate silence suppression. - so audio is sent in bursts.\n"
149 " -j --jitter [min-]max : size of the jitter buffer in ms (100-1000) \n"
150 " -m --marker : turn some of the marker bits off, that indicate speech bursts\n"
152 " -t --trace : Enable trace, use multiple times for more detail.\n"
153 " -o --output : File for trace output, default is stderr.\n"
156 " -h --help : This help message.\n"
157 " -v --version : report version and program info.\n"
158 " -w --wavfile : audio file from which the source data is read from \n"
164 if (args
.HasOption('v')) {
165 cout
<< GetName() << endl
166 << " Version " << GetVersion(TRUE
) << endl
167 << " by " << GetManufacturer() << endl
168 << " on " << GetOSClass() << ' ' << GetOSName() << endl
169 << " (" << GetOSVersion() << '-' << GetOSHardware() << ")\n\n";
174 PTrace::Initialise(args
.GetOptionCount('t'),
175 args
.HasOption('o') ? (const char *)args
.GetOptionString('o') : NULL
,
176 PTrace::Timestamp
|PTrace::Thread
|PTrace::FileAndLine
);
179 if (args
.HasOption('a'))
180 audioDevice
= args
.GetOptionString('a');
182 audioDevice
= PSoundChannel::GetDefaultDevice(PSoundChannel::Player
);
185 maxJitterSize
= 1000;
187 if (args
.HasOption('j')) {
188 unsigned minJitterNew
;
189 unsigned maxJitterNew
;
190 PStringArray delays
= args
.GetOptionString('j').Tokenise(",-");
192 if (delays
.GetSize() > 1) {
193 minJitterNew
= delays
[0].AsUnsigned();
194 maxJitterNew
= delays
[1].AsUnsigned();
196 maxJitterNew
= delays
[0].AsUnsigned();
197 minJitterNew
= maxJitterNew
;
200 if (minJitterNew
>= 20 && minJitterNew
<= maxJitterNew
&& maxJitterNew
<= 1000) {
201 minJitterSize
= minJitterNew
;
202 maxJitterSize
= maxJitterNew
;
204 cout
<< "Jitter should be between 20 milliseconds and 1 seconds, is "
205 << 20 << '-' << 1000 << endl
;
208 cerr
<< "Set jitter buffer size to " << minJitterSize
<< ".." << maxJitterSize
<< " ms" << endl
;
210 silenceSuppression
= args
.HasOption('s');
212 markerSuppression
= args
.HasOption('m');
214 if (args
.HasOption('w'))
215 wavFile
= args
.GetOptionString('w');
217 wavFile
= "../../../contrib/openam/sample_message.wav";
222 if (!PFile::Exists(wavFile
)) {
223 cerr
<< "the audio file " << wavFile
<< " does not exist." << endl
;
224 cerr
<< "Terminating now" << endl
;
228 if (!player
.Open(audioDevice
, PSoundChannel::Player
, 1, 8000, 16)) {
229 cerr
<< "Failed to open the sound device " << audioDevice
230 << " to write the jittered audio to" << endl
;
232 << "available devices are " << endl
;
233 PStringList namesPlay
= PSoundChannel::GetDeviceNames(PSoundChannel::Player
);
234 for (PINDEX i
= 0; i
< namesPlay
.GetSize(); i
++)
235 cerr
<< i
<< " " << namesPlay
[i
] << endl
;
238 cerr
<< "Terminating now" << endl
;
242 jitterBuffer
.SetDelay(8 * minJitterSize
, 8 * maxJitterSize
);
243 jitterBuffer
.Resume();
246 generateTimestamp
= 0;
247 consumeTimestamp
= 0;
249 PThread
* writer
= PThread::Create(PCREATE_NOTIFIER(GenerateUdpPackets
), 0,
250 PThread::NoAutoDeleteThread
,
252 PThread::NormalPriority
,
257 PThread
* reader
= PThread::Create(PCREATE_NOTIFIER(ConsumeUdpPackets
), 0,
258 PThread::NoAutoDeleteThread
,
259 PThread::NormalPriority
,
265 writer
->WaitForTermination();
267 reader
->WaitForTermination();
274 void JesterProcess::GenerateUdpPackets(PThread
&, INT
)
276 PAdaptiveDelay delay
;
277 BOOL lastFrameWasSilence
= TRUE
;
278 PWAVFile
soundFile(wavFile
);
280 PINDEX talkSequenceCounter
= 0;
283 generateTimestamp
= (bytesPerBlock
* 2) + ((generateIndex
* bytesPerBlock
) >> 1);
284 //Silence period, 10 seconds cycle, with 3 second on time.
285 if (silenceSuppression
&& ((generateIndex
% 1000) > 200)) {
286 PTRACE(3, "Don't send this frame - silence period");
287 if (lastFrameWasSilence
== FALSE
) {
288 PTRACE(3, "Stop Audio here");
289 cout
<< "Stop audio at " << PTime() << endl
;
290 talkSequenceCounter
++;
292 lastFrameWasSilence
= TRUE
;
294 RTP_DataFrame
*frame
= new RTP_DataFrame
;
295 if (lastFrameWasSilence
) {
296 PTRACE(3, "StartAudio here");
297 cout
<< "Start Audio at " << PTime() << endl
;
299 frame
->SetMarker(lastFrameWasSilence
);
300 lastFrameWasSilence
= FALSE
;
301 frame
->SetPayloadType(RTP_DataFrame::L16_Mono
);
302 frame
->SetSyncSource(0x12345678);
303 frame
->SetSequenceNumber((WORD
)(generateIndex
+ 100));
304 frame
->SetPayloadSize(bytesPerBlock
);
306 frame
->SetTimestamp(generateTimestamp
);
308 PTRACE(3, "GenerateUdpPacket iteration " << generateIndex
309 << " with time of " << frame
->GetTimestamp() << " rtp time units");
310 memset(frame
->GetPayloadPtr(), 0, frame
->GetPayloadSize());
311 if (!soundFile
.Read(frame
->GetPayloadPtr(), frame
->GetPayloadSize())) {
314 PTRACE(3, "Reopen the sound file, as have reached the end of it");
316 // cerr << " " << silenceSuppression << " " << markerSuppression << " " << frame->GetMarker() << " " << (talkSequenceCounter & 1) << endl;
317 if (silenceSuppression
&& markerSuppression
&& frame
->GetMarker() && (talkSequenceCounter
& 1))
318 cerr
<< "Suppress speech frame" << endl
;
320 jitterBuffer
.NewFrameFromNetwork(frame
);
325 switch (generateIndex
% 2)
329 case 1: PThread::Sleep(30);
335 PTRACE(3, "End of generate udp packets ");
339 void JesterProcess::ConsumeUdpPackets(PThread
&, INT
)
341 RTP_DataFrame readFrame
;
342 PBYTEArray
silence(bytesPerBlock
);
343 consumeTimestamp
= 0;
348 BOOL success
= jitterBuffer
.ReadData(consumeTimestamp
, readFrame
);
350 if (success
&& (readFrame
.GetPayloadSize() > 0)) {
351 consumeTimestamp
= readFrame
.GetTimestamp();
352 PTRACE(3, "Write audio to sound device, " << readFrame
.GetPayloadSize() << " bytes");
353 player
.Write(readFrame
.GetPayloadPtr(), readFrame
.GetPayloadSize());
354 PTRACE(3, "Play audio from the buffer to sound device, ts=" << consumeTimestamp
);
357 PTRACE(3, "Write silence to sound device, " << bytesPerBlock
<< " bytes");
358 player
.Write(silence
, bytesPerBlock
);
359 PTRACE(3, "Play audio from silence buffer to sound device, ts=" << consumeTimestamp
);
362 PTRACE(3, "Write to sound device took " << (thisTime
- lastWriteTime
).GetMilliSeconds() << " ms");
364 consumeTimestamp
+= (bytesPerBlock
/ 2);
368 jitterBuffer
.CloseDown();
370 PTRACE(3, "End of consume udp packets ");
373 void JesterProcess::ManageUserInput()
375 PConsoleChannel
console(PConsoleChannel::StandardInput
);
379 help
<< " X : Exit program\n"
380 << " Q : Exit program\n"
381 << " T : Read and write process report their current timestamps\n"
382 << " R : Report iteration counts\n"
383 << " J : Report some of the internal variables in the jitter buffer\n"
384 << " H : Write this help out\n";
390 // display the prompt
391 cout
<< "(Jester) Command ? " << flush
;
393 // terminate the menu loop if console finished
394 char ch
= (char)console
.peek();
396 cout
<< "\nConsole gone - menu disabled" << endl
;
401 PTRACE(3, "console in audio test is " << ch
);
402 switch (tolower(ch
)) {
407 cout
<< " generate thread=" << generateIndex
<< " consume thread=" << consumeIndex
<< endl
;
413 cerr
<< " Timestamps are " << generateTimestamp
<< "/" << consumeTimestamp
<< " (generate/consume)" << endl
;
415 if (generateTimestamp
> consumeTimestamp
)
416 answer
= generateTimestamp
- consumeTimestamp
;
418 answer
= consumeTimestamp
- generateTimestamp
;
419 cerr
<< " RTP difference " << answer
<< " Milliseconds difference is " << (answer
/8) << endl
;
422 cerr
<< " Target Jitter Time is " << jitterBuffer
.GetTargetJitterTime() << endl
;
423 cerr
<< " Current depth is " << jitterBuffer
.GetCurrentDepth() << endl
;
424 cerr
<< " Current Jitter Time is " << jitterBuffer
.GetCurrentJitterTime() << endl
;
433 cout
<< "end audio test" << endl
;
438 // End of File ///////////////////////////////////////////////////////////////