1%
2%  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3%
4%  Use of this source code is governed by a BSD-style license
5%  that can be found in the LICENSE file in the root of the source
6%  tree. An additional intellectual property rights grant can be found
7%  in the file PATENTS.  All contributing project authors may
8%  be found in the AUTHORS file in the root of the source tree.
9%
10
11function [delay_struct, delayvalues] = plot_neteq_delay(delayfile, varargin)
12
13% InfoStruct = plot_neteq_delay(delayfile)
14% InfoStruct = plot_neteq_delay(delayfile, 'skipdelay', skip_seconds)
15%
16% Henrik Lundin, 2006-11-17
17% Henrik Lundin, 2011-05-17
18%
19
20try
21    s = parse_delay_file(delayfile);
22catch
23    error(lasterr);
24end
25
26delayskip=0;
27noplot=0;
28arg_ptr=1;
29delaypoints=[];
30
31s.sn=unwrap_seqno(s.sn);
32
33while arg_ptr+1 <= nargin
34    switch lower(varargin{arg_ptr})
35    case {'skipdelay', 'delayskip'}
36        % skip a number of seconds in the beginning when calculating delays
37        delayskip = varargin{arg_ptr+1};
38        arg_ptr = arg_ptr + 2;
39    case 'noplot'
40        noplot=1;
41        arg_ptr = arg_ptr + 1;
42    case {'get_delay', 'getdelay'}
43        % return a vector of delay values for the points in the given vector
44        delaypoints = varargin{arg_ptr+1};
45        arg_ptr = arg_ptr + 2;
46    otherwise
47        warning('Unknown switch %s\n', varargin{arg_ptr});
48        arg_ptr = arg_ptr + 1;
49    end
50end
51
52% find lost frames that were covered by one-descriptor decoding
53one_desc_ix=find(isnan(s.arrival));
54for k=1:length(one_desc_ix)
55    ix=find(s.ts==max(s.ts(s.ts(one_desc_ix(k))>s.ts)));
56    s.sn(one_desc_ix(k))=s.sn(ix)+1;
57    s.pt(one_desc_ix(k))=s.pt(ix);
58    s.arrival(one_desc_ix(k))=s.arrival(ix)+s.decode(one_desc_ix(k))-s.decode(ix);
59end
60
61% remove duplicate received frames that were never decoded (RED codec)
62if length(unique(s.ts(isfinite(s.ts)))) < length(s.ts(isfinite(s.ts)))
63    ix=find(isfinite(s.decode));
64    s.sn=s.sn(ix);
65    s.ts=s.ts(ix);
66    s.arrival=s.arrival(ix);
67    s.playout_delay=s.playout_delay(ix);
68    s.pt=s.pt(ix);
69    s.optbuf=s.optbuf(ix);
70    plen=plen(ix);
71    s.decode=s.decode(ix);
72end
73
74% find non-unique sequence numbers
75[~,un_ix]=unique(s.sn);
76nonun_ix=setdiff(1:length(s.sn),un_ix);
77if ~isempty(nonun_ix)
78    warning('RTP sequence numbers are in error');
79end
80
81% sort vectors
82[s.sn,sort_ix]=sort(s.sn);
83s.ts=s.ts(sort_ix);
84s.arrival=s.arrival(sort_ix);
85s.decode=s.decode(sort_ix);
86s.playout_delay=s.playout_delay(sort_ix);
87s.pt=s.pt(sort_ix);
88
89send_t=s.ts-s.ts(1);
90if length(s.fs)<1
91    warning('No info about sample rate found in file. Using default 8000.');
92    s.fs(1)=8000;
93    s.fschange_ts(1)=min(s.ts);
94elseif s.fschange_ts(1)>min(s.ts)
95    s.fschange_ts(1)=min(s.ts);
96end
97
98end_ix=length(send_t);
99for k=length(s.fs):-1:1
100    start_ix=find(s.ts==s.fschange_ts(k));
101    send_t(start_ix:end_ix)=send_t(start_ix:end_ix)/s.fs(k)*1000;
102    s.playout_delay(start_ix:end_ix)=s.playout_delay(start_ix:end_ix)/s.fs(k)*1000;
103    s.optbuf(start_ix:end_ix)=s.optbuf(start_ix:end_ix)/s.fs(k)*1000;
104    end_ix=start_ix-1;
105end
106
107tot_time=max(send_t)-min(send_t);
108
109seq_ix=s.sn-min(s.sn)+1;
110send_t=send_t+max(min(s.arrival-send_t),0);
111
112plot_send_t=nan*ones(max(seq_ix),1);
113plot_send_t(seq_ix)=send_t;
114plot_nw_delay=nan*ones(max(seq_ix),1);
115plot_nw_delay(seq_ix)=s.arrival-send_t;
116
117cng_ix=find(s.pt~=13); % find those packets that are not CNG/SID
118
119if noplot==0
120    h=plot(plot_send_t/1000,plot_nw_delay);
121    set(h,'color',0.75*[1 1 1]);
122    hold on
123    if any(s.optbuf~=0)
124        peak_ix=find(s.optbuf(cng_ix)<0); % peak mode is labeled with negative values
125        no_peak_ix=find(s.optbuf(cng_ix)>0); %setdiff(1:length(cng_ix),peak_ix);
126        h1=plot(send_t(cng_ix(peak_ix))/1000,...
127            s.arrival(cng_ix(peak_ix))+abs(s.optbuf(cng_ix(peak_ix)))-send_t(cng_ix(peak_ix)),...
128            'r.');
129        h2=plot(send_t(cng_ix(no_peak_ix))/1000,...
130            s.arrival(cng_ix(no_peak_ix))+abs(s.optbuf(cng_ix(no_peak_ix)))-send_t(cng_ix(no_peak_ix)),...
131            'g.');
132        set([h1, h2],'markersize',1)
133    end
134    %h=plot(send_t(seq_ix)/1000,s.decode+s.playout_delay-send_t(seq_ix));
135    h=plot(send_t(cng_ix)/1000,s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix));
136    set(h,'linew',1.5);
137    hold off
138    ax1=axis;
139    axis tight
140    ax2=axis;
141    axis([ax2(1:3) ax1(4)])
142end
143
144
145% calculate delays and other parameters
146
147delayskip_ix = find(send_t-send_t(1)>=delayskip*1000, 1 );
148
149use_ix = intersect(cng_ix,... % use those that are not CNG/SID frames...
150    intersect(find(isfinite(s.decode)),... % ... that did arrive ...
151    (delayskip_ix:length(s.decode))')); % ... and are sent after delayskip seconds
152
153mean_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-send_t(use_ix));
154neteq_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix));
155
156Npack=max(s.sn(delayskip_ix:end))-min(s.sn(delayskip_ix:end))+1;
157nw_lossrate=(Npack-length(s.sn(delayskip_ix:end)))/Npack;
158neteq_lossrate=(length(s.sn(delayskip_ix:end))-length(use_ix))/Npack;
159
160delay_struct=struct('mean_delay',mean_delay,'neteq_delay',neteq_delay,...
161    'nw_lossrate',nw_lossrate,'neteq_lossrate',neteq_lossrate,...
162    'tot_expand',round(s.tot_expand),'tot_accelerate',round(s.tot_accelerate),...
163    'tot_preemptive',round(s.tot_preemptive),'tot_time',tot_time,...
164    'filename',delayfile,'units','ms','fs',unique(s.fs));
165
166if not(isempty(delaypoints))
167    delayvalues=interp1(send_t(cng_ix),...
168        s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix),...
169        delaypoints,'nearest',NaN);
170else
171    delayvalues=[];
172end
173
174
175
176% SUBFUNCTIONS %
177
178function y=unwrap_seqno(x)
179
180jumps=find(abs((diff(x)-1))>65000);
181
182while ~isempty(jumps)
183    n=jumps(1);
184    if x(n+1)-x(n) < 0
185        % negative jump
186        x(n+1:end)=x(n+1:end)+65536;
187    else
188        % positive jump
189        x(n+1:end)=x(n+1:end)-65536;
190    end
191
192    jumps=find(abs((diff(x(n+1:end))-1))>65000);
193end
194
195y=x;
196
197return;
198