Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,201 @@
|
|||
%
|
||||
% Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
%
|
||||
% Use of this source code is governed by a BSD-style license
|
||||
% that can be found in the LICENSE file in the root of the source
|
||||
% tree. An additional intellectual property rights grant can be found
|
||||
% in the file PATENTS. All contributing project authors may
|
||||
% be found in the AUTHORS file in the root of the source tree.
|
||||
%
|
||||
|
||||
function outStruct = parse_delay_file(file)
|
||||
|
||||
fid = fopen(file, 'rb');
|
||||
if fid == -1
|
||||
error('Cannot open file %s', file);
|
||||
end
|
||||
|
||||
textline = fgetl(fid);
|
||||
if ~strncmp(textline, '#!NetEQ_Delay_Logging', 21)
|
||||
error('Wrong file format');
|
||||
end
|
||||
|
||||
ver = sscanf(textline, '#!NetEQ_Delay_Logging%d.%d');
|
||||
if ~all(ver == [2; 0])
|
||||
error('Wrong version of delay logging function')
|
||||
end
|
||||
|
||||
|
||||
start_pos = ftell(fid);
|
||||
fseek(fid, -12, 'eof');
|
||||
textline = fgetl(fid);
|
||||
if ~strncmp(textline, 'End of file', 21)
|
||||
error('File ending is not correct. Seems like the simulation ended abnormally.');
|
||||
end
|
||||
|
||||
fseek(fid,-12-4, 'eof');
|
||||
Npackets = fread(fid, 1, 'int32');
|
||||
fseek(fid, start_pos, 'bof');
|
||||
|
||||
rtpts = zeros(Npackets, 1);
|
||||
seqno = zeros(Npackets, 1);
|
||||
pt = zeros(Npackets, 1);
|
||||
plen = zeros(Npackets, 1);
|
||||
recin_t = nan*ones(Npackets, 1);
|
||||
decode_t = nan*ones(Npackets, 1);
|
||||
playout_delay = zeros(Npackets, 1);
|
||||
optbuf = zeros(Npackets, 1);
|
||||
|
||||
fs_ix = 1;
|
||||
clock = 0;
|
||||
ts_ix = 1;
|
||||
ended = 0;
|
||||
late_packets = 0;
|
||||
fs_now = 8000;
|
||||
last_decode_k = 0;
|
||||
tot_expand = 0;
|
||||
tot_accelerate = 0;
|
||||
tot_preemptive = 0;
|
||||
|
||||
while not(ended)
|
||||
signal = fread(fid, 1, '*int32');
|
||||
|
||||
switch signal
|
||||
case 3 % NETEQ_DELAY_LOGGING_SIGNAL_CLOCK
|
||||
clock = fread(fid, 1, '*float32');
|
||||
|
||||
% keep on reading batches of M until the signal is no longer "3"
|
||||
% read int32 + float32 in one go
|
||||
% this is to save execution time
|
||||
temp = [3; 0];
|
||||
M = 120;
|
||||
while all(temp(1,:) == 3)
|
||||
fp = ftell(fid);
|
||||
temp = fread(fid, [2 M], '*int32');
|
||||
end
|
||||
|
||||
% back up to last clock event
|
||||
fseek(fid, fp - ftell(fid) + ...
|
||||
(find(temp(1,:) ~= 3, 1 ) - 2) * 2 * 4 + 4, 'cof');
|
||||
% read the last clock value
|
||||
clock = fread(fid, 1, '*float32');
|
||||
|
||||
case 1 % NETEQ_DELAY_LOGGING_SIGNAL_RECIN
|
||||
temp_ts = fread(fid, 1, 'uint32');
|
||||
|
||||
if late_packets > 0
|
||||
temp_ix = ts_ix - 1;
|
||||
while (temp_ix >= 1) && (rtpts(temp_ix) ~= temp_ts)
|
||||
% TODO(hlundin): use matlab vector search instead?
|
||||
temp_ix = temp_ix - 1;
|
||||
end
|
||||
|
||||
if temp_ix >= 1
|
||||
% the ts was found in the vector
|
||||
late_packets = late_packets - 1;
|
||||
else
|
||||
temp_ix = ts_ix;
|
||||
ts_ix = ts_ix + 1;
|
||||
end
|
||||
else
|
||||
temp_ix = ts_ix;
|
||||
ts_ix = ts_ix + 1;
|
||||
end
|
||||
|
||||
rtpts(temp_ix) = temp_ts;
|
||||
seqno(temp_ix) = fread(fid, 1, 'uint16');
|
||||
pt(temp_ix) = fread(fid, 1, 'int32');
|
||||
plen(temp_ix) = fread(fid, 1, 'int16');
|
||||
recin_t(temp_ix) = clock;
|
||||
|
||||
case 2 % NETEQ_DELAY_LOGGING_SIGNAL_FLUSH
|
||||
% do nothing
|
||||
|
||||
case 4 % NETEQ_DELAY_LOGGING_SIGNAL_EOF
|
||||
ended = 1;
|
||||
|
||||
case 5 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE
|
||||
last_decode_ts = fread(fid, 1, 'uint32');
|
||||
temp_delay = fread(fid, 1, 'uint16');
|
||||
|
||||
k = find(rtpts(1:(ts_ix - 1))==last_decode_ts,1,'last');
|
||||
if ~isempty(k)
|
||||
decode_t(k) = clock;
|
||||
playout_delay(k) = temp_delay + ...
|
||||
5 * fs_now / 8000; % add overlap length
|
||||
last_decode_k = k;
|
||||
end
|
||||
|
||||
case 6 % NETEQ_DELAY_LOGGING_SIGNAL_CHANGE_FS
|
||||
fsvec(fs_ix) = fread(fid, 1, 'uint16');
|
||||
fschange_ts(fs_ix) = last_decode_ts;
|
||||
fs_now = fsvec(fs_ix);
|
||||
fs_ix = fs_ix + 1;
|
||||
|
||||
case 7 % NETEQ_DELAY_LOGGING_SIGNAL_MERGE_INFO
|
||||
playout_delay(last_decode_k) = playout_delay(last_decode_k) ...
|
||||
+ fread(fid, 1, 'int32');
|
||||
|
||||
case 8 % NETEQ_DELAY_LOGGING_SIGNAL_EXPAND_INFO
|
||||
temp = fread(fid, 1, 'int32');
|
||||
if last_decode_k ~= 0
|
||||
tot_expand = tot_expand + temp / (fs_now / 1000);
|
||||
end
|
||||
|
||||
case 9 % NETEQ_DELAY_LOGGING_SIGNAL_ACCELERATE_INFO
|
||||
temp = fread(fid, 1, 'int32');
|
||||
if last_decode_k ~= 0
|
||||
tot_accelerate = tot_accelerate + temp / (fs_now / 1000);
|
||||
end
|
||||
|
||||
case 10 % NETEQ_DELAY_LOGGING_SIGNAL_PREEMPTIVE_INFO
|
||||
temp = fread(fid, 1, 'int32');
|
||||
if last_decode_k ~= 0
|
||||
tot_preemptive = tot_preemptive + temp / (fs_now / 1000);
|
||||
end
|
||||
|
||||
case 11 % NETEQ_DELAY_LOGGING_SIGNAL_OPTBUF
|
||||
optbuf(last_decode_k) = fread(fid, 1, 'int32');
|
||||
|
||||
case 12 % NETEQ_DELAY_LOGGING_SIGNAL_DECODE_ONE_DESC
|
||||
last_decode_ts = fread(fid, 1, 'uint32');
|
||||
k = ts_ix - 1;
|
||||
|
||||
while (k >= 1) && (rtpts(k) ~= last_decode_ts)
|
||||
% TODO(hlundin): use matlab vector search instead?
|
||||
k = k - 1;
|
||||
end
|
||||
|
||||
if k < 1
|
||||
% packet not received yet
|
||||
k = ts_ix;
|
||||
rtpts(ts_ix) = last_decode_ts;
|
||||
late_packets = late_packets + 1;
|
||||
end
|
||||
|
||||
decode_t(k) = clock;
|
||||
playout_delay(k) = fread(fid, 1, 'uint16') + ...
|
||||
5 * fs_now / 8000; % add overlap length
|
||||
last_decode_k = k;
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
fclose(fid);
|
||||
|
||||
outStruct = struct(...
|
||||
'ts', rtpts, ...
|
||||
'sn', seqno, ...
|
||||
'pt', pt,...
|
||||
'plen', plen,...
|
||||
'arrival', recin_t,...
|
||||
'decode', decode_t,...
|
||||
'fs', fsvec(:),...
|
||||
'fschange_ts', fschange_ts(:),...
|
||||
'playout_delay', playout_delay,...
|
||||
'tot_expand', tot_expand,...
|
||||
'tot_accelerate', tot_accelerate,...
|
||||
'tot_preemptive', tot_preemptive,...
|
||||
'optbuf', optbuf);
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
%
|
||||
% Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
%
|
||||
% Use of this source code is governed by a BSD-style license
|
||||
% that can be found in the LICENSE file in the root of the source
|
||||
% tree. An additional intellectual property rights grant can be found
|
||||
% in the file PATENTS. All contributing project authors may
|
||||
% be found in the AUTHORS file in the root of the source tree.
|
||||
%
|
||||
|
||||
function [delay_struct, delayvalues] = plot_neteq_delay(delayfile, varargin)
|
||||
|
||||
% InfoStruct = plot_neteq_delay(delayfile)
|
||||
% InfoStruct = plot_neteq_delay(delayfile, 'skipdelay', skip_seconds)
|
||||
%
|
||||
% Henrik Lundin, 2006-11-17
|
||||
% Henrik Lundin, 2011-05-17
|
||||
%
|
||||
|
||||
try
|
||||
s = parse_delay_file(delayfile);
|
||||
catch
|
||||
error(lasterr);
|
||||
end
|
||||
|
||||
delayskip=0;
|
||||
noplot=0;
|
||||
arg_ptr=1;
|
||||
delaypoints=[];
|
||||
|
||||
s.sn=unwrap_seqno(s.sn);
|
||||
|
||||
while arg_ptr+1 <= nargin
|
||||
switch lower(varargin{arg_ptr})
|
||||
case {'skipdelay', 'delayskip'}
|
||||
% skip a number of seconds in the beginning when calculating delays
|
||||
delayskip = varargin{arg_ptr+1};
|
||||
arg_ptr = arg_ptr + 2;
|
||||
case 'noplot'
|
||||
noplot=1;
|
||||
arg_ptr = arg_ptr + 1;
|
||||
case {'get_delay', 'getdelay'}
|
||||
% return a vector of delay values for the points in the given vector
|
||||
delaypoints = varargin{arg_ptr+1};
|
||||
arg_ptr = arg_ptr + 2;
|
||||
otherwise
|
||||
warning('Unknown switch %s\n', varargin{arg_ptr});
|
||||
arg_ptr = arg_ptr + 1;
|
||||
end
|
||||
end
|
||||
|
||||
% find lost frames that were covered by one-descriptor decoding
|
||||
one_desc_ix=find(isnan(s.arrival));
|
||||
for k=1:length(one_desc_ix)
|
||||
ix=find(s.ts==max(s.ts(s.ts(one_desc_ix(k))>s.ts)));
|
||||
s.sn(one_desc_ix(k))=s.sn(ix)+1;
|
||||
s.pt(one_desc_ix(k))=s.pt(ix);
|
||||
s.arrival(one_desc_ix(k))=s.arrival(ix)+s.decode(one_desc_ix(k))-s.decode(ix);
|
||||
end
|
||||
|
||||
% remove duplicate received frames that were never decoded (RED codec)
|
||||
if length(unique(s.ts(isfinite(s.ts)))) < length(s.ts(isfinite(s.ts)))
|
||||
ix=find(isfinite(s.decode));
|
||||
s.sn=s.sn(ix);
|
||||
s.ts=s.ts(ix);
|
||||
s.arrival=s.arrival(ix);
|
||||
s.playout_delay=s.playout_delay(ix);
|
||||
s.pt=s.pt(ix);
|
||||
s.optbuf=s.optbuf(ix);
|
||||
plen=plen(ix);
|
||||
s.decode=s.decode(ix);
|
||||
end
|
||||
|
||||
% find non-unique sequence numbers
|
||||
[~,un_ix]=unique(s.sn);
|
||||
nonun_ix=setdiff(1:length(s.sn),un_ix);
|
||||
if ~isempty(nonun_ix)
|
||||
warning('RTP sequence numbers are in error');
|
||||
end
|
||||
|
||||
% sort vectors
|
||||
[s.sn,sort_ix]=sort(s.sn);
|
||||
s.ts=s.ts(sort_ix);
|
||||
s.arrival=s.arrival(sort_ix);
|
||||
s.decode=s.decode(sort_ix);
|
||||
s.playout_delay=s.playout_delay(sort_ix);
|
||||
s.pt=s.pt(sort_ix);
|
||||
|
||||
send_t=s.ts-s.ts(1);
|
||||
if length(s.fs)<1
|
||||
warning('No info about sample rate found in file. Using default 8000.');
|
||||
s.fs(1)=8000;
|
||||
s.fschange_ts(1)=min(s.ts);
|
||||
elseif s.fschange_ts(1)>min(s.ts)
|
||||
s.fschange_ts(1)=min(s.ts);
|
||||
end
|
||||
|
||||
end_ix=length(send_t);
|
||||
for k=length(s.fs):-1:1
|
||||
start_ix=find(s.ts==s.fschange_ts(k));
|
||||
send_t(start_ix:end_ix)=send_t(start_ix:end_ix)/s.fs(k)*1000;
|
||||
s.playout_delay(start_ix:end_ix)=s.playout_delay(start_ix:end_ix)/s.fs(k)*1000;
|
||||
s.optbuf(start_ix:end_ix)=s.optbuf(start_ix:end_ix)/s.fs(k)*1000;
|
||||
end_ix=start_ix-1;
|
||||
end
|
||||
|
||||
tot_time=max(send_t)-min(send_t);
|
||||
|
||||
seq_ix=s.sn-min(s.sn)+1;
|
||||
send_t=send_t+max(min(s.arrival-send_t),0);
|
||||
|
||||
plot_send_t=nan*ones(max(seq_ix),1);
|
||||
plot_send_t(seq_ix)=send_t;
|
||||
plot_nw_delay=nan*ones(max(seq_ix),1);
|
||||
plot_nw_delay(seq_ix)=s.arrival-send_t;
|
||||
|
||||
cng_ix=find(s.pt~=13); % find those packets that are not CNG/SID
|
||||
|
||||
if noplot==0
|
||||
h=plot(plot_send_t/1000,plot_nw_delay);
|
||||
set(h,'color',0.75*[1 1 1]);
|
||||
hold on
|
||||
if any(s.optbuf~=0)
|
||||
peak_ix=find(s.optbuf(cng_ix)<0); % peak mode is labeled with negative values
|
||||
no_peak_ix=find(s.optbuf(cng_ix)>0); %setdiff(1:length(cng_ix),peak_ix);
|
||||
h1=plot(send_t(cng_ix(peak_ix))/1000,...
|
||||
s.arrival(cng_ix(peak_ix))+abs(s.optbuf(cng_ix(peak_ix)))-send_t(cng_ix(peak_ix)),...
|
||||
'r.');
|
||||
h2=plot(send_t(cng_ix(no_peak_ix))/1000,...
|
||||
s.arrival(cng_ix(no_peak_ix))+abs(s.optbuf(cng_ix(no_peak_ix)))-send_t(cng_ix(no_peak_ix)),...
|
||||
'g.');
|
||||
set([h1, h2],'markersize',1)
|
||||
end
|
||||
%h=plot(send_t(seq_ix)/1000,s.decode+s.playout_delay-send_t(seq_ix));
|
||||
h=plot(send_t(cng_ix)/1000,s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix));
|
||||
set(h,'linew',1.5);
|
||||
hold off
|
||||
ax1=axis;
|
||||
axis tight
|
||||
ax2=axis;
|
||||
axis([ax2(1:3) ax1(4)])
|
||||
end
|
||||
|
||||
|
||||
% calculate delays and other parameters
|
||||
|
||||
delayskip_ix = find(send_t-send_t(1)>=delayskip*1000, 1 );
|
||||
|
||||
use_ix = intersect(cng_ix,... % use those that are not CNG/SID frames...
|
||||
intersect(find(isfinite(s.decode)),... % ... that did arrive ...
|
||||
(delayskip_ix:length(s.decode))')); % ... and are sent after delayskip seconds
|
||||
|
||||
mean_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-send_t(use_ix));
|
||||
neteq_delay = mean(s.decode(use_ix)+s.playout_delay(use_ix)-s.arrival(use_ix));
|
||||
|
||||
Npack=max(s.sn(delayskip_ix:end))-min(s.sn(delayskip_ix:end))+1;
|
||||
nw_lossrate=(Npack-length(s.sn(delayskip_ix:end)))/Npack;
|
||||
neteq_lossrate=(length(s.sn(delayskip_ix:end))-length(use_ix))/Npack;
|
||||
|
||||
delay_struct=struct('mean_delay',mean_delay,'neteq_delay',neteq_delay,...
|
||||
'nw_lossrate',nw_lossrate,'neteq_lossrate',neteq_lossrate,...
|
||||
'tot_expand',round(s.tot_expand),'tot_accelerate',round(s.tot_accelerate),...
|
||||
'tot_preemptive',round(s.tot_preemptive),'tot_time',tot_time,...
|
||||
'filename',delayfile,'units','ms','fs',unique(s.fs));
|
||||
|
||||
if not(isempty(delaypoints))
|
||||
delayvalues=interp1(send_t(cng_ix),...
|
||||
s.decode(cng_ix)+s.playout_delay(cng_ix)-send_t(cng_ix),...
|
||||
delaypoints,'nearest',NaN);
|
||||
else
|
||||
delayvalues=[];
|
||||
end
|
||||
|
||||
|
||||
|
||||
% SUBFUNCTIONS %
|
||||
|
||||
function y=unwrap_seqno(x)
|
||||
|
||||
jumps=find(abs((diff(x)-1))>65000);
|
||||
|
||||
while ~isempty(jumps)
|
||||
n=jumps(1);
|
||||
if x(n+1)-x(n) < 0
|
||||
% negative jump
|
||||
x(n+1:end)=x(n+1:end)+65536;
|
||||
else
|
||||
% positive jump
|
||||
x(n+1:end)=x(n+1:end)-65536;
|
||||
end
|
||||
|
||||
jumps=find(abs((diff(x(n+1:end))-1))>65000);
|
||||
end
|
||||
|
||||
y=x;
|
||||
|
||||
return;
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/audio_coding/neteq/test/neteq_decoding_test.h"
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
#include "api/rtp_headers.h"
|
||||
#include "modules/audio_coding/neteq/default_neteq_factory.h"
|
||||
#include "modules/audio_coding/neteq/test/result_sink.h"
|
||||
#include "rtc_base/strings/string_builder.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
|
||||
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h"
|
||||
#else
|
||||
#include "modules/audio_coding/neteq/neteq_unittest.pb.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
|
||||
void LoadDecoders(webrtc::NetEq* neteq) {
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(0, SdpAudioFormat("pcmu", 8000, 1)));
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(8, SdpAudioFormat("pcma", 8000, 1)));
|
||||
#ifdef WEBRTC_CODEC_ILBC
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(102, SdpAudioFormat("ilbc", 8000, 1)));
|
||||
#endif
|
||||
#if defined(WEBRTC_CODEC_ISAC) || defined(WEBRTC_CODEC_ISACFX)
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(103, SdpAudioFormat("isac", 16000, 1)));
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_ISAC
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(104, SdpAudioFormat("isac", 32000, 1)));
|
||||
#endif
|
||||
#ifdef WEBRTC_CODEC_OPUS
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(
|
||||
111, SdpAudioFormat("opus", 48000, 2, {{"stereo", "0"}})));
|
||||
#endif
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(93, SdpAudioFormat("L16", 8000, 1)));
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(94, SdpAudioFormat("L16", 16000, 1)));
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(95, SdpAudioFormat("L16", 32000, 1)));
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(13, SdpAudioFormat("cn", 8000, 1)));
|
||||
ASSERT_EQ(true,
|
||||
neteq->RegisterPayloadType(98, SdpAudioFormat("cn", 16000, 1)));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const int NetEqDecodingTest::kTimeStepMs;
|
||||
const size_t NetEqDecodingTest::kBlockSize8kHz;
|
||||
const size_t NetEqDecodingTest::kBlockSize16kHz;
|
||||
const size_t NetEqDecodingTest::kBlockSize32kHz;
|
||||
const int NetEqDecodingTest::kInitSampleRateHz;
|
||||
|
||||
NetEqDecodingTest::NetEqDecodingTest()
|
||||
: clock_(0),
|
||||
config_(),
|
||||
output_sample_rate_(kInitSampleRateHz),
|
||||
algorithmic_delay_ms_(0) {
|
||||
config_.sample_rate_hz = kInitSampleRateHz;
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::SetUp() {
|
||||
auto decoder_factory = CreateBuiltinAudioDecoderFactory();
|
||||
neteq_ = DefaultNetEqFactory().CreateNetEq(config_, decoder_factory, &clock_);
|
||||
NetEqNetworkStatistics stat;
|
||||
ASSERT_EQ(0, neteq_->NetworkStatistics(&stat));
|
||||
algorithmic_delay_ms_ = stat.current_buffer_size_ms;
|
||||
ASSERT_TRUE(neteq_);
|
||||
LoadDecoders(neteq_.get());
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::TearDown() {}
|
||||
|
||||
void NetEqDecodingTest::OpenInputFile(absl::string_view rtp_file) {
|
||||
rtp_source_.reset(test::RtpFileSource::Create(rtp_file));
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::Process() {
|
||||
// Check if time to receive.
|
||||
while (packet_ && clock_.TimeInMilliseconds() >= packet_->time_ms()) {
|
||||
if (packet_->payload_length_bytes() > 0) {
|
||||
#ifndef WEBRTC_CODEC_ISAC
|
||||
// Ignore payload type 104 (iSAC-swb) if ISAC is not supported.
|
||||
if (packet_->header().payloadType != 104)
|
||||
#endif
|
||||
ASSERT_EQ(
|
||||
0, neteq_->InsertPacket(
|
||||
packet_->header(),
|
||||
rtc::ArrayView<const uint8_t>(
|
||||
packet_->payload(), packet_->payload_length_bytes())));
|
||||
}
|
||||
// Get next packet.
|
||||
packet_ = rtp_source_->NextPacket();
|
||||
}
|
||||
|
||||
// Get audio from NetEq.
|
||||
bool muted;
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_FALSE(muted);
|
||||
ASSERT_TRUE((out_frame_.samples_per_channel_ == kBlockSize8kHz) ||
|
||||
(out_frame_.samples_per_channel_ == kBlockSize16kHz) ||
|
||||
(out_frame_.samples_per_channel_ == kBlockSize32kHz) ||
|
||||
(out_frame_.samples_per_channel_ == kBlockSize48kHz));
|
||||
output_sample_rate_ = out_frame_.sample_rate_hz_;
|
||||
EXPECT_EQ(output_sample_rate_, neteq_->last_output_sample_rate_hz());
|
||||
|
||||
// Increase time.
|
||||
clock_.AdvanceTimeMilliseconds(kTimeStepMs);
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::DecodeAndCompare(
|
||||
absl::string_view rtp_file,
|
||||
absl::string_view output_checksum,
|
||||
absl::string_view network_stats_checksum,
|
||||
bool gen_ref) {
|
||||
OpenInputFile(rtp_file);
|
||||
|
||||
std::string ref_out_file =
|
||||
gen_ref ? webrtc::test::OutputPath() + "neteq_universal_ref.pcm" : "";
|
||||
ResultSink output(ref_out_file);
|
||||
|
||||
std::string stat_out_file =
|
||||
gen_ref ? webrtc::test::OutputPath() + "neteq_network_stats.dat" : "";
|
||||
ResultSink network_stats(stat_out_file);
|
||||
|
||||
packet_ = rtp_source_->NextPacket();
|
||||
int i = 0;
|
||||
uint64_t last_concealed_samples = 0;
|
||||
uint64_t last_total_samples_received = 0;
|
||||
while (packet_) {
|
||||
rtc::StringBuilder ss;
|
||||
ss << "Lap number " << i++ << " in DecodeAndCompare while loop";
|
||||
SCOPED_TRACE(ss.str()); // Print out the parameter values on failure.
|
||||
ASSERT_NO_FATAL_FAILURE(Process());
|
||||
ASSERT_NO_FATAL_FAILURE(
|
||||
output.AddResult(out_frame_.data(), out_frame_.samples_per_channel_));
|
||||
|
||||
// Query the network statistics API once per second
|
||||
if (clock_.TimeInMilliseconds() % 1000 == 0) {
|
||||
// Process NetworkStatistics.
|
||||
NetEqNetworkStatistics current_network_stats;
|
||||
ASSERT_EQ(0, neteq_->NetworkStatistics(¤t_network_stats));
|
||||
ASSERT_NO_FATAL_FAILURE(network_stats.AddResult(current_network_stats));
|
||||
|
||||
// Verify that liftime stats and network stats report similar loss
|
||||
// concealment rates.
|
||||
auto lifetime_stats = neteq_->GetLifetimeStatistics();
|
||||
const uint64_t delta_concealed_samples =
|
||||
lifetime_stats.concealed_samples - last_concealed_samples;
|
||||
last_concealed_samples = lifetime_stats.concealed_samples;
|
||||
const uint64_t delta_total_samples_received =
|
||||
lifetime_stats.total_samples_received - last_total_samples_received;
|
||||
last_total_samples_received = lifetime_stats.total_samples_received;
|
||||
// The tolerance is 1% but expressed in Q14.
|
||||
EXPECT_NEAR(
|
||||
(delta_concealed_samples << 14) / delta_total_samples_received,
|
||||
current_network_stats.expand_rate, (2 << 14) / 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
SCOPED_TRACE("Check output audio.");
|
||||
output.VerifyChecksum(output_checksum);
|
||||
SCOPED_TRACE("Check network stats.");
|
||||
network_stats.VerifyChecksum(network_stats_checksum);
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::PopulateRtpInfo(int frame_index,
|
||||
int timestamp,
|
||||
RTPHeader* rtp_info) {
|
||||
rtp_info->sequenceNumber = frame_index;
|
||||
rtp_info->timestamp = timestamp;
|
||||
rtp_info->ssrc = 0x1234; // Just an arbitrary SSRC.
|
||||
rtp_info->payloadType = 94; // PCM16b WB codec.
|
||||
rtp_info->markerBit = false;
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::PopulateCng(int frame_index,
|
||||
int timestamp,
|
||||
RTPHeader* rtp_info,
|
||||
uint8_t* payload,
|
||||
size_t* payload_len) {
|
||||
rtp_info->sequenceNumber = frame_index;
|
||||
rtp_info->timestamp = timestamp;
|
||||
rtp_info->ssrc = 0x1234; // Just an arbitrary SSRC.
|
||||
rtp_info->payloadType = 98; // WB CNG.
|
||||
rtp_info->markerBit = false;
|
||||
payload[0] = 64; // Noise level -64 dBov, quite arbitrarily chosen.
|
||||
*payload_len = 1; // Only noise level, no spectral parameters.
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::WrapTest(uint16_t start_seq_no,
|
||||
uint32_t start_timestamp,
|
||||
const std::set<uint16_t>& drop_seq_numbers,
|
||||
bool expect_seq_no_wrap,
|
||||
bool expect_timestamp_wrap) {
|
||||
uint16_t seq_no = start_seq_no;
|
||||
uint32_t timestamp = start_timestamp;
|
||||
const int kBlocksPerFrame = 3; // Number of 10 ms blocks per frame.
|
||||
const int kFrameSizeMs = kBlocksPerFrame * kTimeStepMs;
|
||||
const int kSamples = kBlockSize16kHz * kBlocksPerFrame;
|
||||
const size_t kPayloadBytes = kSamples * sizeof(int16_t);
|
||||
double next_input_time_ms = 0.0;
|
||||
|
||||
// Insert speech for 2 seconds.
|
||||
const int kSpeechDurationMs = 2000;
|
||||
uint16_t last_seq_no;
|
||||
uint32_t last_timestamp;
|
||||
bool timestamp_wrapped = false;
|
||||
bool seq_no_wrapped = false;
|
||||
for (double t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
|
||||
// Each turn in this for loop is 10 ms.
|
||||
while (next_input_time_ms <= t_ms) {
|
||||
// Insert one 30 ms speech frame.
|
||||
uint8_t payload[kPayloadBytes] = {0};
|
||||
RTPHeader rtp_info;
|
||||
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
|
||||
if (drop_seq_numbers.find(seq_no) == drop_seq_numbers.end()) {
|
||||
// This sequence number was not in the set to drop. Insert it.
|
||||
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload));
|
||||
}
|
||||
NetEqNetworkStatistics network_stats;
|
||||
ASSERT_EQ(0, neteq_->NetworkStatistics(&network_stats));
|
||||
|
||||
EXPECT_LE(network_stats.preferred_buffer_size_ms, 80);
|
||||
EXPECT_LE(network_stats.current_buffer_size_ms,
|
||||
80 + algorithmic_delay_ms_);
|
||||
last_seq_no = seq_no;
|
||||
last_timestamp = timestamp;
|
||||
|
||||
++seq_no;
|
||||
timestamp += kSamples;
|
||||
next_input_time_ms += static_cast<double>(kFrameSizeMs);
|
||||
|
||||
seq_no_wrapped |= seq_no < last_seq_no;
|
||||
timestamp_wrapped |= timestamp < last_timestamp;
|
||||
}
|
||||
// Pull out data once.
|
||||
AudioFrame output;
|
||||
bool muted;
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&output, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, output.samples_per_channel_);
|
||||
ASSERT_EQ(1u, output.num_channels_);
|
||||
|
||||
// Expect delay (in samples) to be less than 2 packets.
|
||||
absl::optional<uint32_t> playout_timestamp = neteq_->GetPlayoutTimestamp();
|
||||
ASSERT_TRUE(playout_timestamp);
|
||||
EXPECT_LE(timestamp - *playout_timestamp,
|
||||
static_cast<uint32_t>(kSamples * 2));
|
||||
}
|
||||
// Make sure we have actually tested wrap-around.
|
||||
ASSERT_EQ(expect_seq_no_wrap, seq_no_wrapped);
|
||||
ASSERT_EQ(expect_timestamp_wrap, timestamp_wrapped);
|
||||
}
|
||||
|
||||
void NetEqDecodingTest::LongCngWithClockDrift(double drift_factor,
|
||||
double network_freeze_ms,
|
||||
bool pull_audio_during_freeze,
|
||||
int delay_tolerance_ms,
|
||||
int max_time_to_speech_ms) {
|
||||
uint16_t seq_no = 0;
|
||||
uint32_t timestamp = 0;
|
||||
const int kFrameSizeMs = 30;
|
||||
const size_t kSamples = kFrameSizeMs * 16;
|
||||
const size_t kPayloadBytes = kSamples * 2;
|
||||
double next_input_time_ms = 0.0;
|
||||
double t_ms;
|
||||
bool muted;
|
||||
|
||||
// Insert speech for 5 seconds.
|
||||
const int kSpeechDurationMs = 5000;
|
||||
for (t_ms = 0; t_ms < kSpeechDurationMs; t_ms += 10) {
|
||||
// Each turn in this for loop is 10 ms.
|
||||
while (next_input_time_ms <= t_ms) {
|
||||
// Insert one 30 ms speech frame.
|
||||
uint8_t payload[kPayloadBytes] = {0};
|
||||
RTPHeader rtp_info;
|
||||
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
|
||||
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload));
|
||||
++seq_no;
|
||||
timestamp += kSamples;
|
||||
next_input_time_ms += static_cast<double>(kFrameSizeMs) * drift_factor;
|
||||
}
|
||||
// Pull out data once.
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, out_frame_.samples_per_channel_);
|
||||
}
|
||||
|
||||
EXPECT_EQ(AudioFrame::kNormalSpeech, out_frame_.speech_type_);
|
||||
absl::optional<uint32_t> playout_timestamp = neteq_->GetPlayoutTimestamp();
|
||||
ASSERT_TRUE(playout_timestamp);
|
||||
int32_t delay_before = timestamp - *playout_timestamp;
|
||||
|
||||
// Insert CNG for 1 minute (= 60000 ms).
|
||||
const int kCngPeriodMs = 100;
|
||||
const int kCngPeriodSamples = kCngPeriodMs * 16; // Period in 16 kHz samples.
|
||||
const int kCngDurationMs = 60000;
|
||||
for (; t_ms < kSpeechDurationMs + kCngDurationMs; t_ms += 10) {
|
||||
// Each turn in this for loop is 10 ms.
|
||||
while (next_input_time_ms <= t_ms) {
|
||||
// Insert one CNG frame each 100 ms.
|
||||
uint8_t payload[kPayloadBytes];
|
||||
size_t payload_len;
|
||||
RTPHeader rtp_info;
|
||||
PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len);
|
||||
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, rtc::ArrayView<const uint8_t>(
|
||||
payload, payload_len)));
|
||||
++seq_no;
|
||||
timestamp += kCngPeriodSamples;
|
||||
next_input_time_ms += static_cast<double>(kCngPeriodMs) * drift_factor;
|
||||
}
|
||||
// Pull out data once.
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, out_frame_.samples_per_channel_);
|
||||
}
|
||||
|
||||
EXPECT_EQ(AudioFrame::kCNG, out_frame_.speech_type_);
|
||||
|
||||
if (network_freeze_ms > 0) {
|
||||
// First keep pulling audio for `network_freeze_ms` without inserting
|
||||
// any data, then insert CNG data corresponding to `network_freeze_ms`
|
||||
// without pulling any output audio.
|
||||
const double loop_end_time = t_ms + network_freeze_ms;
|
||||
for (; t_ms < loop_end_time; t_ms += 10) {
|
||||
// Pull out data once.
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, out_frame_.samples_per_channel_);
|
||||
EXPECT_EQ(AudioFrame::kCNG, out_frame_.speech_type_);
|
||||
}
|
||||
bool pull_once = pull_audio_during_freeze;
|
||||
// If `pull_once` is true, GetAudio will be called once half-way through
|
||||
// the network recovery period.
|
||||
double pull_time_ms = (t_ms + next_input_time_ms) / 2;
|
||||
while (next_input_time_ms <= t_ms) {
|
||||
if (pull_once && next_input_time_ms >= pull_time_ms) {
|
||||
pull_once = false;
|
||||
// Pull out data once.
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, out_frame_.samples_per_channel_);
|
||||
EXPECT_EQ(AudioFrame::kCNG, out_frame_.speech_type_);
|
||||
t_ms += 10;
|
||||
}
|
||||
// Insert one CNG frame each 100 ms.
|
||||
uint8_t payload[kPayloadBytes];
|
||||
size_t payload_len;
|
||||
RTPHeader rtp_info;
|
||||
PopulateCng(seq_no, timestamp, &rtp_info, payload, &payload_len);
|
||||
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, rtc::ArrayView<const uint8_t>(
|
||||
payload, payload_len)));
|
||||
++seq_no;
|
||||
timestamp += kCngPeriodSamples;
|
||||
next_input_time_ms += kCngPeriodMs * drift_factor;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert speech again until output type is speech.
|
||||
double speech_restart_time_ms = t_ms;
|
||||
while (out_frame_.speech_type_ != AudioFrame::kNormalSpeech) {
|
||||
// Each turn in this for loop is 10 ms.
|
||||
while (next_input_time_ms <= t_ms) {
|
||||
// Insert one 30 ms speech frame.
|
||||
uint8_t payload[kPayloadBytes] = {0};
|
||||
RTPHeader rtp_info;
|
||||
PopulateRtpInfo(seq_no, timestamp, &rtp_info);
|
||||
ASSERT_EQ(0, neteq_->InsertPacket(rtp_info, payload));
|
||||
++seq_no;
|
||||
timestamp += kSamples;
|
||||
next_input_time_ms += kFrameSizeMs * drift_factor;
|
||||
}
|
||||
// Pull out data once.
|
||||
ASSERT_EQ(0, neteq_->GetAudio(&out_frame_, &muted));
|
||||
ASSERT_EQ(kBlockSize16kHz, out_frame_.samples_per_channel_);
|
||||
// Increase clock.
|
||||
t_ms += 10;
|
||||
}
|
||||
|
||||
// Check that the speech starts again within reasonable time.
|
||||
double time_until_speech_returns_ms = t_ms - speech_restart_time_ms;
|
||||
EXPECT_LT(time_until_speech_returns_ms, max_time_to_speech_ms);
|
||||
playout_timestamp = neteq_->GetPlayoutTimestamp();
|
||||
ASSERT_TRUE(playout_timestamp);
|
||||
int32_t delay_after = timestamp - *playout_timestamp;
|
||||
// Compare delay before and after, and make sure it differs less than 20 ms.
|
||||
EXPECT_LE(delay_after, delay_before + delay_tolerance_ms * 16);
|
||||
EXPECT_GE(delay_after, delay_before - delay_tolerance_ms * 16);
|
||||
}
|
||||
|
||||
void NetEqDecodingTestTwoInstances::SetUp() {
|
||||
NetEqDecodingTest::SetUp();
|
||||
config2_ = config_;
|
||||
}
|
||||
|
||||
void NetEqDecodingTestTwoInstances::CreateSecondInstance() {
|
||||
auto decoder_factory = CreateBuiltinAudioDecoderFactory();
|
||||
neteq2_ =
|
||||
DefaultNetEqFactory().CreateNetEq(config2_, decoder_factory, &clock_);
|
||||
ASSERT_TRUE(neteq2_);
|
||||
LoadDecoders(neteq2_.get());
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_AUDIO_CODING_NETEQ_TEST_NETEQ_DECODING_TEST_H_
|
||||
#define MODULES_AUDIO_CODING_NETEQ_TEST_NETEQ_DECODING_TEST_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "api/neteq/neteq.h"
|
||||
#include "api/rtp_headers.h"
|
||||
#include "modules/audio_coding/neteq/tools/packet.h"
|
||||
#include "modules/audio_coding/neteq/tools/rtp_file_source.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class NetEqDecodingTest : public ::testing::Test {
|
||||
protected:
|
||||
// NetEQ must be polled for data once every 10 ms.
|
||||
// Thus, none of the constants below can be changed.
|
||||
static constexpr int kTimeStepMs = 10;
|
||||
static constexpr size_t kBlockSize8kHz = kTimeStepMs * 8;
|
||||
static constexpr size_t kBlockSize16kHz = kTimeStepMs * 16;
|
||||
static constexpr size_t kBlockSize32kHz = kTimeStepMs * 32;
|
||||
static constexpr size_t kBlockSize48kHz = kTimeStepMs * 48;
|
||||
static constexpr int kInitSampleRateHz = 8000;
|
||||
|
||||
NetEqDecodingTest();
|
||||
virtual void SetUp();
|
||||
virtual void TearDown();
|
||||
void OpenInputFile(absl::string_view rtp_file);
|
||||
void Process();
|
||||
|
||||
void DecodeAndCompare(absl::string_view rtp_file,
|
||||
absl::string_view output_checksum,
|
||||
absl::string_view network_stats_checksum,
|
||||
bool gen_ref);
|
||||
|
||||
static void PopulateRtpInfo(int frame_index,
|
||||
int timestamp,
|
||||
RTPHeader* rtp_info);
|
||||
static void PopulateCng(int frame_index,
|
||||
int timestamp,
|
||||
RTPHeader* rtp_info,
|
||||
uint8_t* payload,
|
||||
size_t* payload_len);
|
||||
|
||||
void WrapTest(uint16_t start_seq_no,
|
||||
uint32_t start_timestamp,
|
||||
const std::set<uint16_t>& drop_seq_numbers,
|
||||
bool expect_seq_no_wrap,
|
||||
bool expect_timestamp_wrap);
|
||||
|
||||
void LongCngWithClockDrift(double drift_factor,
|
||||
double network_freeze_ms,
|
||||
bool pull_audio_during_freeze,
|
||||
int delay_tolerance_ms,
|
||||
int max_time_to_speech_ms);
|
||||
|
||||
SimulatedClock clock_;
|
||||
std::unique_ptr<NetEq> neteq_;
|
||||
NetEq::Config config_;
|
||||
std::unique_ptr<test::RtpFileSource> rtp_source_;
|
||||
std::unique_ptr<test::Packet> packet_;
|
||||
AudioFrame out_frame_;
|
||||
int output_sample_rate_;
|
||||
int algorithmic_delay_ms_;
|
||||
};
|
||||
|
||||
class NetEqDecodingTestTwoInstances : public NetEqDecodingTest {
|
||||
public:
|
||||
NetEqDecodingTestTwoInstances() : NetEqDecodingTest() {}
|
||||
|
||||
void SetUp() override;
|
||||
|
||||
void CreateSecondInstance();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<NetEq> neteq2_;
|
||||
NetEq::Config config2_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_AUDIO_CODING_NETEQ_TEST_NETEQ_DECODING_TEST_H_
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_quality_test.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
|
||||
ABSL_FLAG(int, frame_size_ms, 20, "Codec frame size (milliseconds).");
|
||||
|
||||
using ::testing::InitGoogleTest;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
static const int kInputSampleRateKhz = 8;
|
||||
static const int kOutputSampleRateKhz = 8;
|
||||
} // namespace
|
||||
|
||||
class NetEqIlbcQualityTest : public NetEqQualityTest {
|
||||
protected:
|
||||
NetEqIlbcQualityTest()
|
||||
: NetEqQualityTest(absl::GetFlag(FLAGS_frame_size_ms),
|
||||
kInputSampleRateKhz,
|
||||
kOutputSampleRateKhz,
|
||||
SdpAudioFormat("ilbc", 8000, 1)) {
|
||||
// Flag validation
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_frame_size_ms) == 20 ||
|
||||
absl::GetFlag(FLAGS_frame_size_ms) == 30 ||
|
||||
absl::GetFlag(FLAGS_frame_size_ms) == 40 ||
|
||||
absl::GetFlag(FLAGS_frame_size_ms) == 60)
|
||||
<< "Invalid frame size, should be 20, 30, 40, or 60 ms.";
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_EQ(1u, channels_) << "iLBC supports only mono audio.";
|
||||
AudioEncoderIlbcConfig config;
|
||||
config.frame_size_ms = absl::GetFlag(FLAGS_frame_size_ms);
|
||||
encoder_.reset(new AudioEncoderIlbcImpl(config, 102));
|
||||
NetEqQualityTest::SetUp();
|
||||
}
|
||||
|
||||
int EncodeBlock(int16_t* in_data,
|
||||
size_t block_size_samples,
|
||||
rtc::Buffer* payload,
|
||||
size_t max_bytes) override {
|
||||
const size_t kFrameSizeSamples = 80; // Samples per 10 ms.
|
||||
size_t encoded_samples = 0;
|
||||
uint32_t dummy_timestamp = 0;
|
||||
AudioEncoder::EncodedInfo info;
|
||||
do {
|
||||
info = encoder_->Encode(dummy_timestamp,
|
||||
rtc::ArrayView<const int16_t>(
|
||||
in_data + encoded_samples, kFrameSizeSamples),
|
||||
payload);
|
||||
encoded_samples += kFrameSizeSamples;
|
||||
} while (info.encoded_bytes == 0);
|
||||
return rtc::checked_cast<int>(info.encoded_bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioEncoderIlbcImpl> encoder_;
|
||||
};
|
||||
|
||||
TEST_F(NetEqIlbcQualityTest, Test) {
|
||||
Simulate();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "modules/audio_coding/codecs/opus/opus_inst.h"
|
||||
#include "modules/audio_coding/codecs/opus/opus_interface.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_quality_test.h"
|
||||
|
||||
ABSL_FLAG(int, bit_rate_kbps, 32, "Target bit rate (kbps).");
|
||||
|
||||
ABSL_FLAG(int,
|
||||
complexity,
|
||||
10,
|
||||
"Complexity: 0 ~ 10 -- defined as in Opus"
|
||||
"specification.");
|
||||
|
||||
ABSL_FLAG(int, maxplaybackrate, 48000, "Maximum playback rate (Hz).");
|
||||
|
||||
ABSL_FLAG(int, application, 0, "Application mode: 0 -- VOIP, 1 -- Audio.");
|
||||
|
||||
ABSL_FLAG(int, reported_loss_rate, 10, "Reported percentile of packet loss.");
|
||||
|
||||
ABSL_FLAG(bool, fec, false, "Enable FEC for encoding (-nofec to disable).");
|
||||
|
||||
ABSL_FLAG(bool, dtx, false, "Enable DTX for encoding (-nodtx to disable).");
|
||||
|
||||
ABSL_FLAG(int, sub_packets, 1, "Number of sub packets to repacketize.");
|
||||
|
||||
using ::testing::InitGoogleTest;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
static const int kOpusBlockDurationMs = 20;
|
||||
static const int kOpusSamplingKhz = 48;
|
||||
} // namespace
|
||||
|
||||
class NetEqOpusQualityTest : public NetEqQualityTest {
|
||||
protected:
|
||||
NetEqOpusQualityTest();
|
||||
void SetUp() override;
|
||||
void TearDown() override;
|
||||
int EncodeBlock(int16_t* in_data,
|
||||
size_t block_size_samples,
|
||||
rtc::Buffer* payload,
|
||||
size_t max_bytes) override;
|
||||
|
||||
private:
|
||||
WebRtcOpusEncInst* opus_encoder_;
|
||||
OpusRepacketizer* repacketizer_;
|
||||
size_t sub_block_size_samples_;
|
||||
int bit_rate_kbps_;
|
||||
bool fec_;
|
||||
bool dtx_;
|
||||
int complexity_;
|
||||
int maxplaybackrate_;
|
||||
int target_loss_rate_;
|
||||
int sub_packets_;
|
||||
int application_;
|
||||
};
|
||||
|
||||
NetEqOpusQualityTest::NetEqOpusQualityTest()
|
||||
: NetEqQualityTest(kOpusBlockDurationMs * absl::GetFlag(FLAGS_sub_packets),
|
||||
kOpusSamplingKhz,
|
||||
kOpusSamplingKhz,
|
||||
SdpAudioFormat("opus", 48000, 2)),
|
||||
opus_encoder_(NULL),
|
||||
repacketizer_(NULL),
|
||||
sub_block_size_samples_(
|
||||
static_cast<size_t>(kOpusBlockDurationMs * kOpusSamplingKhz)),
|
||||
bit_rate_kbps_(absl::GetFlag(FLAGS_bit_rate_kbps)),
|
||||
fec_(absl::GetFlag(FLAGS_fec)),
|
||||
dtx_(absl::GetFlag(FLAGS_dtx)),
|
||||
complexity_(absl::GetFlag(FLAGS_complexity)),
|
||||
maxplaybackrate_(absl::GetFlag(FLAGS_maxplaybackrate)),
|
||||
target_loss_rate_(absl::GetFlag(FLAGS_reported_loss_rate)),
|
||||
sub_packets_(absl::GetFlag(FLAGS_sub_packets)) {
|
||||
// Flag validation
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_bit_rate_kbps) >= 6 &&
|
||||
absl::GetFlag(FLAGS_bit_rate_kbps) <= 510)
|
||||
<< "Invalid bit rate, should be between 6 and 510 kbps.";
|
||||
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_complexity) >= -1 &&
|
||||
absl::GetFlag(FLAGS_complexity) <= 10)
|
||||
<< "Invalid complexity setting, should be between 0 and 10.";
|
||||
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_application) == 0 ||
|
||||
absl::GetFlag(FLAGS_application) == 1)
|
||||
<< "Invalid application mode, should be 0 or 1.";
|
||||
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_reported_loss_rate) >= 0 &&
|
||||
absl::GetFlag(FLAGS_reported_loss_rate) <= 100)
|
||||
<< "Invalid packet loss percentile, should be between 0 and 100.";
|
||||
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_sub_packets) >= 1 &&
|
||||
absl::GetFlag(FLAGS_sub_packets) <= 3)
|
||||
<< "Invalid number of sub packets, should be between 1 and 3.";
|
||||
|
||||
// Redefine decoder type if input is stereo.
|
||||
if (channels_ > 1) {
|
||||
audio_format_ =
|
||||
SdpAudioFormat("opus", 48000, 2, CodecParameterMap{{"stereo", "1"}});
|
||||
}
|
||||
application_ = absl::GetFlag(FLAGS_application);
|
||||
}
|
||||
|
||||
void NetEqOpusQualityTest::SetUp() {
|
||||
// Create encoder memory.
|
||||
WebRtcOpus_EncoderCreate(&opus_encoder_, channels_, application_, 48000);
|
||||
ASSERT_TRUE(opus_encoder_);
|
||||
|
||||
// Create repacketizer.
|
||||
repacketizer_ = opus_repacketizer_create();
|
||||
ASSERT_TRUE(repacketizer_);
|
||||
|
||||
// Set bitrate.
|
||||
EXPECT_EQ(0, WebRtcOpus_SetBitRate(opus_encoder_, bit_rate_kbps_ * 1000));
|
||||
if (fec_) {
|
||||
EXPECT_EQ(0, WebRtcOpus_EnableFec(opus_encoder_));
|
||||
}
|
||||
if (dtx_) {
|
||||
EXPECT_EQ(0, WebRtcOpus_EnableDtx(opus_encoder_));
|
||||
}
|
||||
EXPECT_EQ(0, WebRtcOpus_SetComplexity(opus_encoder_, complexity_));
|
||||
EXPECT_EQ(0, WebRtcOpus_SetMaxPlaybackRate(opus_encoder_, maxplaybackrate_));
|
||||
EXPECT_EQ(0, WebRtcOpus_SetPacketLossRate(opus_encoder_, target_loss_rate_));
|
||||
NetEqQualityTest::SetUp();
|
||||
}
|
||||
|
||||
void NetEqOpusQualityTest::TearDown() {
|
||||
// Free memory.
|
||||
EXPECT_EQ(0, WebRtcOpus_EncoderFree(opus_encoder_));
|
||||
opus_repacketizer_destroy(repacketizer_);
|
||||
NetEqQualityTest::TearDown();
|
||||
}
|
||||
|
||||
int NetEqOpusQualityTest::EncodeBlock(int16_t* in_data,
|
||||
size_t block_size_samples,
|
||||
rtc::Buffer* payload,
|
||||
size_t max_bytes) {
|
||||
EXPECT_EQ(block_size_samples, sub_block_size_samples_ * sub_packets_);
|
||||
int16_t* pointer = in_data;
|
||||
int value;
|
||||
opus_repacketizer_init(repacketizer_);
|
||||
for (int idx = 0; idx < sub_packets_; idx++) {
|
||||
payload->AppendData(max_bytes, [&](rtc::ArrayView<uint8_t> payload) {
|
||||
value = WebRtcOpus_Encode(opus_encoder_, pointer, sub_block_size_samples_,
|
||||
max_bytes, payload.data());
|
||||
|
||||
Log() << "Encoded a frame with Opus mode "
|
||||
<< (value == 0 ? 0 : payload[0] >> 3) << std::endl;
|
||||
|
||||
return (value >= 0) ? static_cast<size_t>(value) : 0;
|
||||
});
|
||||
|
||||
if (OPUS_OK !=
|
||||
opus_repacketizer_cat(repacketizer_, payload->data(), value)) {
|
||||
opus_repacketizer_init(repacketizer_);
|
||||
// If the repacketization fails, we discard this frame.
|
||||
return 0;
|
||||
}
|
||||
pointer += sub_block_size_samples_ * channels_;
|
||||
}
|
||||
value = opus_repacketizer_out(repacketizer_, payload->data(),
|
||||
static_cast<opus_int32>(max_bytes));
|
||||
EXPECT_GE(value, 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
TEST_F(NetEqOpusQualityTest, Test) {
|
||||
Simulate();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "modules/audio_coding/codecs/pcm16b/audio_encoder_pcm16b.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_quality_test.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
|
||||
ABSL_FLAG(int, frame_size_ms, 20, "Codec frame size (milliseconds).");
|
||||
|
||||
using ::testing::InitGoogleTest;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
static const int kInputSampleRateKhz = 48;
|
||||
static const int kOutputSampleRateKhz = 48;
|
||||
} // namespace
|
||||
|
||||
class NetEqPcm16bQualityTest : public NetEqQualityTest {
|
||||
protected:
|
||||
NetEqPcm16bQualityTest()
|
||||
: NetEqQualityTest(absl::GetFlag(FLAGS_frame_size_ms),
|
||||
kInputSampleRateKhz,
|
||||
kOutputSampleRateKhz,
|
||||
SdpAudioFormat("l16", 48000, 1)) {
|
||||
// Flag validation
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_frame_size_ms) >= 10 &&
|
||||
absl::GetFlag(FLAGS_frame_size_ms) <= 60 &&
|
||||
(absl::GetFlag(FLAGS_frame_size_ms) % 10) == 0)
|
||||
<< "Invalid frame size, should be 10, 20, ..., 60 ms.";
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
AudioEncoderPcm16B::Config config;
|
||||
config.frame_size_ms = absl::GetFlag(FLAGS_frame_size_ms);
|
||||
config.sample_rate_hz = 48000;
|
||||
config.num_channels = channels_;
|
||||
encoder_.reset(new AudioEncoderPcm16B(config));
|
||||
NetEqQualityTest::SetUp();
|
||||
}
|
||||
|
||||
int EncodeBlock(int16_t* in_data,
|
||||
size_t block_size_samples,
|
||||
rtc::Buffer* payload,
|
||||
size_t max_bytes) override {
|
||||
const size_t kFrameSizeSamples = 480; // Samples per 10 ms.
|
||||
size_t encoded_samples = 0;
|
||||
uint32_t dummy_timestamp = 0;
|
||||
AudioEncoder::EncodedInfo info;
|
||||
do {
|
||||
info = encoder_->Encode(dummy_timestamp,
|
||||
rtc::ArrayView<const int16_t>(
|
||||
in_data + encoded_samples, kFrameSizeSamples),
|
||||
payload);
|
||||
encoded_samples += kFrameSizeSamples;
|
||||
} while (info.encoded_bytes == 0);
|
||||
return rtc::checked_cast<int>(info.encoded_bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioEncoderPcm16B> encoder_;
|
||||
};
|
||||
|
||||
TEST_F(NetEqPcm16bQualityTest, Test) {
|
||||
Simulate();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "modules/audio_coding/codecs/g711/audio_encoder_pcm.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_quality_test.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/numerics/safe_conversions.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
|
||||
ABSL_FLAG(int, frame_size_ms, 20, "Codec frame size (milliseconds).");
|
||||
|
||||
using ::testing::InitGoogleTest;
|
||||
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
namespace {
|
||||
static const int kInputSampleRateKhz = 8;
|
||||
static const int kOutputSampleRateKhz = 8;
|
||||
} // namespace
|
||||
|
||||
class NetEqPcmuQualityTest : public NetEqQualityTest {
|
||||
protected:
|
||||
NetEqPcmuQualityTest()
|
||||
: NetEqQualityTest(absl::GetFlag(FLAGS_frame_size_ms),
|
||||
kInputSampleRateKhz,
|
||||
kOutputSampleRateKhz,
|
||||
SdpAudioFormat("pcmu", 8000, 1)) {
|
||||
// Flag validation
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_frame_size_ms) >= 10 &&
|
||||
absl::GetFlag(FLAGS_frame_size_ms) <= 60 &&
|
||||
(absl::GetFlag(FLAGS_frame_size_ms) % 10) == 0)
|
||||
<< "Invalid frame size, should be 10, 20, ..., 60 ms.";
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_EQ(1u, channels_) << "PCMu supports only mono audio.";
|
||||
AudioEncoderPcmU::Config config;
|
||||
config.frame_size_ms = absl::GetFlag(FLAGS_frame_size_ms);
|
||||
encoder_.reset(new AudioEncoderPcmU(config));
|
||||
NetEqQualityTest::SetUp();
|
||||
}
|
||||
|
||||
int EncodeBlock(int16_t* in_data,
|
||||
size_t block_size_samples,
|
||||
rtc::Buffer* payload,
|
||||
size_t max_bytes) override {
|
||||
const size_t kFrameSizeSamples = 80; // Samples per 10 ms.
|
||||
size_t encoded_samples = 0;
|
||||
uint32_t dummy_timestamp = 0;
|
||||
AudioEncoder::EncodedInfo info;
|
||||
do {
|
||||
info = encoder_->Encode(dummy_timestamp,
|
||||
rtc::ArrayView<const int16_t>(
|
||||
in_data + encoded_samples, kFrameSizeSamples),
|
||||
payload);
|
||||
encoded_samples += kFrameSizeSamples;
|
||||
} while (info.encoded_bytes == 0);
|
||||
return rtc::checked_cast<int>(info.encoded_bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<AudioEncoderPcmU> encoder_;
|
||||
};
|
||||
|
||||
TEST_F(NetEqPcmuQualityTest, Test) {
|
||||
Simulate();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "api/test/metrics/global_metrics_logger_and_exporter.h"
|
||||
#include "api/test/metrics/metric.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_performance_test.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/test_flags.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::webrtc::test::GetGlobalMetricsLogger;
|
||||
using ::webrtc::test::ImprovementDirection;
|
||||
using ::webrtc::test::Unit;
|
||||
|
||||
// Runs a test with 10% packet losses and 10% clock drift, to exercise
|
||||
// both loss concealment and time-stretching code.
|
||||
TEST(NetEqPerformanceTest, 10_Pl_10_Drift) {
|
||||
const int kSimulationTimeMs = 10000000;
|
||||
const int kQuickSimulationTimeMs = 100000;
|
||||
const int kLossPeriod = 10; // Drop every 10th packet.
|
||||
const double kDriftFactor = 0.1;
|
||||
int64_t runtime = test::NetEqPerformanceTest::Run(
|
||||
absl::GetFlag(FLAGS_webrtc_quick_perf_test) ? kQuickSimulationTimeMs
|
||||
: kSimulationTimeMs,
|
||||
kLossPeriod, kDriftFactor);
|
||||
ASSERT_GT(runtime, 0);
|
||||
GetGlobalMetricsLogger()->LogSingleValueMetric(
|
||||
"neteq_performance", "10_pl_10_drift", runtime, Unit::kMilliseconds,
|
||||
ImprovementDirection::kNeitherIsBetter);
|
||||
}
|
||||
|
||||
// Runs a test with neither packet losses nor clock drift, to put
|
||||
// emphasis on the "good-weather" code path, which is presumably much
|
||||
// more lightweight.
|
||||
TEST(NetEqPerformanceTest, 0_Pl_0_Drift) {
|
||||
const int kSimulationTimeMs = 10000000;
|
||||
const int kQuickSimulationTimeMs = 100000;
|
||||
const int kLossPeriod = 0; // No losses.
|
||||
const double kDriftFactor = 0.0; // No clock drift.
|
||||
int64_t runtime = test::NetEqPerformanceTest::Run(
|
||||
absl::GetFlag(FLAGS_webrtc_quick_perf_test) ? kQuickSimulationTimeMs
|
||||
: kSimulationTimeMs,
|
||||
kLossPeriod, kDriftFactor);
|
||||
ASSERT_GT(runtime, 0);
|
||||
GetGlobalMetricsLogger()->LogSingleValueMetric(
|
||||
"neteq_performance", "0_pl_0_drift", runtime, Unit::kMilliseconds,
|
||||
ImprovementDirection::kNeitherIsBetter);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/flags/flag.h"
|
||||
#include "absl/flags/parse.h"
|
||||
#include "modules/audio_coding/neteq/tools/neteq_performance_test.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
// Define command line flags.
|
||||
ABSL_FLAG(int, runtime_ms, 10000, "Simulated runtime in ms.");
|
||||
ABSL_FLAG(int, lossrate, 10, "Packet lossrate; drop every N packets.");
|
||||
ABSL_FLAG(float, drift, 0.1f, "Clockdrift factor.");
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::vector<char*> args = absl::ParseCommandLine(argc, argv);
|
||||
std::string program_name = args[0];
|
||||
std::string usage =
|
||||
"Tool for measuring the speed of NetEq.\n"
|
||||
"Usage: " +
|
||||
program_name +
|
||||
" [options]\n\n"
|
||||
" --runtime_ms=N runtime in ms; default is 10000 ms\n"
|
||||
" --lossrate=N drop every N packets; default is 10\n"
|
||||
" --drift=F clockdrift factor between 0.0 and 1.0; "
|
||||
"default is 0.1\n";
|
||||
if (args.size() != 1) {
|
||||
printf("%s", usage.c_str());
|
||||
return 1;
|
||||
}
|
||||
RTC_CHECK_GT(absl::GetFlag(FLAGS_runtime_ms), 0);
|
||||
RTC_CHECK_GE(absl::GetFlag(FLAGS_lossrate), 0);
|
||||
RTC_CHECK(absl::GetFlag(FLAGS_drift) >= 0.0 &&
|
||||
absl::GetFlag(FLAGS_drift) < 1.0);
|
||||
|
||||
int64_t result = webrtc::test::NetEqPerformanceTest::Run(
|
||||
absl::GetFlag(FLAGS_runtime_ms), absl::GetFlag(FLAGS_lossrate),
|
||||
absl::GetFlag(FLAGS_drift));
|
||||
if (result <= 0) {
|
||||
std::cout << "There was an error" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << "Simulation done" << std::endl;
|
||||
std::cout << "Runtime = " << result << " ms" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/audio_coding/neteq/test/result_sink.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "rtc_base/message_digest.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
|
||||
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
|
||||
#include "external/webrtc/webrtc/modules/audio_coding/neteq/neteq_unittest.pb.h"
|
||||
#else
|
||||
#include "modules/audio_coding/neteq/neteq_unittest.pb.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
void Convert(const webrtc::NetEqNetworkStatistics& stats_raw,
|
||||
webrtc::neteq_unittest::NetEqNetworkStatistics* stats) {
|
||||
stats->set_current_buffer_size_ms(stats_raw.current_buffer_size_ms);
|
||||
stats->set_preferred_buffer_size_ms(stats_raw.preferred_buffer_size_ms);
|
||||
stats->set_jitter_peaks_found(stats_raw.jitter_peaks_found);
|
||||
stats->set_expand_rate(stats_raw.expand_rate);
|
||||
stats->set_speech_expand_rate(stats_raw.speech_expand_rate);
|
||||
stats->set_preemptive_rate(stats_raw.preemptive_rate);
|
||||
stats->set_accelerate_rate(stats_raw.accelerate_rate);
|
||||
stats->set_secondary_decoded_rate(stats_raw.secondary_decoded_rate);
|
||||
stats->set_secondary_discarded_rate(stats_raw.secondary_discarded_rate);
|
||||
stats->set_mean_waiting_time_ms(stats_raw.mean_waiting_time_ms);
|
||||
stats->set_median_waiting_time_ms(stats_raw.median_waiting_time_ms);
|
||||
stats->set_min_waiting_time_ms(stats_raw.min_waiting_time_ms);
|
||||
stats->set_max_waiting_time_ms(stats_raw.max_waiting_time_ms);
|
||||
}
|
||||
|
||||
void AddMessage(FILE* file,
|
||||
rtc::MessageDigest* digest,
|
||||
absl::string_view message) {
|
||||
int32_t size = message.length();
|
||||
if (file)
|
||||
ASSERT_EQ(1u, fwrite(&size, sizeof(size), 1, file));
|
||||
digest->Update(&size, sizeof(size));
|
||||
|
||||
if (file)
|
||||
ASSERT_EQ(static_cast<size_t>(size),
|
||||
fwrite(message.data(), sizeof(char), size, file));
|
||||
digest->Update(message.data(), sizeof(char) * size);
|
||||
}
|
||||
|
||||
#endif // WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
|
||||
ResultSink::ResultSink(absl::string_view output_file)
|
||||
: output_fp_(nullptr),
|
||||
digest_(rtc::MessageDigestFactory::Create(rtc::DIGEST_SHA_1)) {
|
||||
if (!output_file.empty()) {
|
||||
output_fp_ = fopen(std::string(output_file).c_str(), "wb");
|
||||
EXPECT_TRUE(output_fp_ != NULL);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSink::~ResultSink() {
|
||||
if (output_fp_)
|
||||
fclose(output_fp_);
|
||||
}
|
||||
|
||||
void ResultSink::AddResult(const NetEqNetworkStatistics& stats_raw) {
|
||||
#ifdef WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
neteq_unittest::NetEqNetworkStatistics stats;
|
||||
Convert(stats_raw, &stats);
|
||||
|
||||
std::string stats_string;
|
||||
ASSERT_TRUE(stats.SerializeToString(&stats_string));
|
||||
AddMessage(output_fp_, digest_.get(), stats_string);
|
||||
#else
|
||||
FAIL() << "Writing to reference file requires Proto Buffer.";
|
||||
#endif // WEBRTC_NETEQ_UNITTEST_BITEXACT
|
||||
}
|
||||
|
||||
void ResultSink::VerifyChecksum(absl::string_view checksum) {
|
||||
std::string buffer;
|
||||
buffer.resize(digest_->Size());
|
||||
digest_->Finish(buffer.data(), buffer.size());
|
||||
const std::string result = rtc::hex_encode(buffer);
|
||||
if (checksum.size() == result.size()) {
|
||||
EXPECT_EQ(checksum, result);
|
||||
} else {
|
||||
// Check result is one the '|'-separated checksums.
|
||||
EXPECT_NE(checksum.find(result), absl::string_view::npos)
|
||||
<< result << " should be one of these:\n"
|
||||
<< checksum;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_AUDIO_CODING_NETEQ_TEST_RESULT_SINK_H_
|
||||
#define MODULES_AUDIO_CODING_NETEQ_TEST_RESULT_SINK_H_
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "api/neteq/neteq.h"
|
||||
#include "rtc_base/message_digest.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class ResultSink {
|
||||
public:
|
||||
explicit ResultSink(absl::string_view output_file);
|
||||
~ResultSink();
|
||||
|
||||
template <typename T>
|
||||
void AddResult(const T* test_results, size_t length);
|
||||
|
||||
void AddResult(const NetEqNetworkStatistics& stats);
|
||||
|
||||
void VerifyChecksum(absl::string_view ref_check_sum);
|
||||
|
||||
private:
|
||||
FILE* output_fp_;
|
||||
std::unique_ptr<rtc::MessageDigest> digest_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void ResultSink::AddResult(const T* test_results, size_t length) {
|
||||
if (output_fp_) {
|
||||
ASSERT_EQ(length, fwrite(test_results, sizeof(T), length, output_fp_));
|
||||
}
|
||||
digest_->Update(test_results, sizeof(T) * length);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_AUDIO_CODING_NETEQ_TEST_RESULT_SINK_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue