Darwin-Streaming-Server/StreamingLoadTool/parseSLTOut.py

204 lines
6.6 KiB
Python
Raw Normal View History

#!python
"""Script for parsing the output of a StreamingLoadTool file. Can be used both as a script and as a python module.
The output is a commma separated file. The StreamingLoadTool has to be run with -V 3 or above.
Usage: ParseSLTOut.py [-h] [-b interval] [-s all] [filename]
options:
-h show this help message and exit
-b Take the packet lengths and categorizes them according to their arrival time.
The packet lengths within the same bucket (of size interval seconds) are summed up
and averaged over the interval, which is useful for seeing the bit rate.
-s The streams to parse: one of video, audio, all; default is video
If filename is missing, then the script will read from standard in. The script writes to standard out."""
"""
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2008 Apple Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*
"""
import re, sys
#the regular expression for matching lines in the SLT output
playRE = re.compile(\
r"^Receiving track (\d+), trackID=(\d+), (\w+) at time (\d+)")
timeRE = re.compile(\
r"^Processing incoming packets at time (\d+)")
processedRE = re.compile(\
r"^Processed packet: track=(\d+), len=(\d+), seq=(\d+), time=(\d+)\((\d+)\); bufferingDelay=(\d+), FBS=(\d+)")
def parseSLTInput(inputFile):
"""
Parses a string consisting of outputs from StreamingLoadTool.
The output is a list, where the index is the track index. The value is a map that maps from processing time to
packets. Each packet is a 3-tuple containing the packet length(in bytes), the sequence number, and the timestamp.
"""
videoTrackIndex = None
audioTrackIndex = None
localTimeBase = None
curTime = None
packetTable = [{}, {}]
for line in inputFile:
matchObj = playRE.match(line)
if matchObj is not None:
trackIndex, trackID, trackType, startTime = matchObj.groups();
if trackType == "video":
videoTrackIndex = int(trackIndex)
elif trackType == "audio":
audioTrackIndex = int(trackIndex)
localTimeBase = int(startTime)
continue
matchObj = timeRE.match(line)
if matchObj is not None:
processingTime, = matchObj.groups()
curTime = int(processingTime)
continue
matchObj = processedRE.match(line)
if matchObj is not None:
trackIndex, packetLen, seqNum, timeStamp, timeStampInMediaTime, playoutDelay, freeBufferSpace = matchObj.groups()
trackIndex = int(trackIndex);
while trackIndex >= len(packetTable):
packetTable.append({})
packetList = packetTable[trackIndex].setdefault(curTime, [])
packetList.append( (int(packetLen), int(seqNum), int(timeStamp)) )
continue
if localTimeBase is None:
sys.exit("Parse error: cannot find a track")
# modify the processing times to be 0-based
newPacketTable = [{}] * len(packetTable)
for trackIndex in range(len(packetTable)):
newPackets = {}
for (curTime, packet) in packetTable[trackIndex].items():
curTime -= localTimeBase
newPackets[curTime] = packet
packetTable[trackIndex] = newPackets
return (packetTable, videoTrackIndex, audioTrackIndex)
def calcBitRate(xList, yList, interval):
"""xList is a list of processing time, and yList is a list of packet size.
The function will calculate the bitrate, divided into interval-sized buckets, and returns it as a list
of (processing time, bitrate) pair.
xList is expected to be a list of time in milliseconds, and yList is expected to be a list of packet size in bytes."""
maxIndex = max(xList) / interval
bitsReceived = [0] * (maxIndex + 1)
for i in range(len(yList)):
x = xList[i]
y = yList[i]
bitsReceived[x / interval] += y * 8
return [ (i * interval, (bitsReceived[i] * 1000) / interval) for i in range(len(bitsReceived))]
if __name__ == "__main__":
# the file is ran as a script
# first parse the command line
import getopt
from decimal import Decimal
try:
optlist, args = getopt.getopt(sys.argv[1:], "hb:s:")
except getopt.GetoptError:
sys.exit(__doc__)
categorize = False
streams = 'video'
for opt, optarg in optlist:
if opt == '-h':
print __doc__
sys.exit(0)
elif opt == '-b':
categorize = True
try:
interval = int(Decimal(optarg) * 1000)
except:
sys.exit(__doc__)
elif opt == '-s':
streams = optarg
if streams != 'video' and streams != 'audio' and streams != 'all':
sys.exit(__doc__)
if len(args) == 0:
inputFile = sys.stdin
elif len(args) == 1:
inputFile = open(args[0], 'r')
else:
sys.exit(__doc__)
(packetTable, videoIndex, audioIndex) = parseSLTInput(inputFile)
if streams == 'video':
if videoIndex is None:
sys.exit("Parse error: Cannot find a video stream")
packets = packetTable[videoIndex]
elif streams == 'audio':
if audioIndex is None:
sys.exit("Parse error: Cannot find an audio stream")
packets = packetTable[audioIndex]
else:
packets = {}
for perStreamPackets in packetTable:
for (time, packetList) in perStreamPackets.items():
packets.setdefault(time, []).extend(packetList)
if len(packets) == 0:
sys.exit("Error: Cannot find a stream")
def millisecondsToSec(time):
"""Convert time to 123.456 form as a string"""
return str(time / 1000) + '.' + str(time % 1000)
if categorize:
xList = []
yList = []
for processingTime, packetList in packets.items():
for (packetLen, seqNum, timeStamp) in packetList:
xList.append(processingTime)
yList.append(packetLen)
data = calcBitRate(xList, yList, interval)
for (time, bitrate) in data:
print "%s, %i" % (millisecondsToSec(time), bitrate)
else:
processingTimes = packets.keys()
processingTimes.sort()
for processingTime in processingTimes:
packetList = packets[processingTime]
for (packetLen, seqNum, timeStamp) in packets[processingTime]:
# output.append( (processingTime, packetLen) )
print ("%s, %i") % ( millisecondsToSec(processingTime), packetLen )