blob: eb0cb8f586ffa879151fa18b44ae94ff59c295fc [file] [log] [blame]
Henrik Lundin80518322015-05-28 12:37:46 +02001function rtpAnalyze( input_file )
2%RTP_ANALYZE Analyze RTP stream(s) from a txt file
3% The function takes the output from the command line tool rtp_analyze
4% and analyzes the stream(s) therein. First, process your rtpdump file
5% through rtp_analyze (from command line):
6% $ out/Debug/rtp_analyze my_file.rtp my_file.txt
7% Then load it with this function (in Matlab):
8% >> rtpAnalyze('my_file.txt')
9
10% Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
11%
12% Use of this source code is governed by a BSD-style license
13% that can be found in the LICENSE file in the root of the source
14% tree. An additional intellectual property rights grant can be found
15% in the file PATENTS. All contributing project authors may
16% be found in the AUTHORS file in the root of the source tree.
17
Henrik Lundin80518322015-05-28 12:37:46 +020018[SeqNo,TimeStamp,ArrTime,Size,PT,M,SSRC] = importfile(input_file);
19
Henrik Lundin60508f82015-06-03 09:38:23 +020020%% Find streams.
Henrik Lundin80518322015-05-28 12:37:46 +020021[uSSRC, ~, uix] = unique(SSRC);
22
Henrik Lundin60508f82015-06-03 09:38:23 +020023% If there are multiple streams, select one and purge the other
24% streams from the data vectors. If there is only one stream, the
25% vectors are good to use as they are.
Henrik Lundin80518322015-05-28 12:37:46 +020026if length(uSSRC) > 1
27 for i=1:length(uSSRC)
28 uPT = unique(PT(uix == i));
29 fprintf('%i: %s (%d packets, pt: %i', i, uSSRC{i}, ...
30 length(find(uix==i)), uPT(1));
31 if length(uPT) > 1
32 fprintf(', %i', uPT(2:end));
33 end
34 fprintf(')\n');
35 end
36 sel = input('Select stream number: ');
37 if sel < 1 || sel > length(uSSRC)
38 error('Out of range');
39 end
Henrik Lundin60508f82015-06-03 09:38:23 +020040 ix = find(uix == sel);
41 % This is where the data vectors are trimmed.
Henrik Lundin80518322015-05-28 12:37:46 +020042 SeqNo = SeqNo(ix);
43 TimeStamp = TimeStamp(ix);
44 ArrTime = ArrTime(ix);
45 Size = Size(ix);
46 PT = PT(ix);
47 M = M(ix);
48 SSRC = SSRC(ix);
49end
50
Henrik Lundin60508f82015-06-03 09:38:23 +020051%% Unwrap SeqNo and TimeStamp.
Henrik Lundin80518322015-05-28 12:37:46 +020052SeqNoUW = maxUnwrap(SeqNo, 65535);
53TimeStampUW = maxUnwrap(TimeStamp, 4294967295);
54
Henrik Lundin60508f82015-06-03 09:38:23 +020055%% Generate some stats for the stream.
Henrik Lundin80518322015-05-28 12:37:46 +020056fprintf('Statistics:\n');
57fprintf('SSRC: %s\n', SSRC{1});
58uPT = unique(PT);
59if length(uPT) > 1
60 warning('This tool cannot yet handle changes in codec sample rate');
61end
62fprintf('Payload type(s): %i', uPT(1));
63if length(uPT) > 1
64 fprintf(', %i', uPT(2:end));
65end
66fprintf('\n');
67fprintf('Packets: %i\n', length(SeqNo));
Henrik Lundin76381d92015-06-16 09:28:09 +020068SortSeqNo = sort(SeqNoUW);
Henrik Lundin80518322015-05-28 12:37:46 +020069fprintf('Missing sequence numbers: %i\n', ...
Henrik Lundin76381d92015-06-16 09:28:09 +020070 length(find(diff(SortSeqNo) > 1)));
71fprintf('Duplicated packets: %i\n', length(find(diff(SortSeqNo) == 0)));
72reorderIx = findReorderedPackets(SeqNoUW);
73fprintf('Reordered packets: %i\n', length(reorderIx));
Henrik Lundin80518322015-05-28 12:37:46 +020074tsdiff = diff(TimeStampUW);
75tsdiff = tsdiff(diff(SeqNoUW) == 1);
76[utsdiff, ~, ixtsdiff] = unique(tsdiff);
77fprintf('Common packet sizes:\n');
78for i = 1:length(utsdiff)
79 fprintf(' %i samples (%i%%)\n', ...
80 utsdiff(i), ...
Henrik Lundin60508f82015-06-03 09:38:23 +020081 round(100 * length(find(ixtsdiff == i))/length(ixtsdiff)));
Henrik Lundin80518322015-05-28 12:37:46 +020082end
83
Henrik Lundin60508f82015-06-03 09:38:23 +020084%% Trying to figure out sample rate.
Henrik Lundin80518322015-05-28 12:37:46 +020085fs_est = (TimeStampUW(end) - TimeStampUW(1)) / (ArrTime(end) - ArrTime(1));
86fs_vec = [8, 16, 32, 48];
87fs = 0;
88for f = fs_vec
89 if abs((fs_est-f)/f) < 0.05 % 5% margin
90 fs = f;
91 break;
92 end
93end
94if fs == 0
95 fprintf('Cannot determine sample rate. I get it to %.2f kHz\n', ...
96 fs_est);
97 fs = input('Please, input a sample rate (in kHz): ');
98else
99 fprintf('Sample rate estimated to %i kHz\n', fs);
100end
101
102SendTimeMs = (TimeStampUW - TimeStampUW(1)) / fs;
103
104fprintf('Stream duration at sender: %.1f seconds\n', ...
105 (SendTimeMs(end) - SendTimeMs(1)) / 1000);
106
107fprintf('Stream duration at receiver: %.1f seconds\n', ...
108 (ArrTime(end) - ArrTime(1)) / 1000);
109
110fprintf('Clock drift: %.2f%%\n', ...
111 100 * ((ArrTime(end) - ArrTime(1)) / ...
112 (SendTimeMs(end) - SendTimeMs(1)) - 1));
113
114fprintf('Sent average bitrate: %i kbps\n', ...
115 round(sum(Size) * 8 / (SendTimeMs(end)-SendTimeMs(1))));
Henrik Lundin60508f82015-06-03 09:38:23 +0200116
Henrik Lundin80518322015-05-28 12:37:46 +0200117fprintf('Received average bitrate: %i kbps\n', ...
118 round(sum(Size) * 8 / (ArrTime(end)-ArrTime(1))));
119
Henrik Lundin60508f82015-06-03 09:38:23 +0200120%% Plots.
Henrik Lundin80518322015-05-28 12:37:46 +0200121delay = ArrTime - SendTimeMs;
122delay = delay - min(delay);
Henrik Lundin76381d92015-06-16 09:28:09 +0200123delayOrdered = delay;
124delayOrdered(reorderIx) = nan; % Set reordered packets to NaN.
125delayReordered = delay(reorderIx); % Pick the reordered packets.
126sendTimeMsReordered = SendTimeMs(reorderIx);
127
128% Sort time arrays in packet send order.
129[~, sortix] = sort(SeqNoUW);
130SendTimeMs = SendTimeMs(sortix);
131Size = Size(sortix);
132delayOrdered = delayOrdered(sortix);
133
Henrik Lundin80518322015-05-28 12:37:46 +0200134figure
Henrik Lundin76381d92015-06-16 09:28:09 +0200135plot(SendTimeMs / 1000, delayOrdered, ...
136 sendTimeMsReordered / 1000, delayReordered, 'r.');
Henrik Lundin80518322015-05-28 12:37:46 +0200137xlabel('Send time [s]');
138ylabel('Relative transport delay [ms]');
139title(sprintf('SSRC: %s', SSRC{1}));
140
141SendBitrateKbps = 8 * Size(1:end-1) ./ diff(SendTimeMs);
142figure
143plot(SendTimeMs(1:end-1)/1000, SendBitrateKbps);
144xlabel('Send time [s]');
145ylabel('Send bitrate [kbps]');
146end
147
Henrik Lundin76381d92015-06-16 09:28:09 +0200148%% Subfunctions.
149
150% findReorderedPackets returns the index to all packets that are considered
151% old compared with the largest seen sequence number. The input seqNo must
152% be unwrapped for this to work.
153function reorderIx = findReorderedPackets(seqNo)
154largestSeqNo = seqNo(1);
155reorderIx = [];
156for i = 2:length(seqNo)
157 if seqNo(i) < largestSeqNo
158 reorderIx = [reorderIx; i]; %#ok<AGROW>
159 else
160 largestSeqNo = seqNo(i);
161 end
162end
163end
164
Henrik Lundin60508f82015-06-03 09:38:23 +0200165%% Auto-generated subfunction.
Henrik Lundin80518322015-05-28 12:37:46 +0200166function [SeqNo,TimeStamp,SendTime,Size,PT,M,SSRC] = ...
167 importfile(filename, startRow, endRow)
168%IMPORTFILE Import numeric data from a text file as column vectors.
169% [SEQNO,TIMESTAMP,SENDTIME,SIZE,PT,M,SSRC] = IMPORTFILE(FILENAME) Reads
170% data from text file FILENAME for the default selection.
171%
172% [SEQNO,TIMESTAMP,SENDTIME,SIZE,PT,M,SSRC] = IMPORTFILE(FILENAME,
173% STARTROW, ENDROW) Reads data from rows STARTROW through ENDROW of text
174% file FILENAME.
175%
176% Example:
177% [SeqNo,TimeStamp,SendTime,Size,PT,M,SSRC] =
178% importfile('rtpdump_recv.txt',2, 123);
179%
180% See also TEXTSCAN.
181
182% Auto-generated by MATLAB on 2015/05/28 09:55:50
183
184%% Initialize variables.
185if nargin<=2
186 startRow = 2;
187 endRow = inf;
188end
189
190%% Format string for each line of text:
191% column1: double (%f)
192% column2: double (%f)
193% column3: double (%f)
194% column4: double (%f)
195% column5: double (%f)
196% column6: double (%f)
197% column7: text (%s)
198% For more information, see the TEXTSCAN documentation.
199formatSpec = '%5f%11f%11f%6f%6f%3f%s%[^\n\r]';
200
201%% Open the text file.
202fileID = fopen(filename,'r');
203
204%% Read columns of data according to format string.
205% This call is based on the structure of the file used to generate this
206% code. If an error occurs for a different file, try regenerating the code
207% from the Import Tool.
208dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, ...
209 'Delimiter', '', 'WhiteSpace', '', 'HeaderLines', startRow(1)-1, ...
210 'ReturnOnError', false);
211for block=2:length(startRow)
212 frewind(fileID);
213 dataArrayBlock = textscan(fileID, formatSpec, ...
214 endRow(block)-startRow(block)+1, 'Delimiter', '', 'WhiteSpace', ...
215 '', 'HeaderLines', startRow(block)-1, 'ReturnOnError', false);
216 for col=1:length(dataArray)
217 dataArray{col} = [dataArray{col};dataArrayBlock{col}];
218 end
219end
220
221%% Close the text file.
222fclose(fileID);
223
224%% Post processing for unimportable data.
225% No unimportable data rules were applied during the import, so no post
226% processing code is included. To generate code which works for
227% unimportable data, select unimportable cells in a file and regenerate the
228% script.
229
230%% Allocate imported array to column variable names
231SeqNo = dataArray{:, 1};
232TimeStamp = dataArray{:, 2};
233SendTime = dataArray{:, 3};
234Size = dataArray{:, 4};
235PT = dataArray{:, 5};
236M = dataArray{:, 6};
237SSRC = dataArray{:, 7};
238end
239