OLD | NEW |
| (Empty) |
1 function 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 | |
18 [SeqNo,TimeStamp,ArrTime,Size,PT,M,SSRC] = importfile(input_file); | |
19 | |
20 %% Filter out RTCP packets. | |
21 % These appear as RTP packets having payload types 72 through 76. | |
22 ix = not(ismember(PT, 72:76)); | |
23 fprintf('Removing %i RTCP packets\n', length(SeqNo) - sum(ix)); | |
24 SeqNo = SeqNo(ix); | |
25 TimeStamp = TimeStamp(ix); | |
26 ArrTime = ArrTime(ix); | |
27 Size = Size(ix); | |
28 PT = PT(ix); | |
29 M = M(ix); | |
30 SSRC = SSRC(ix); | |
31 | |
32 %% Find streams. | |
33 [uSSRC, ~, uix] = unique(SSRC); | |
34 | |
35 % If there are multiple streams, select one and purge the other | |
36 % streams from the data vectors. If there is only one stream, the | |
37 % vectors are good to use as they are. | |
38 if length(uSSRC) > 1 | |
39 for i=1:length(uSSRC) | |
40 uPT = unique(PT(uix == i)); | |
41 fprintf('%i: %s (%d packets, pt: %i', i, uSSRC{i}, ... | |
42 length(find(uix==i)), uPT(1)); | |
43 if length(uPT) > 1 | |
44 fprintf(', %i', uPT(2:end)); | |
45 end | |
46 fprintf(')\n'); | |
47 end | |
48 sel = input('Select stream number: '); | |
49 if sel < 1 || sel > length(uSSRC) | |
50 error('Out of range'); | |
51 end | |
52 ix = find(uix == sel); | |
53 % This is where the data vectors are trimmed. | |
54 SeqNo = SeqNo(ix); | |
55 TimeStamp = TimeStamp(ix); | |
56 ArrTime = ArrTime(ix); | |
57 Size = Size(ix); | |
58 PT = PT(ix); | |
59 M = M(ix); | |
60 SSRC = SSRC(ix); | |
61 end | |
62 | |
63 %% Unwrap SeqNo and TimeStamp. | |
64 SeqNoUW = maxUnwrap(SeqNo, 65535); | |
65 TimeStampUW = maxUnwrap(TimeStamp, 4294967295); | |
66 | |
67 %% Generate some stats for the stream. | |
68 fprintf('Statistics:\n'); | |
69 fprintf('SSRC: %s\n', SSRC{1}); | |
70 uPT = unique(PT); | |
71 if length(uPT) > 1 | |
72 warning('This tool cannot yet handle changes in codec sample rate'); | |
73 end | |
74 fprintf('Payload type(s): %i', uPT(1)); | |
75 if length(uPT) > 1 | |
76 fprintf(', %i', uPT(2:end)); | |
77 end | |
78 fprintf('\n'); | |
79 fprintf('Packets: %i\n', length(SeqNo)); | |
80 SortSeqNo = sort(SeqNoUW); | |
81 fprintf('Missing sequence numbers: %i\n', ... | |
82 length(find(diff(SortSeqNo) > 1))); | |
83 fprintf('Duplicated packets: %i\n', length(find(diff(SortSeqNo) == 0))); | |
84 reorderIx = findReorderedPackets(SeqNoUW); | |
85 fprintf('Reordered packets: %i\n', length(reorderIx)); | |
86 tsdiff = diff(TimeStampUW); | |
87 tsdiff = tsdiff(diff(SeqNoUW) == 1); | |
88 [utsdiff, ~, ixtsdiff] = unique(tsdiff); | |
89 fprintf('Common packet sizes:\n'); | |
90 for i = 1:length(utsdiff) | |
91 fprintf(' %i samples (%i%%)\n', ... | |
92 utsdiff(i), ... | |
93 round(100 * length(find(ixtsdiff == i))/length(ixtsdiff))); | |
94 end | |
95 | |
96 %% Trying to figure out sample rate. | |
97 fs_est = (TimeStampUW(end) - TimeStampUW(1)) / (ArrTime(end) - ArrTime(1)); | |
98 fs_vec = [8, 16, 32, 48]; | |
99 fs = 0; | |
100 for f = fs_vec | |
101 if abs((fs_est-f)/f) < 0.05 % 5% margin | |
102 fs = f; | |
103 break; | |
104 end | |
105 end | |
106 if fs == 0 | |
107 fprintf('Cannot determine sample rate. I get it to %.2f kHz\n', ... | |
108 fs_est); | |
109 fs = input('Please, input a sample rate (in kHz): '); | |
110 else | |
111 fprintf('Sample rate estimated to %i kHz\n', fs); | |
112 end | |
113 | |
114 SendTimeMs = (TimeStampUW - TimeStampUW(1)) / fs; | |
115 | |
116 fprintf('Stream duration at sender: %.1f seconds\n', ... | |
117 (SendTimeMs(end) - SendTimeMs(1)) / 1000); | |
118 | |
119 fprintf('Stream duration at receiver: %.1f seconds\n', ... | |
120 (ArrTime(end) - ArrTime(1)) / 1000); | |
121 | |
122 fprintf('Clock drift: %.2f%%\n', ... | |
123 100 * ((ArrTime(end) - ArrTime(1)) / ... | |
124 (SendTimeMs(end) - SendTimeMs(1)) - 1)); | |
125 | |
126 fprintf('Sent average bitrate: %i kbps\n', ... | |
127 round(sum(Size) * 8 / (SendTimeMs(end)-SendTimeMs(1)))); | |
128 | |
129 fprintf('Received average bitrate: %i kbps\n', ... | |
130 round(sum(Size) * 8 / (ArrTime(end)-ArrTime(1)))); | |
131 | |
132 %% Plots. | |
133 delay = ArrTime - SendTimeMs; | |
134 delay = delay - min(delay); | |
135 delayOrdered = delay; | |
136 delayOrdered(reorderIx) = nan; % Set reordered packets to NaN. | |
137 delayReordered = delay(reorderIx); % Pick the reordered packets. | |
138 sendTimeMsReordered = SendTimeMs(reorderIx); | |
139 | |
140 % Sort time arrays in packet send order. | |
141 [~, sortix] = sort(SeqNoUW); | |
142 SendTimeMs = SendTimeMs(sortix); | |
143 Size = Size(sortix); | |
144 delayOrdered = delayOrdered(sortix); | |
145 | |
146 figure | |
147 plot(SendTimeMs / 1000, delayOrdered, ... | |
148 sendTimeMsReordered / 1000, delayReordered, 'r.'); | |
149 xlabel('Send time [s]'); | |
150 ylabel('Relative transport delay [ms]'); | |
151 title(sprintf('SSRC: %s', SSRC{1})); | |
152 | |
153 SendBitrateKbps = 8 * Size(1:end-1) ./ diff(SendTimeMs); | |
154 figure | |
155 plot(SendTimeMs(1:end-1)/1000, SendBitrateKbps); | |
156 xlabel('Send time [s]'); | |
157 ylabel('Send bitrate [kbps]'); | |
158 end | |
159 | |
160 %% Subfunctions. | |
161 | |
162 % findReorderedPackets returns the index to all packets that are considered | |
163 % old compared with the largest seen sequence number. The input seqNo must | |
164 % be unwrapped for this to work. | |
165 function reorderIx = findReorderedPackets(seqNo) | |
166 largestSeqNo = seqNo(1); | |
167 reorderIx = []; | |
168 for i = 2:length(seqNo) | |
169 if seqNo(i) < largestSeqNo | |
170 reorderIx = [reorderIx; i]; %#ok<AGROW> | |
171 else | |
172 largestSeqNo = seqNo(i); | |
173 end | |
174 end | |
175 end | |
176 | |
177 %% Auto-generated subfunction. | |
178 function [SeqNo,TimeStamp,SendTime,Size,PT,M,SSRC] = ... | |
179 importfile(filename, startRow, endRow) | |
180 %IMPORTFILE Import numeric data from a text file as column vectors. | |
181 % [SEQNO,TIMESTAMP,SENDTIME,SIZE,PT,M,SSRC] = IMPORTFILE(FILENAME) Reads | |
182 % data from text file FILENAME for the default selection. | |
183 % | |
184 % [SEQNO,TIMESTAMP,SENDTIME,SIZE,PT,M,SSRC] = IMPORTFILE(FILENAME, | |
185 % STARTROW, ENDROW) Reads data from rows STARTROW through ENDROW of text | |
186 % file FILENAME. | |
187 % | |
188 % Example: | |
189 % [SeqNo,TimeStamp,SendTime,Size,PT,M,SSRC] = | |
190 % importfile('rtpdump_recv.txt',2, 123); | |
191 % | |
192 % See also TEXTSCAN. | |
193 | |
194 % Auto-generated by MATLAB on 2015/05/28 09:55:50 | |
195 | |
196 %% Initialize variables. | |
197 if nargin<=2 | |
198 startRow = 2; | |
199 endRow = inf; | |
200 end | |
201 | |
202 %% Format string for each line of text: | |
203 % column1: double (%f) | |
204 % column2: double (%f) | |
205 % column3: double (%f) | |
206 % column4: double (%f) | |
207 % column5: double (%f) | |
208 % column6: double (%f) | |
209 % column7: text (%s) | |
210 % For more information, see the TEXTSCAN documentation. | |
211 formatSpec = '%5f%11f%11f%6f%6f%3f%s%[^\n\r]'; | |
212 | |
213 %% Open the text file. | |
214 fileID = fopen(filename,'r'); | |
215 | |
216 %% Read columns of data according to format string. | |
217 % This call is based on the structure of the file used to generate this | |
218 % code. If an error occurs for a different file, try regenerating the code | |
219 % from the Import Tool. | |
220 dataArray = textscan(fileID, formatSpec, endRow(1)-startRow(1)+1, ... | |
221 'Delimiter', '', 'WhiteSpace', '', 'HeaderLines', startRow(1)-1, ... | |
222 'ReturnOnError', false); | |
223 for block=2:length(startRow) | |
224 frewind(fileID); | |
225 dataArrayBlock = textscan(fileID, formatSpec, ... | |
226 endRow(block)-startRow(block)+1, 'Delimiter', '', 'WhiteSpace', ... | |
227 '', 'HeaderLines', startRow(block)-1, 'ReturnOnError', false); | |
228 for col=1:length(dataArray) | |
229 dataArray{col} = [dataArray{col};dataArrayBlock{col}]; | |
230 end | |
231 end | |
232 | |
233 %% Close the text file. | |
234 fclose(fileID); | |
235 | |
236 %% Post processing for unimportable data. | |
237 % No unimportable data rules were applied during the import, so no post | |
238 % processing code is included. To generate code which works for | |
239 % unimportable data, select unimportable cells in a file and regenerate the | |
240 % script. | |
241 | |
242 %% Allocate imported array to column variable names | |
243 SeqNo = dataArray{:, 1}; | |
244 TimeStamp = dataArray{:, 2}; | |
245 SendTime = dataArray{:, 3}; | |
246 Size = dataArray{:, 4}; | |
247 PT = dataArray{:, 5}; | |
248 M = dataArray{:, 6}; | |
249 SSRC = dataArray{:, 7}; | |
250 end | |
251 | |
OLD | NEW |