[Shtoom] whoops -- I omitted the runme_test_playout.py file from that last patch

Zooko O'Whielacronx zooko at zooko.com
Tue Feb 22 18:15:12 CET 2005


Here is the self-test for the playout module.  It would be excellent if 
someone would think of other tests, because the playout algorithm 
probably needs some improvement.  Look at this file for an example of 
how to implement such.

Each test (there are four in here) has a "feed_next_packet()" method 
that gets called every so often to simulate an RTP packet arriving from 
the network.  For example, the EvenFlowTester calls feed_next_packet 
every 20 ms and gives another packet containing 20 ms worth of audio.  
The SmoothJitterTester calls feed_next_packet every 40 ms and gives two 
20 ms packets at once.

Each test also has a "check_test()" method which asserts that there is 
some desirable quality about the output from the playout module.  For 
example the EvenFlowTester asserts that every one of those nicely timed 
input packets got written out to the audio device.  The 
SmoothJitterTester asserts that there were no gaps between audio 
samples written to the audio device.

Regards,

Zooko

#!/usr/bin/env python
# Copyright (C) 2004 Anthony Baxter
"""Tests for shtoom.audio.playout
Hello I am not a unit test.  Run me explicitly from the cmdline.  Thank 
you.
"""

# from shtoom
import shtoom.audio.playout
# shtoom.audio.playout.DEBUG = True
from shtoom.audio.playout import Playout
from shtoom.audio.converters import MediaSample

# from the Twisted library
from twisted.internet import reactor, defer
from twisted.python import log

# from the Python Standard Library
import sys
log.startLogging(sys.stdout)


EPSILON=0.0001

SAMPLEHZ = 8000
SAMPLESIZE = 2
BPS = SAMPLEHZ * SAMPLESIZE

PACKETLEN=0.020

PACKETSIZE=int(PACKETLEN * BPS)

# We must give enough test packets to satisfy the playout object's 
desire to store up media in order to compensate for jitter.
TESTPACKETS=int(shtoom.audio.playout.JITTER_BUFFER_SECONDS * BPS / 
PACKETSIZE)

import datetime, time
def timestamp():
     return datetime.datetime.fromtimestamp(time.time()).isoformat(' ')

class DummyWriter:
     def __init__(self):
         self.b = []
         self.ts = []
     def write(self, data):
         self.b.append(data)
         self.ts.append(time.time())

class DummyMediaLayer:
     def __init__(self):
         self._d = DummyWriter()

class Tester:
     def __init__(self):
         self.dml = DummyMediaLayer()
         self.p = Playout(self.dml, )
         self.inb = []
         for i in range(TESTPACKETS):
             self.inb.append(str(i) + ('\x00' * (PACKETSIZE - 
len(str(i)))))
         self.i = 0
         log.msg("%s %s.__init__()" % (timestamp(), 
self.__class__.__name__,))
         self.d = defer.Deferred()
         reactor.callLater(0, self.feed_next_packet)


def repr_buf_for_log(buf):
     return map(lambda x: x[:3], buf)

class EvenFlowTester(Tester):
     """
     If a steady flow of TESTPACKETS packets arrives, followed by a long 
silence, then the playout ought to play all TESTPACKETS of the packets.
     """
     def check_test(self):
         self.d.callback(None)
         assert self.dml._d.b == self.inb, "%s %s, %s" % (timestamp(), 
repr_buf_for_log(self.dml._d.b), repr_buf_for_log(self.inb),)
         log.msg("%s %s success" % (timestamp(), self,))

     def feed_next_packet(self):
         # log.msg("%s %s about to write %s, %s" % (timestamp(), 
self.__class__.__name__, `self.inb[self.i]`, self.i,))
         data = self.inb[self.i]
         self.p.write(data, self.i)
         self.i += 1
         if self.i < TESTPACKETS:
             reactor.callLater(max(0, (len(data) / float(BPS)) - 
EPSILON), self.feed_next_packet)
         else:
             reactor.callLater(max(0, ((len(data) * len(self.inb)) / 
float(BPS)) - EPSILON), self.check_test)


class OutOfOrderArrivalTester(Tester):
     """
     If a steady flow of TESTPACKETS packets arrives, but with each pair 
swapped, followed by a long silence, then the playout ought to play all 
TESTPACKETS of the packets (in the right order).
     """
     def check_test(self):
         self.d.callback(None)
         assert self.dml._d.b == self.inb, "%s, %s" % (self.dml._d.b, 
self.inb,)
         log.msg("%s %s success" % (timestamp(), self,))

     def feed_next_packet(self):
         thisi = self.i ^ 1
         data = self.inb[thisi]
         self.p.write(data, thisi)
         self.i += 1
         if self.i < TESTPACKETS:
             reactor.callLater(max(0, (len(data) / float(BPS)) - 
EPSILON), self.feed_next_packet)
         else:
             reactor.callLater(max(0, ((len(data) * len(self.inb)) / 
float(BPS)) - EPSILON), self.check_test)


class CatchupTester(Tester):
     """
     If a steady flow of TESTPACKETS packets arrives at faster than 
realtime, followed by a long silence, then the playout ought to play 
the last packet.
     """
     def check_test(self):
         self.d.callback(None)
         assert self.dml._d.b[-1] == self.inb[-1], "%s, %s" % 
(`self.dml._d.b[-1][:4]`, `self.inb[-1][:4]`,)
         log.msg("%s %s success" % (timestamp(), self,))

     def feed_next_packet(self):
         data = self.inb[self.i]
         self.p.write(data, self.i)
         self.i += 1
         if self.i < TESTPACKETS:
             reactor.callLater(0, self.feed_next_packet)
         else:
             reactor.callLater(max(0, ((len(data) * len(self.inb)) / 
float(BPS)) - EPSILON), self.check_test)

class SmoothJitterTester(Tester):
     """
     If an unstead flow arrives: two packets back-to-back followed by an 
"empty slot" followed by two packets back-to-back, etc., then the 
playout should output a perfectly even flow.
     """
     def check_test(self):
         self.d.callback(None)
         prevend = self.dml._d.ts[0] + (len(self.dml._d.b[0]) / 
float(BPS))
         for i in range(1, len(self.dml._d.ts)):
             ts = self.dml._d.ts[i]
             bl = len(self.dml._d.b[i])
             assert ts <= prevend, "ts: %s, prevend: %s, i: %s" % (ts, 
prevend, i,)
             prevend += bl / float(BPS)
         log.msg("%s %s success" % (timestamp(), self,))

     def feed_next_packet(self):
         data = str(self.i) + ('\x00' * (PACKETSIZE - len(str(self.i))))
         self.inb.append(time.time())
         self.p.write(data, self.i)
         self.i += 1
         data = str(self.i) + ('\x00' * (PACKETSIZE - len(str(self.i))))
         self.inb.append(time.time())
         self.p.write(data, self.i)
         self.i += 1
         if self.i < TESTPACKETS:
             reactor.callLater(max(0, (2 * len(data) / float(BPS)) - 
EPSILON), self.feed_next_packet)
         else:
             reactor.callLater(max(0, ((len(data) * len(self.inb) * 2) / 
float(BPS)) - EPSILON), self.check_test)


if __name__ == "__main__":
     l = []
     l.append(EvenFlowTester().d)
     l.append(CatchupTester().d)
     l.append(SmoothJitterTester().d)
     l.append(OutOfOrderArrivalTester().d)
     dl = defer.DeferredList(l)
     dl.addCallbacks(lambda x: reactor.stop(), lambda x: reactor.stop())
     reactor.run()



-------------- next part --------------
A non-text attachment was scrubbed...
Name: runme_test_playout.py
Type: application/octet-stream
Size: 5851 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/shtoom/attachments/20050222/24fd4675/attachment.obj>


More information about the Shtoom mailing list