Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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(&current_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

View file

@ -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_

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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_