Skip to content

Conversation

@pstroffolino
Copy link
Contributor

VPLAY-12274 ewma algorithm testing

Reason for Change: test code for profiling abr algorithms

Reason for Change: test code for profiling abr algorithms

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings December 30, 2025 20:38
@pstroffolino pstroffolino requested a review from a team as a code owner December 30, 2025 20:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new standalone throughput estimator tool for profiling and testing EWMA (Exponentially Weighted Moving Average) algorithms used in Adaptive Bitrate (ABR) streaming. The tool downloads media segments repeatedly to collect baseline performance data and test throughput estimation algorithms.

Key changes:

  • Implementation of EWMA-based throughput estimation for ABR
  • Mid-download underflow prediction logic
  • Performance profiling capabilities for network downloads

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 20 comments.

Show a summary per file
File Description
throughput_estimator/throughput_estimator/main.cpp New C++17 profiling tool implementing EWMA throughput estimation, transfer statistics tracking, and mid-download abort logic for testing ABR algorithms
throughput_estimator/throughput_estimator.xcodeproj/xcuserdata/.../*.plist User-specific Xcode configuration files (should not be committed)
throughput_estimator/throughput_estimator.xcodeproj/xcuserdata/.../Breakpoints_v2.xcbkptlist User-specific Xcode breakpoint configuration (should not be committed)
throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/xcuserdata/.../UserInterfaceState.xcuserstate User-specific Xcode UI state file (should not be committed)
throughput_estimator/throughput_estimator.xcodeproj/xcshareddata/xcschemes/throughput_estimator.xcscheme Xcode build scheme configuration for the throughput_estimator target
throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata Xcode workspace metadata file
throughput_estimator/throughput_estimator.xcodeproj/project.pbxproj Xcode project file defining build settings, targets, and dependencies
Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings December 31, 2025 03:26
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 4 changed files in this pull request and generated 16 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

pstroffolino and others added 2 commits December 31, 2025 16:36
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
…/pstrof200@cable.comcast.com.xcuserdatad/xcschemes/xcschememanagement.plist
Copilot AI review requested due to automatic review settings December 31, 2025 21:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated 13 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

… removing libcurl dependencies

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 16 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings January 1, 2026 14:50
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 12 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

Comment on lines 109 to 126
class DownloadContext
{
private:
FILE *f = NULL; // logging
static constexpr double ewma_short_window_weight = 0.4;
double ewma_bytes_per_second = 0.0;
size_t dlnow_prev = 0;
double time_prev = 0.0;

public:
explicit DownloadContext( FILE *f );

/**
* @param dltotal total bytes to download
* @param dlnow downloaded bytes so far
*/
int xferinfo( size_t dltotal, size_t dlnow );
};
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DownloadContext class stores a raw FILE pointer as a member without ownership semantics. This violates RAII principles and makes the class difficult to use safely. The class should either own the FILE* (and close it in the destructor) or use a non-owning observer pattern with clear documentation. Consider using std::ofstream instead or at minimum, document the ownership semantics clearly.

Copilot generated this review using guidance from repository custom instructions.
…ded to curlInfo

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings January 1, 2026 16:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 8 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copy link
Contributor

Copilot AI commented Jan 2, 2026

@pstroffolino I've opened a new pull request, #814, to work on those changes. Once the pull request is ready, I'll request review from you.

pstroffolino and others added 2 commits January 2, 2026 18:06
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 2, 2026 23:20
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 14 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

# If not stated otherwise in this file or this component's license file the
# following copyright and licenses apply:
#
# Copyright 2023 RDK Management
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The copyright year should be 2026 to match the other new files in this PR. Currently shows 2023.

Suggested change
# Copyright 2023 RDK Management
# Copyright 2026 RDK Management

Copilot uses AI. Check for mistakes.
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastUpgradeCheck = 2620;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'LastUpgradeCheck' is set to 2620, which appears to be an invalid Xcode version number. Xcode versions follow a pattern like 1420 (for Xcode 14.2). This should be corrected to a valid version number.

Suggested change
LastUpgradeCheck = 2620;
LastUpgradeCheck = 1420;

Copilot uses AI. Check for mistakes.
}

Sample::Sample( const CurlInfo &curlInfo ) : m_curlInfo(curlInfo)
{ // compute derived values
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment contains inconsistent formatting. There's a space after the opening brace on line 83 ("{ // compute derived values"), but on line 64 the same pattern uses a different style. Consider using consistent comment placement throughout the file - either always on the same line after braces or always on a separate line before.

Suggested change
{ // compute derived values
{
// compute derived values

Copilot uses AI. Check for mistakes.
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 26.2;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macOS deployment target is set to 26.2 in the Release configuration as well, which is invalid. This should be a proper macOS version number like 11.0 or later.

Suggested change
MACOSX_DEPLOYMENT_TARGET = 26.2;
MACOSX_DEPLOYMENT_TARGET = 11.0;

Copilot uses AI. Check for mistakes.
#include "NetworkBandwidthEstimator.h"

static const double epsilon = 1e-6;
static const double BLEND_WEIGHT_HARMONIC = 0.6; // 60% harmonic, 40% EWMA
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable 'BLEND_WEIGHT_HARMONIC' should be declared as 'constexpr' instead of 'const' since it's a compile-time constant. This is a C++11 best practice that allows the value to be used in more contexts and communicates intent more clearly.

Copilot generated this review using guidance from repository custom instructions.
#include <string>
#include <iomanip>

std::ofstream f_EWMA;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global file stream 'f_EWMA' violates the principle of avoiding global mutable state. Consider refactoring this to be passed as a parameter or encapsulated in a class to improve testability and maintainability.

Copilot generated this review using guidance from repository custom instructions.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings January 2, 2026 23:34
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 15 comments.

Files not reviewed (1)
  • throughput_estimator/throughput_estimator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: Language not supported

Comment on lines +58 to +70
TEST_F(FunctionalTests, MedianTest)
{
std::vector<double> values;
EXPECT_EQ( GetMedian(values), 0.0 );
values.push_back(10.0);
EXPECT_EQ( GetMedian(values), 10.0 );
values.push_back(30.0);
EXPECT_EQ( GetMedian(values), 20.0 );
values.push_back(30.0);
EXPECT_EQ( GetMedian(values), 30.0 );
values.push_back(20.0);
EXPECT_EQ( GetMedian(values), 25.0 );
}
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for boundary conditions. The MedianTest should include edge cases such as negative values, very large values, NaN, and infinity to ensure robust handling of edge cases in the GetMedian function.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +26
int main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty test main function. The NetworkBandwidthEstimatorTests.cpp file only contains a test main() function but no actual test cases. Either add test cases to this file or remove it if all tests are in FunctionalTests.cpp, as Google Test can auto-generate the main function.

Copilot uses AI. Check for mistakes.
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp);
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid reinterpret_cast when static_cast is sufficient. The conversion from void* to DownloadContext* should use static_cast instead of reinterpret_cast, as the pointer type is known at compile time and this cast is type-safe.

Suggested change
DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp);
DownloadContext *context = static_cast<DownloadContext*>(clientp);

Copilot uses AI. Check for mistakes.
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = C9XTLYAG8J;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded development team identifier. The DEVELOPMENT_TEAM is set to "C9XTLYAG8J" which appears to be a personal or specific team identifier. This should either be removed to allow developers to use their own team settings, or moved to a local configuration file that is not committed to version control.

Suggested change
DEVELOPMENT_TEAM = C9XTLYAG8J;

Copilot uses AI. Check for mistakes.
static constexpr double ALPHA_SLOW = 0.2;

// Harmonic mean over last N samples
// Harmonic mean over last N samples.
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate comment detected. This line is identical to line 111 and should be removed to maintain clean documentation.

Suggested change
// Harmonic mean over last N samples.

Copilot uses AI. Check for mistakes.
Comment on lines 107 to 131
void NetworkBandwidthEstimator::RecomputeHarmonicMeanAndMedianTTFB()
{ // Overhead = median TTFB from all samples
std::vector<double> ttfb;
ttfb.reserve(m_history.size());
for( const auto& s : m_history )
{
ttfb.push_back(s.GetTimeToFirstByteSeconds() );
}
m_estimated_TTFB_seconds = GetMedian(ttfb);

// Harmonic mean of throughput over last harmonic_window samples
const size_t n = m_history.size();
const size_t start = (n > harmonic_window) ? (n - harmonic_window) : 0;
double denominator = 0.0;
size_t count = 0;
for( size_t i = start; i < n; i++ )
{
const double payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond();
if( payloadBytesPerSecond > epsilon )
{
denominator += 1.0/payloadBytesPerSecond;
count++;
}
}
m_harmonic_BytesPerSecond = (count > 0 && denominator > 0.0) ? (static_cast<double>(count) / denominator) : 0.0;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent namespace qualification. The function uses std::size_t for some variables (lines 61, 62) but size_t without std:: for others (line 119, 121, 122). Maintain consistency by using std::size_t throughout the function.

Copilot uses AI. Check for mistakes.
curl_off_t ultotal, curl_off_t ulnow)
{
double now = GetCurrentTimeMonotonicSeconds();
DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp);
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid reinterpret_cast when static_cast is sufficient. The conversion from void* to DownloadContext* should use static_cast instead of reinterpret_cast, as the pointer type is known at compile time and this cast is type-safe.

Suggested change
DownloadContext *context = reinterpret_cast<DownloadContext*>(clientp);
DownloadContext *context = static_cast<DownloadContext*>(clientp);

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +145
m_history.emplace_back(sample);

if (m_history.size() > MAX_HISTORY)
{ // Trim history to avoid unbounded growth
m_history.erase(m_history.begin(), m_history.begin() + (m_history.size() - MAX_HISTORY));
}

Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vector is resized unnecessarily. Using emplace_back followed by erase when history exceeds MAX_HISTORY is inefficient. Consider using a circular buffer or std::deque, or at minimum, check the size before emplacing to avoid the grow-then-shrink pattern on every insertion once the history is full.

Suggested change
m_history.emplace_back(sample);
if (m_history.size() > MAX_HISTORY)
{ // Trim history to avoid unbounded growth
m_history.erase(m_history.begin(), m_history.begin() + (m_history.size() - MAX_HISTORY));
}
// Trim history to avoid unbounded growth before inserting new sample
if( m_history.size() == MAX_HISTORY )
{
m_history.erase(m_history.begin());
}
m_history.emplace_back(sample);

Copilot uses AI. Check for mistakes.
/**
* @brief predict completion time for a new segment
*/
double NetworkBandwidthEstimator::GetPredictedDownloadTimeSeconds(size_t segment_size_bytes) const
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer std::size_t over size_t for consistency with modern C++ style. While both are equivalent, std::size_t makes the namespace explicit and is preferred in modern C++ codebases.

Copilot uses AI. Check for mistakes.
Comment on lines 98 to 107
for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ )
{
EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon );
EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon );
EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon );

CurlInfo curlInfo;
curlInfo.m_size_download_bytes = test_data[i].download_bytes;
curlInfo.m_total_time_seconds = test_data[i].total_time_seconds;
curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C-style array size calculation is error-prone. Replace sizeof(test_data)/sizeof(test_data[0]) with std::size(test_data) from <iterator> (C++17) or use a range-based for loop to iterate over the array directly, which is safer and more modern.

Suggested change
for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ )
{
EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon );
EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon );
EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon );
CurlInfo curlInfo;
curlInfo.m_size_download_bytes = test_data[i].download_bytes;
curlInfo.m_total_time_seconds = test_data[i].total_time_seconds;
curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds;
for( const auto &data : test_data )
{
EXPECT_NEAR( data.timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon );
EXPECT_NEAR( data.throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon );
EXPECT_NEAR( data.predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon );
CurlInfo curlInfo;
curlInfo.m_size_download_bytes = data.download_bytes;
curlInfo.m_total_time_seconds = data.total_time_seconds;
curlInfo.m_time_to_first_byte_seconds = data.time_to_first_byte_seconds;

Copilot uses AI. Check for mistakes.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings January 2, 2026 23:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 14 comments.

Comment on lines +161 to +165
static constexpr double m_ewma_short_window_weight = 0.4;
double m_ewma_bytes_per_second = 0.0;
std::size_t m_dltotal = 0;
std::size_t m_dlnow_prev = 0;
double m_time_prev = 0.0;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The member variable names use m_ prefix with snake_case (m_ewma_short_window_weight, m_ewma_bytes_per_second, m_dltotal, m_dlnow_prev, m_time_prev). According to the coding guidelines, member variables should use the m prefix with camelCase. Consider renaming to: mEwmaShortWindowWeight, mEwmaBytesPerSecond, mDlTotal, mDlNowPrev, mTimePrev.

Suggested change
static constexpr double m_ewma_short_window_weight = 0.4;
double m_ewma_bytes_per_second = 0.0;
std::size_t m_dltotal = 0;
std::size_t m_dlnow_prev = 0;
double m_time_prev = 0.0;
static constexpr double mEwmaShortWindowWeight = 0.4;
double mEwmaBytesPerSecond = 0.0;
std::size_t mDlTotal = 0;
std::size_t mDlNowPrev = 0;
double mTimePrev = 0.0;

Copilot uses AI. Check for mistakes.
Comment on lines 252 to 259
const size_t delta_bytes = dlnow - m_dlnow_prev;
const double delta_time = now - m_time_prev;
m_dltotal = dltotal;
if( delta_bytes > 0 )
{ // some data has trickled in
if( delta_time > epsilon )
{
const double Bps = static_cast<double>(delta_bytes)/delta_time;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local variable names use snake_case (delta_bytes, delta_time) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to deltaBytes and deltaTime for consistency.

Suggested change
const size_t delta_bytes = dlnow - m_dlnow_prev;
const double delta_time = now - m_time_prev;
m_dltotal = dltotal;
if( delta_bytes > 0 )
{ // some data has trickled in
if( delta_time > epsilon )
{
const double Bps = static_cast<double>(delta_bytes)/delta_time;
const size_t deltaBytes = dlnow - m_dlnow_prev;
const double deltaTime = now - m_time_prev;
m_dltotal = dltotal;
if( deltaBytes > 0 )
{ // some data has trickled in
if( deltaTime > epsilon )
{
const double Bps = static_cast<double>(deltaBytes) / deltaTime;

Copilot uses AI. Check for mistakes.
Comment on lines 173 to 184
double EWMA_min = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0)
? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond)
: std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond);
if( EWMA_min > 0.0 )
{
if (m_harmonic_BytesPerSecond > 0.0)
{
return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * EWMA_min;
}
else
{
return EWMA_min;
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local variable names payloadBytesPerSecond and EWMA_min use inconsistent casing. According to the coding guidelines, variables should use camelCase. Consider renaming EWMA_min to ewmaMin for consistency with the camelCase convention.

Suggested change
double EWMA_min = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0)
? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond)
: std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond);
if( EWMA_min > 0.0 )
{
if (m_harmonic_BytesPerSecond > 0.0)
{
return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * EWMA_min;
}
else
{
return EWMA_min;
double ewmaMin = (m_EWMA_fast_BytesPerSecond > 0.0 && m_EWMA_slow_BytesPerSecond > 0.0)
? std::min(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond)
: std::max(m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond);
if( ewmaMin > 0.0 )
{
if (m_harmonic_BytesPerSecond > 0.0)
{
return BLEND_WEIGHT_HARMONIC * m_harmonic_BytesPerSecond + (1.0 - BLEND_WEIGHT_HARMONIC) * ewmaMin;
}
else
{
return ewmaMin;

Copilot uses AI. Check for mistakes.
Comment on lines 28 to 33
FunctionalTests()
{
}
};


Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The empty constructor body is unnecessary. Since the constructor does nothing, it can be removed entirely or declared with = default in the class definition, which is more explicit about its intent and follows modern C++ best practices.

Suggested change
FunctionalTests()
{
}
};
FunctionalTests() = default;
};

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +48
struct CurlInfo
{
std::size_t m_size_download_bytes; // CURLINFO_SIZE_DOWNLOAD
double m_total_time_seconds; // CURLINFO_TOTAL_TIME
double m_time_to_first_byte_seconds; // CURLINFO_STARTTRANSFER_TIME
};
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CurlInfo struct uses built-in types (std::size_t and double) instead of fixed-width integer types. According to the coding guidelines, when defining data structures that may be accessed from other languages (like Python's ctypes), use fixed-width integer types from cstdint header (e.g., uint64_t, uint32_t) to ensure consistent memory layout across platforms. Consider using uint64_t for m_size_download_bytes if this structure might be used for cross-language interoperability.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 76 to 95
const struct
{
double timeToFirstByteSeconds;
double throughputBytesPerSecond;
double predictedDownloadTimeSeconds;

size_t download_bytes;
double total_time_seconds;
double time_to_first_byte_seconds;
} test_data[] =
{
{ 0.000000,0.000000,0.000000,112463,0.398257,0.249254 },
{ 0.249254,754770.038187,0.398257,112463,0.130057,0.048034 },
{ 0.148644,935373.273008,0.268877,112463,0.120855,0.056830 },
{ 0.056830,1107592.676445,0.158368,112463,0.119009,0.057246 },
{ 0.057038,1239315.381966,0.147784,112463,0.116028,0.048744 },
{ 0.056830,1315556.262523,0.142317,112463,0.117878,0.052516 },
{ 0.054673,1380828.841638,0.136119,112463,0.116275,0.051314 },
{ 0.052516,1433386.044995,0.130976,112463,0.116544,0.051999 }
};
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable names in this test use snake_case (test_data, download_bytes, total_time_seconds, time_to_first_byte_seconds) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to testData, downloadBytes, totalTimeSeconds, timeToFirstByteSeconds.

Copilot uses AI. Check for mistakes.
};

NetworkBandwidthEstimator networkBandwidthEstimator;
for( int i=0; i<sizeof(test_data)/sizeof(test_data[0]); i++ )
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using C-style array size calculation with sizeof is error-prone and not modern C++. The code uses sizeof(test_data)/sizeof(test_data[0]) which can be replaced with std::size(test_data) (C++17) or the simpler range-based for loop. Consider refactoring to: for(const auto& test : test_data) to improve readability and type safety.

Copilot generated this review using guidance from repository custom instructions.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines 168 to 169
DownloadContext();
~DownloadContext();
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor and destructor declarations are redundant. Since both are defaulted in the implementation file (lines 217, 219), you can use the = default syntax directly in the header or simply omit them if they would be implicitly generated with the same behavior. This reduces unnecessary declarations and improves maintainability.

Suggested change
DownloadContext();
~DownloadContext();
DownloadContext() = default;
~DownloadContext() = default;

Copilot uses AI. Check for mistakes.
Comment on lines 97 to 105
std::vector<Sample> m_history;

// Robust per-request overhead Time to First Byte (TTFB) estimate
double m_estimated_TTFB_seconds = 0.0; // median TTFB - computed brute force

// Robust throughput estimates (bytes/s)
double m_EWMA_fast_BytesPerSecond = 0.0; // reacts quickly
double m_EWMA_slow_BytesPerSecond = 0.0; // stable
double m_harmonic_BytesPerSecond = 0.0; // conservative
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The member variable names use m_ prefix with snake_case or inconsistent patterns (m_history, m_estimated_TTFB_seconds, m_EWMA_fast_BytesPerSecond, m_EWMA_slow_BytesPerSecond, m_harmonic_BytesPerSecond). According to the coding guidelines, member variables should use the m prefix with camelCase. Consider standardizing to: mHistory, mEstimatedTtfbSeconds, mEwmaFastBytesPerSecond, mEwmaSlowBytesPerSecond, mHarmonicBytesPerSecond.

Suggested change
std::vector<Sample> m_history;
// Robust per-request overhead Time to First Byte (TTFB) estimate
double m_estimated_TTFB_seconds = 0.0; // median TTFB - computed brute force
// Robust throughput estimates (bytes/s)
double m_EWMA_fast_BytesPerSecond = 0.0; // reacts quickly
double m_EWMA_slow_BytesPerSecond = 0.0; // stable
double m_harmonic_BytesPerSecond = 0.0; // conservative
std::vector<Sample> mHistory;
// Robust per-request overhead Time to First Byte (TTFB) estimate
double mEstimatedTtfbSeconds = 0.0; // median TTFB - computed brute force
// Robust throughput estimates (bytes/s)
double mEwmaFastBytesPerSecond = 0.0; // reacts quickly
double mEwmaSlowBytesPerSecond = 0.0; // stable
double mHarmonicBytesPerSecond = 0.0; // conservative

Copilot uses AI. Check for mistakes.
{
Sample sample(curlInfo);
// extract derived payload_bytes_per_second from sample
const double payload_bytes_per_second = sample.GetPayloadBytesPerSecond();
Copy link

Copilot AI Jan 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The local variable names use snake_case (payload_bytes_per_second) which is inconsistent with the project's camelCase convention for variables. According to the coding guidelines, variables should use camelCase. Consider renaming to payloadBytesPerSecond for consistency.

Copilot uses AI. Check for mistakes.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings January 3, 2026 20:32
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 12 comments.

static constexpr double ALPHA_FAST = 0.5;
static constexpr double ALPHA_SLOW = 0.2;

// Harmonic mean over last N samples
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a duplicate comment on line 112. The comment "Harmonic mean over last N samples" appears on both line 111 and line 112. Remove the duplicate on line 111.

Suggested change
// Harmonic mean over last N samples

Copilot uses AI. Check for mistakes.
Comment on lines 23 to 29
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <optional>
#include <string>
#include <ctime>
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The includes for iostream, cstdio, cstdlib, ctime, and string appear to be unused in this file. Unused includes should be removed to minimize compilation dependencies and improve build times.

Suggested change
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <optional>
#include <string>
#include <ctime>
#include <numeric>
#include <optional>

Copilot uses AI. Check for mistakes.
* @return The median value computed from @p values.
*/
double GetMedian( std::vector<double> &values );
double GetCurrentTimeMonotonicSeconds( void );
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function signature uses 'void' in the parameter list, which is C-style syntax. In modern C++, empty parentheses should be used instead. Change 'GetCurrentTimeMonotonicSeconds( void )' to 'GetCurrentTimeMonotonicSeconds()'.

Suggested change
double GetCurrentTimeMonotonicSeconds( void );
double GetCurrentTimeMonotonicSeconds();

Copilot uses AI. Check for mistakes.
*/
struct CurlInfo
{
std::size_t m_size_download_bytes; // CURLINFO_SIZE_DOWNLOAD
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The member variables use raw 'size_t' type. For structures that may be used across language boundaries or need guaranteed sizes, consider using fixed-width types from <cstdint> such as 'uint64_t' instead of 'size_t' to ensure consistent memory layout across platforms.

Copilot uses AI. Check for mistakes.
Comment on lines +44 to +51
static int xferinfo(void *clientp,
curl_off_t dltotal, curl_off_t dlnow,
curl_off_t , curl_off_t )
{
DownloadContext *context = static_cast<DownloadContext*>(clientp);
const double now = GetCurrentTimeMonotonicSeconds();
return context->xferinfo( now, dltotal, dlnow );
}
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test uses the DownloadContext class defined in the production code being tested, but the xferinfo callback function and related test infrastructure reference DownloadContext without showing how it's instantiated or used in the actual production code. The test appears to be testing implementation rather than behavior through the public API. Consider whether this test should be structured differently to test the component's public interface more directly.

Copilot uses AI. Check for mistakes.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
Copilot AI review requested due to automatic review settings January 3, 2026 21:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 22 comments.

{ // some data has trickled in
if( delta_time > epsilon )
{
const double bytesPerSecond = static_cast<double>(delta_bytes)/delta_time;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction: auto bytesPerSecond = static_cast(delta_bytes)/delta_time;

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +67 to +71
const double upper = values[mid];

// Find the element at mid-1 position (the max of the lower half)
std::nth_element(values.begin(), values.begin() + (mid - 1), values.end());
const double lower = values[mid - 1];
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction: auto upper = values[mid]; auto lower = values[mid - 1];

Copilot generated this review using guidance from repository custom instructions.
*/
double NetworkBandwidthEstimator::GetThroughputBytesPerSecond() const
{
double ewma_min = (m_ewma_fast_BytesPerSecond > 0.0 && m_ewma_slow_BytesPerSecond > 0.0)
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction to improve readability: auto ewma_min = ...

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +30 to +38
/**
* @brief libcurl progress callback (XFERINFOFUNCTION)
* This is called at frequent intervals allowing application to monitor progress and abort stalled transfers
* Note: this may be called even after all data has been downloaded while final logic is executed
*
* @param dltotal total bytes to download
* @param dlnow downloaded bytes so far
* @param ultotal total bytes to upload
* @param ulnow uploaded bytes so far
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Doxygen documentation for the clientp parameter. All parameters should be documented with @param tags for consistency with the project's documentation standards.

Copilot uses AI. Check for mistakes.
double DownloadContext::GetEstimatedRemainingTime() const
{
double remaining_time_seconds = 0.0;
const size_t remaining_bytes = m_dltotal - m_dlnow_prev;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction: auto remaining_bytes = m_dltotal - m_dlnow_prev;

Copilot generated this review using guidance from repository custom instructions.
size_t count = 0;
for( size_t i = start; i < n; i++ )
{
const double payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond();
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction: auto payloadBytesPerSecond = m_history[i].GetPayloadBytesPerSecond();

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 93 to 104
for( int i=0; i<test_data.size(); i++ )
{
EXPECT_NEAR( test_data[i].timeToFirstByteSeconds, networkBandwidthEstimator.GetTimeToFirstByteSeconds(), epsilon );
EXPECT_NEAR( test_data[i].throughputBytesPerSecond, networkBandwidthEstimator.GetThroughputBytesPerSecond(), epsilon );
EXPECT_NEAR( test_data[i].predictedDownloadTimeSeconds, networkBandwidthEstimator.GetPredictedDownloadTimeSeconds(segment_size_bytes), epsilon );

CurlInfo curlInfo;
curlInfo.m_size_download_bytes = test_data[i].download_bytes;
curlInfo.m_total_time_seconds = test_data[i].total_time_seconds;
curlInfo.m_time_to_first_byte_seconds = test_data[i].time_to_first_byte_seconds;
networkBandwidthEstimator.UpdateDownloadMetrics(curlInfo);
}
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a range-based for loop instead of index-based iteration for better readability and to follow modern C++ practices. For example: for (const auto& data : test_data) { /* use data instead of test_data[i] */ }

Copilot generated this review using guidance from repository custom instructions.
class Sample
{
private:
CurlInfo m_curlInfo;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming convention violates the project's C++ standards. Member variables should be prefixed with 'm' (e.g., mCurlInfo) rather than using m_ prefix. The project uses camelCase for member variables with 'm' prefix, not snake_case with m_ prefix.

Copilot generated this review using guidance from repository custom instructions.
* @brief constructor - populate sample metrics
* @param curlInfo Curl Handle profiling data
*/
Sample( const CurlInfo &curlInfo );
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor should be marked as explicit to prevent implicit conversions, following modern C++ best practices. Change to: explicit Sample(const CurlInfo &curlInfo);

Copilot generated this review using guidance from repository custom instructions.
*/
double NetworkBandwidthEstimator::GetPredictedDownloadTimeSeconds(size_t segment_size_bytes) const
{
const double throughput = GetThroughputBytesPerSecond();
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using auto for type deduction: auto throughput = GetThroughputBytesPerSecond();

Copilot generated this review using guidance from repository custom instructions.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
@pstroffolino pstroffolino merged commit 666992f into dev_sprint_25_2 Jan 3, 2026
5 of 8 checks passed
@pstroffolino pstroffolino deleted the feature/VPLAY-12274-abr branch January 3, 2026 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants