From a8e0d9f24b57cec116474021de9d8ea517e5aa17 Mon Sep 17 00:00:00 2001 From: Anurag Krishnan Date: Wed, 14 Jan 2026 19:21:28 +0530 Subject: [PATCH 01/15] VPLAY-12359 suboptimal UTCTiming behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reason for change: Updates UTCTiming logic to skip sync if interval hasn’t elapsed. Add Configs to control the interval Test Procedure: updated in ticket Risks: Low Signed-off-by: Anurag Krishnan --- AampConfig.cpp | 5 ++++- AampConfig.h | 3 +++ AampDefine.h | 3 +++ fragmentcollector_mpd.cpp | 25 ++++++++++++++++++------- fragmentcollector_mpd.h | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/AampConfig.cpp b/AampConfig.cpp index 565095eea..8da87da8f 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -368,7 +368,8 @@ static const ConfigLookupEntryBool mConfigLookupTableBool[AAMPCONFIG_BOOL_COUNT] {false, "curlThroughput", eAAMPConfig_CurlThroughput, false }, {false, "useFireboltSDK", eAAMPConfig_UseFireboltSDK, false}, {true, "enableChunkInjection", eAAMPConfig_EnableChunkInjection, true}, - {false, "debugChunkTransfer", eAAMPConfig_DebugChunkTransfer, false} + {false, "debugChunkTransfer", eAAMPConfig_DebugChunkTransfer, false}, + {true, "utcSyncOnStartup", eAAMPConfig_UTCSyncOnStartup, true} }; #define CONFIG_INT_ALIAS_COUNT 2 @@ -471,6 +472,8 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON // aliases, kept for backwards compatibility {DEFAULT_INIT_BITRATE,"defaultBitrate",eAAMPConfig_DefaultBitrate,true }, {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true }, + {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, + {DEFAULT_UTC_SYNC_ON_ERROR_RETRY,"utcSyncOnErrorRetrySec",eAAMPConfig_UTCSyncOnErrorRetrySec,true }, }; /** diff --git a/AampConfig.h b/AampConfig.h index 15811aebd..348d05db3 100644 --- a/AampConfig.h +++ b/AampConfig.h @@ -214,6 +214,7 @@ typedef enum eAAMPConfig_UseFireboltSDK, /**< Config to use Firebolt SDK for license Acquisition */ eAAMPConfig_EnableChunkInjection, /**< Config to enable chunk injection for low latency DASH */ eAAMPConfig_DebugChunkTransfer, /**< app-managed chunked transfer protocol */ + eAAMPConfig_UTCSyncOnStartup, /** Perform sync at startup */ eAAMPConfig_BoolMaxValue /**< Max value of bool config always last element */ } AAMPConfigSettingBool; @@ -311,6 +312,8 @@ typedef enum eAAMPConfig_MonitorAVJumpThreshold, /**< configures threshold aligned audio,video positions advancing together by unexpectedly large delta to be reported as jump in milliseconds*/ eAAMPConfig_ProgressLoggingDivisor, /**< Divisor to avoid printing the progress report too frequently in the log */ eAAMPConfig_MonitorAVReportingInterval, /**< Timeout in milliseconds for reporting MonitorAV events */ + eAAMPConfig_UTCSyncMinIntervalSec, /**< Minimum interval between sync attempts */ + eAAMPConfig_UTCSyncOnErrorRetrySec, /**< Backoff after error */ eAAMPConfig_IntMaxValue /**< Max value of int config always last element*/ } AAMPConfigSettingInt; #define AAMPCONFIG_INT_COUNT (eAAMPConfig_IntMaxValue) diff --git a/AampDefine.h b/AampDefine.h index 8cbdbc359..aa4bd13a3 100644 --- a/AampDefine.h +++ b/AampDefine.h @@ -143,6 +143,9 @@ #define DEFAULT_MONITOR_AV_JUMP_THRESHOLD_MS 100 /**< default jump threshold to MonitorAV reporting */ #define DEFAULT_MAX_DOWNLOAD_BUFFER 10 /**< Default maximum download buffer in seconds, this can be used to limit player download job scheduling for DASH */ #define DEFAULT_MONITOR_AV_REPORTING_INTERVAL 1000 /**< time interval in ms for MonitorAV reporting */ +#define DEFAULT_UTC_SYNC_MIN_INTERVAL 60 /**< Minimum interval between sync attempts */ +#define DEFAULT_UTC_SYNC_ON_ERROR_RETRY 120 /**< Backoff after error */ + // We can enable the following once we have a thread monitoring video PTS progress and triggering subtec clock fast update when we detect video freeze. Disabled it for now for brute force fast refresh.. //#define SUBTEC_VARIABLE_CLOCK_UPDATE_RATE /* enable this to make the clock update rate dynamic*/ diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 83f56e642..217a412c6 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4505,16 +4505,27 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) aamp_ResolveURL(ServerUrl, aamp->GetManifestUrl(), valueCopy.c_str(), false); } - mLocalUtcTime = GetNetworkTime(ServerUrl, &http_error, aamp->GetNetworkProxy()); - if(mLocalUtcTime > 0 ) + double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; + if ((!mTimeSyncClient.hasSynced && ISCONFIGSET(eAAMPConfig_UTCSyncOnStartup)) || (elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec))) { - double currentTime = (double)aamp_GetCurrentTimeMS() / 1000; - mDeltaTime = mLocalUtcTime - currentTime; - hasServerUtcTime = true; + mLocalUtcTime = GetNetworkTime(ServerUrl, &http_error, aamp->GetNetworkProxy()); + if(mLocalUtcTime > 0) + { + mTimeSyncClient.lastSync = aamp_GetCurrentTimeMS(); + mDeltaTime = mLocalUtcTime - (double)mTimeSyncClient.lastSync / 1000; + mTimeSyncClient.lastOffset = mDeltaTime; + mTimeSyncClient.hasSynced = true; + hasServerUtcTime = true; + } + else + { + AAMPLOG_WARN("Failed to read timeServer [%s] RetCode[%d]",ServerUrl.c_str(),http_error); + } } - else + else if(mTimeSyncClient.hasSynced) { - AAMPLOG_ERR("Failed to read timeServer [%s] RetCode[%d]",ServerUrl.c_str(),http_error); + mDeltaTime = mTimeSyncClient.lastOffset; + hasServerUtcTime = true; } break; } diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 1bab9657b..d6c71ded8 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -67,6 +67,19 @@ struct ProfileInfo int representationIndex; }; +/** + * @struct TimeSyncClient + * @brief UTC time sync info + */ +struct TimeSyncClient +{ + long long lastSync; + double lastOffset; + bool hasSynced; + TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) + {} +}; + class AampDashWorkerJob : public aamp::AampTrackWorkerJob { private: @@ -1244,6 +1257,7 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP bool mShortAdOffsetCalc; AampTime mNextPts; /*For PTS restamping*/ bool mIsFinalFirstPTS; /**< Flag to indicate if the first PTS is final or not */ + TimeSyncClient mTimeSyncClient; }; #endif //FRAGMENTCOLLECTOR_MPD_H_ From e57c144ac64ae5c04a7a618f167b6482d518debb Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 14:35:19 -0500 Subject: [PATCH 02/15] Reason for Change: deprecated eAAMPConfig_UTCSyncOnErrorRetrySec fixed bug with config order causing runtime crash added some doxygen comments Signed-off-by: Philip Stroffolino --- AampConfig.cpp | 9 +++++---- AampConfig.h | 1 - AampDefine.h | 1 - fragmentcollector_mpd.h | 8 ++++---- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/AampConfig.cpp b/AampConfig.cpp index 8da87da8f..5537c1585 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -469,11 +469,12 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON {DEFAULT_MONITOR_AV_JUMP_THRESHOLD_MS,"monitorAVJumpThreshold",eAAMPConfig_MonitorAVJumpThreshold,true,eCONFIG_RANGE_MONITOR_AVSYNC_JUMP_THRESHOLD }, {DEFAULT_PROGRESS_LOGGING_DIVISOR,"progressLoggingDivisor",eAAMPConfig_ProgressLoggingDivisor,false}, {DEFAULT_MONITOR_AV_REPORTING_INTERVAL, "monitorAVReportingInterval", eAAMPConfig_MonitorAVReportingInterval, false}, + {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, + // add new entries here + // aliases, kept for backwards compatibility {DEFAULT_INIT_BITRATE,"defaultBitrate",eAAMPConfig_DefaultBitrate,true }, - {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true }, - {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, - {DEFAULT_UTC_SYNC_ON_ERROR_RETRY,"utcSyncOnErrorRetrySec",eAAMPConfig_UTCSyncOnErrorRetrySec,true }, + {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true } }; /** @@ -498,7 +499,7 @@ static const ConfigLookupEntryFloat mConfigLookupTableFloat[AAMPCONFIG_FLOAT_COU {DEFAULT_NORMAL_RATE_CORRECTION_SPEED,"normalLatencyCorrectionPlaybackRate",eAAMPConfig_NormalLatencyCorrectionPlaybackRate,false}, {DEFAULT_MIN_BUFFER_LOW_LATENCY,"lowLatencyMinBuffer",eAAMPConfig_LowLatencyMinBuffer,true, eCONFIG_RANGE_LLDBUFFER}, {DEFAULT_TARGET_BUFFER_LOW_LATENCY,"lowLatencyTargetBuffer",eAAMPConfig_LowLatencyTargetBuffer,true, eCONFIG_RANGE_LLDBUFFER}, - {GST_BW_TO_BUFFER_FACTOR,"bandwidthToBufferFactor", eAAMPConfig_BWToGstBufferFactor,true}, + {GST_BW_TO_BUFFER_FACTOR,"bandwidthToBufferFactor", eAAMPConfig_BWToGstBufferFactor,true} }; /** diff --git a/AampConfig.h b/AampConfig.h index 348d05db3..5c94d9db9 100644 --- a/AampConfig.h +++ b/AampConfig.h @@ -313,7 +313,6 @@ typedef enum eAAMPConfig_ProgressLoggingDivisor, /**< Divisor to avoid printing the progress report too frequently in the log */ eAAMPConfig_MonitorAVReportingInterval, /**< Timeout in milliseconds for reporting MonitorAV events */ eAAMPConfig_UTCSyncMinIntervalSec, /**< Minimum interval between sync attempts */ - eAAMPConfig_UTCSyncOnErrorRetrySec, /**< Backoff after error */ eAAMPConfig_IntMaxValue /**< Max value of int config always last element*/ } AAMPConfigSettingInt; #define AAMPCONFIG_INT_COUNT (eAAMPConfig_IntMaxValue) diff --git a/AampDefine.h b/AampDefine.h index aa4bd13a3..813ee646d 100644 --- a/AampDefine.h +++ b/AampDefine.h @@ -144,7 +144,6 @@ #define DEFAULT_MAX_DOWNLOAD_BUFFER 10 /**< Default maximum download buffer in seconds, this can be used to limit player download job scheduling for DASH */ #define DEFAULT_MONITOR_AV_REPORTING_INTERVAL 1000 /**< time interval in ms for MonitorAV reporting */ #define DEFAULT_UTC_SYNC_MIN_INTERVAL 60 /**< Minimum interval between sync attempts */ -#define DEFAULT_UTC_SYNC_ON_ERROR_RETRY 120 /**< Backoff after error */ // We can enable the following once we have a thread monitoring video PTS progress and triggering subtec clock fast update when we detect video freeze. Disabled it for now for brute force fast refresh.. diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index d6c71ded8..9d43412e7 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -69,13 +69,13 @@ struct ProfileInfo /** * @struct TimeSyncClient - * @brief UTC time sync info + * @brief UTC time sync client state */ struct TimeSyncClient { - long long lastSync; - double lastOffset; - bool hasSynced; + long long lastSync; // utcms time at which time was last synchronized with time server + double lastOffset; // current delta (seconds) between local and server time + bool hasSynced; // true if time has been synchronized at least once TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) {} }; From 63776aaffcc26e32e626136efdc413e2cadd4b1a Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 15:36:12 -0500 Subject: [PATCH 03/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AampConfig.cpp | 2 +- AampConfig.h | 2 +- fragmentcollector_mpd.cpp | 4 +++- fragmentcollector_mpd.h | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/AampConfig.cpp b/AampConfig.cpp index 5537c1585..ba1943325 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -471,7 +471,7 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON {DEFAULT_MONITOR_AV_REPORTING_INTERVAL, "monitorAVReportingInterval", eAAMPConfig_MonitorAVReportingInterval, false}, {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, // add new entries here - + // aliases, kept for backwards compatibility {DEFAULT_INIT_BITRATE,"defaultBitrate",eAAMPConfig_DefaultBitrate,true }, {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true } diff --git a/AampConfig.h b/AampConfig.h index 5c94d9db9..af0aff4be 100644 --- a/AampConfig.h +++ b/AampConfig.h @@ -214,7 +214,7 @@ typedef enum eAAMPConfig_UseFireboltSDK, /**< Config to use Firebolt SDK for license Acquisition */ eAAMPConfig_EnableChunkInjection, /**< Config to enable chunk injection for low latency DASH */ eAAMPConfig_DebugChunkTransfer, /**< app-managed chunked transfer protocol */ - eAAMPConfig_UTCSyncOnStartup, /** Perform sync at startup */ + eAAMPConfig_UTCSyncOnStartup, /**< Perform sync at startup */ eAAMPConfig_BoolMaxValue /**< Max value of bool config always last element */ } AAMPConfigSettingBool; diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 217a412c6..70d2ff820 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4506,7 +4506,9 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) } double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; - if ((!mTimeSyncClient.hasSynced && ISCONFIGSET(eAAMPConfig_UTCSyncOnStartup)) || (elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec))) + bool shouldSyncOnStartup = !mTimeSyncClient.hasSynced && ISCONFIGSET(eAAMPConfig_UTCSyncOnStartup); + bool intervalElapsed = elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec); + if (shouldSyncOnStartup || intervalElapsed) { mLocalUtcTime = GetNetworkTime(ServerUrl, &http_error, aamp->GetNetworkProxy()); if(mLocalUtcTime > 0) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 9d43412e7..ee639003c 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -73,7 +73,7 @@ struct ProfileInfo */ struct TimeSyncClient { - long long lastSync; // utcms time at which time was last synchronized with time server + long long lastSync; // UTC time in milliseconds since epoch when time was last synchronized with time server double lastOffset; // current delta (seconds) between local and server time bool hasSynced; // true if time has been synchronized at least once TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) From 2721698c33e5797aca493bdbdc60ee75dd454920 Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 15:44:29 -0500 Subject: [PATCH 04/15] Reason for Change: additional comments Signed-off-by: Philip Stroffolino --- fragmentcollector_mpd.cpp | 5 ++++- fragmentcollector_mpd.h | 16 +++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 70d2ff820..d9f6b5319 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4468,11 +4468,14 @@ void StreamAbstractionAAMP_MPD::FindPeriodGapsAndReport() } } +TimeSyncClient::TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) +{}; + /** * @brief Read UTCTiming _element_ * @retval Return true if UTCTiming _element_ is available in the manifest */ -bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) +bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) { bool hasServerUtcTime = false; if( root ) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index ee639003c..9cd561b3a 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -69,15 +69,17 @@ struct ProfileInfo /** * @struct TimeSyncClient - * @brief UTC time sync client state - */ + * + * @brief this class defines state associated with periodic time server synchronization. it is used in accordance with configuration after a manifest has been refreshed to determine if elapsed time is sufficient to merit requesting updated time from server. + * + * The constructor is used to initialize lastSync with current time. +*/ struct TimeSyncClient { - long long lastSync; // UTC time in milliseconds since epoch when time was last synchronized with time server - double lastOffset; // current delta (seconds) between local and server time - bool hasSynced; // true if time has been synchronized at least once - TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) - {} + long long lastSync; /**< UTC time in milliseconds since epoch when time was last synchronized with time server */ + double lastOffset; /**< current delta (seconds) between local and server time */ + bool hasSynced; /**< true if time has been synchronized at least once */ + TimeSyncClient(); }; class AampDashWorkerJob : public aamp::AampTrackWorkerJob From 8f7466061cd1572cad9b16702f6f1cf123a376d8 Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 16:03:43 -0500 Subject: [PATCH 05/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index d9f6b5319..3a710692a 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4508,9 +4508,13 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) aamp_ResolveURL(ServerUrl, aamp->GetManifestUrl(), valueCopy.c_str(), false); } - double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; bool shouldSyncOnStartup = !mTimeSyncClient.hasSynced && ISCONFIGSET(eAAMPConfig_UTCSyncOnStartup); - bool intervalElapsed = elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec); + bool intervalElapsed = false; + if (!shouldSyncOnStartup) + { + double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; + intervalElapsed = elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec); + } if (shouldSyncOnStartup || intervalElapsed) { mLocalUtcTime = GetNetworkTime(ServerUrl, &http_error, aamp->GetNetworkProxy()); From e14c9ece41debf8f7eacb19f1651b1a3a1ab970c Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 16:18:59 -0500 Subject: [PATCH 06/15] Readon for Change: more better comments Signed-off-by: Philip Stroffolino --- fragmentcollector_mpd.cpp | 8 ++++++-- fragmentcollector_mpd.h | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index d9f6b5319..d05d80db5 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4508,9 +4508,13 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) aamp_ResolveURL(ServerUrl, aamp->GetManifestUrl(), valueCopy.c_str(), false); } - double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; bool shouldSyncOnStartup = !mTimeSyncClient.hasSynced && ISCONFIGSET(eAAMPConfig_UTCSyncOnStartup); - bool intervalElapsed = elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec); + bool intervalElapsed = false; + if( !shouldSyncOnStartup ) + { + const double elapsed = (double)(aamp_GetCurrentTimeMS() - mTimeSyncClient.lastSync) / 1000; + intervalElapsed = elapsed >= GETCONFIGVALUE(eAAMPConfig_UTCSyncMinIntervalSec); + } if (shouldSyncOnStartup || intervalElapsed) { mLocalUtcTime = GetNetworkTime(ServerUrl, &http_error, aamp->GetNetworkProxy()); diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 9cd561b3a..a4abc6f8f 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -70,15 +70,39 @@ struct ProfileInfo /** * @struct TimeSyncClient * - * @brief this class defines state associated with periodic time server synchronization. it is used in accordance with configuration after a manifest has been refreshed to determine if elapsed time is sufficient to merit requesting updated time from server. + * @brief Maintains state for periodic synchronization of the local clock + * with a remote UTC time server, used in DASH manifest processing. * - * The constructor is used to initialize lastSync with current time. -*/ + * This struct tracks the last successful synchronization time and the + * cached offset between the local system clock and the server's UTC time. + * It supports logic to determine when a new synchronization request should + * be made based on elapsed time and configuration. + * + * Members: + * - lastSync: Timestamp (milliseconds since epoch) of the last successful sync. + * - lastOffset: Cached time delta (in seconds) between local and server time. + * - hasSynced: Flag indicating whether at least one successful sync has occurred. + */ struct TimeSyncClient { - long long lastSync; /**< UTC time in milliseconds since epoch when time was last synchronized with time server */ - double lastOffset; /**< current delta (seconds) between local and server time */ - bool hasSynced; /**< true if time has been synchronized at least once */ + /** + * @brief UTC time in milliseconds since epoch when time was last synchronized with time server. + */ + long long lastSync; + + /** + * @brief Current delta (seconds) between local and server time. + */ + double lastOffset; + + /** + * @brief True if time has been synchronized at least once. + */ + bool hasSynced; + + /** + * @brief Constructor initializes lastSync with current time and resets other members. + */ TimeSyncClient(); }; From 7d42196c38024de1769ff0f0970e42bc78951e29 Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 16:30:37 -0500 Subject: [PATCH 07/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index d05d80db5..cf5247a14 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4468,8 +4468,7 @@ void StreamAbstractionAAMP_MPD::FindPeriodGapsAndReport() } } -TimeSyncClient::TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) -{}; +TimeSyncClient::TimeSyncClient(): lastSync(aamp_GetCurrentTimeMS()), lastOffset(0), hasSynced(false) {} /** * @brief Read UTCTiming _element_ @@ -4528,7 +4527,14 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) } else { - AAMPLOG_WARN("Failed to read timeServer [%s] RetCode[%d]",ServerUrl.c_str(),http_error); + if (!mTimeSyncClient.hasSynced) + { + AAMPLOG_ERR("Failed initial read of timeServer [%s] RetCode[%d]", ServerUrl.c_str(), http_error); + } + else + { + AAMPLOG_WARN("Failed to refresh timeServer [%s] RetCode[%d]", ServerUrl.c_str(), http_error); + } } } else if(mTimeSyncClient.hasSynced) From 517d7324b7fb1fab6084b23653f1a4203d27fc8e Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 17:43:17 -0500 Subject: [PATCH 08/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AampConfig.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AampConfig.cpp b/AampConfig.cpp index ba1943325..b51eab335 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -470,9 +470,10 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON {DEFAULT_PROGRESS_LOGGING_DIVISOR,"progressLoggingDivisor",eAAMPConfig_ProgressLoggingDivisor,false}, {DEFAULT_MONITOR_AV_REPORTING_INTERVAL, "monitorAVReportingInterval", eAAMPConfig_MonitorAVReportingInterval, false}, {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, - // add new entries here - // aliases, kept for backwards compatibility + // Add new integer config entries above this line, before the aliases section. + // + // Aliases, kept for backwards compatibility {DEFAULT_INIT_BITRATE,"defaultBitrate",eAAMPConfig_DefaultBitrate,true }, {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true } }; From 12f9951bad53dea79a10586c379cc5460607db92 Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 17:52:46 -0500 Subject: [PATCH 09/15] Reason for Change: more nitpicks Signed-off-by: Philip Stroffolino --- AampConfig.cpp | 6 +++--- AampDefine.h | 2 +- fragmentcollector_mpd.h | 22 +++------------------- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/AampConfig.cpp b/AampConfig.cpp index b51eab335..406f754a3 100644 --- a/AampConfig.cpp +++ b/AampConfig.cpp @@ -369,7 +369,7 @@ static const ConfigLookupEntryBool mConfigLookupTableBool[AAMPCONFIG_BOOL_COUNT] {false, "useFireboltSDK", eAAMPConfig_UseFireboltSDK, false}, {true, "enableChunkInjection", eAAMPConfig_EnableChunkInjection, true}, {false, "debugChunkTransfer", eAAMPConfig_DebugChunkTransfer, false}, - {true, "utcSyncOnStartup", eAAMPConfig_UTCSyncOnStartup, true} + {true, "utcSyncOnStartup", eAAMPConfig_UTCSyncOnStartup, true}, }; #define CONFIG_INT_ALIAS_COUNT 2 @@ -469,13 +469,13 @@ static const ConfigLookupEntryInt mConfigLookupTableInt[AAMPCONFIG_INT_COUNT+CON {DEFAULT_MONITOR_AV_JUMP_THRESHOLD_MS,"monitorAVJumpThreshold",eAAMPConfig_MonitorAVJumpThreshold,true,eCONFIG_RANGE_MONITOR_AVSYNC_JUMP_THRESHOLD }, {DEFAULT_PROGRESS_LOGGING_DIVISOR,"progressLoggingDivisor",eAAMPConfig_ProgressLoggingDivisor,false}, {DEFAULT_MONITOR_AV_REPORTING_INTERVAL, "monitorAVReportingInterval", eAAMPConfig_MonitorAVReportingInterval, false}, - {DEFAULT_UTC_SYNC_MIN_INTERVAL,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, + {DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC,"utcSyncMinIntervalSec",eAAMPConfig_UTCSyncMinIntervalSec,true }, // Add new integer config entries above this line, before the aliases section. // // Aliases, kept for backwards compatibility {DEFAULT_INIT_BITRATE,"defaultBitrate",eAAMPConfig_DefaultBitrate,true }, - {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true } + {DEFAULT_INIT_BITRATE_4K,"defaultBitrate4K",eAAMPConfig_DefaultBitrate4K,true }, }; /** diff --git a/AampDefine.h b/AampDefine.h index 813ee646d..d97b3b839 100644 --- a/AampDefine.h +++ b/AampDefine.h @@ -143,7 +143,7 @@ #define DEFAULT_MONITOR_AV_JUMP_THRESHOLD_MS 100 /**< default jump threshold to MonitorAV reporting */ #define DEFAULT_MAX_DOWNLOAD_BUFFER 10 /**< Default maximum download buffer in seconds, this can be used to limit player download job scheduling for DASH */ #define DEFAULT_MONITOR_AV_REPORTING_INTERVAL 1000 /**< time interval in ms for MonitorAV reporting */ -#define DEFAULT_UTC_SYNC_MIN_INTERVAL 60 /**< Minimum interval between sync attempts */ +#define DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC 60 /**< Minimum interval between sync attempts */ // We can enable the following once we have a thread monitoring video PTS progress and triggering subtec clock fast update when we detect video freeze. Disabled it for now for brute force fast refresh.. diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index a4abc6f8f..2e731deb1 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -77,28 +77,12 @@ struct ProfileInfo * cached offset between the local system clock and the server's UTC time. * It supports logic to determine when a new synchronization request should * be made based on elapsed time and configuration. - * - * Members: - * - lastSync: Timestamp (milliseconds since epoch) of the last successful sync. - * - lastOffset: Cached time delta (in seconds) between local and server time. - * - hasSynced: Flag indicating whether at least one successful sync has occurred. */ struct TimeSyncClient { - /** - * @brief UTC time in milliseconds since epoch when time was last synchronized with time server. - */ - long long lastSync; - - /** - * @brief Current delta (seconds) between local and server time. - */ - double lastOffset; - - /** - * @brief True if time has been synchronized at least once. - */ - bool hasSynced; + long long lastSync; /**< Timestamp (milliseconds since epoch) of the last successful sync.. */ + double lastOffset; /**< Cached time delta (in seconds) between local and server time. */ + bool hasSynced; /**< Flag indicating whether at least one successful sync has occurred. */ /** * @brief Constructor initializes lastSync with current time and resets other members. From 4301e0e02ca52e9b892ccdfe066e862926af9ebe Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 21:33:11 -0500 Subject: [PATCH 10/15] Reason for Change: fix l1tests Signed-off-by: Philip Stroffolino --- test/utests/fakes/FakeFragmentCollector_MPD.cpp | 4 +++- .../tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test/utests/fakes/FakeFragmentCollector_MPD.cpp b/test/utests/fakes/FakeFragmentCollector_MPD.cpp index 0e0c8bd4d..08409311c 100644 --- a/test/utests/fakes/FakeFragmentCollector_MPD.cpp +++ b/test/utests/fakes/FakeFragmentCollector_MPD.cpp @@ -20,6 +20,8 @@ #include "fragmentcollector_mpd.h" #include "MockStreamAbstractionAAMP_MPD.h" +TimeSyncClient::TimeSyncClient(){} + MockStreamAbstractionAAMP_MPD *g_mockStreamAbstractionAAMP_MPD = nullptr; StreamAbstractionAAMP_MPD::StreamAbstractionAAMP_MPD(class PrivateInstanceAAMP *aamp,double seek_pos, float rate, id3_callback_t id3Handler) @@ -304,4 +306,4 @@ void StreamAbstractionAAMP_MPD::clearFirstPTS(void) bool StreamAbstractionAAMP_MPD::ExtractAndAddSubtitleMediaHeader() { return false; -} \ No newline at end of file +} diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index 8b0f5c8d0..70ba4b804 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -104,6 +104,7 @@ class FunctionalTestsBase {eAAMPConfig_useRialtoSink, false}, {eAAMPConfig_GstSubtecEnabled, false}, {eAAMPConfig_UseMp4Demux, false}, + {eAAMPConfig_UTCSyncOnStartup, true}, }; BoolConfigSettings mBoolConfigSettings; @@ -121,7 +122,8 @@ class FunctionalTestsBase {eAAMPConfig_VODTrickPlayFPS, TRICKPLAY_VOD_PLAYBACK_FPS}, {eAAMPConfig_ABRBufferCounter, DEFAULT_ABR_BUFFER_COUNTER}, {eAAMPConfig_MaxDownloadBuffer, DEFAULT_MAX_DOWNLOAD_BUFFER}, - {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK} + {eAAMPConfig_MaxFragmentChunkCached, DEFAULT_CACHED_FRAGMENT_CHUNKS_PER_TRACK}, + {eAAMPConfig_UTCSyncMinIntervalSec, DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC} }; IntConfigSettings mIntConfigSettings; From b2d00e238f0082aff96ae533b4cc64e0bff1143b Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 21:34:41 -0500 Subject: [PATCH 11/15] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 2 +- fragmentcollector_mpd.h | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index cf5247a14..07f7e2df2 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4529,7 +4529,7 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) { if (!mTimeSyncClient.hasSynced) { - AAMPLOG_ERR("Failed initial read of timeServer [%s] RetCode[%d]", ServerUrl.c_str(), http_error); + AAMPLOG_ERR("Failed timeServer sync on startup [%s] RetCode[%d]", ServerUrl.c_str(), http_error); } else { diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index 2e731deb1..a0528d53a 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -1267,6 +1267,18 @@ class StreamAbstractionAAMP_MPD : public StreamAbstractionAAMP bool mShortAdOffsetCalc; AampTime mNextPts; /*For PTS restamping*/ bool mIsFinalFirstPTS; /**< Flag to indicate if the first PTS is final or not */ + /** + * @brief Client used for server time synchronization. + * + * @note TimeSyncClient maintains internal mutable state (e.g. lastSync, + * lastOffset, hasSynced) and is not internally thread-safe. + * All accesses to mTimeSyncClient (including via FindServerUTCTime + * in the implementation) are expected to be serialized by the + * caller. By design, this member is accessed only from the + * manifest-processing thread and MUST NOT be used concurrently + * from multiple threads without additional external + * synchronization. + */ TimeSyncClient mTimeSyncClient; }; From e4cdd352c255f35d430222d9e3a7a79309021e28 Mon Sep 17 00:00:00 2001 From: Philip Stroffolino Date: Wed, 14 Jan 2026 22:29:12 -0500 Subject: [PATCH 12/15] Reason for Test: new utests Signed-off-by: Philip Stroffolino --- .../FunctionalTests.cpp | 1256 ++++++++++------- 1 file changed, 770 insertions(+), 486 deletions(-) diff --git a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp index 70ba4b804..eff635806 100644 --- a/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp +++ b/test/utests/tests/StreamAbstractionAAMP_MPD/FunctionalTests.cpp @@ -26,6 +26,7 @@ #include "AampLogManager.h" #include "fragmentcollector_mpd.h" #include "MediaStreamContext.h" +#include "AampMPDUtils.h" #include "MockAampConfig.h" #include "MockAampUtils.h" #include "MockAampGstPlayer.h" @@ -306,7 +307,7 @@ R"( ManifestDownloadResponsePtr response = MakeSharedManifestDownloadResponsePtr(); response->mMPDStatus = AAMPStatusType::eAAMPSTATUS_MANIFEST_DOWNLOAD_ERROR; response->mMPDDownloadResponse->iHttpRetValue = curlTimeoutFailureReason; - response->mMPDDownloadResponse->sEffectiveUrl = mManifestUrl; + response->mMPDDownloadResponse->sEffectiveUrl = mManifestUrl; response->mMPDDownloadResponse->mDownloadData.assign((uint8_t*)test_manifest, (uint8_t*)(test_manifest + strlen(test_manifest))); GetMPDFromManifest(response); mResponse = response; @@ -700,6 +701,7 @@ class StreamAbstractionAAMP_MPDTest : public FunctionalTestsBase, // Set up your objects before each test case mPrivateInstanceAAMP = new PrivateInstanceAAMP(); g_mockPrivateInstanceAAMP = new NiceMock(); + g_mockAampConfig = new NiceMock(); mStreamAbstractionAAMP_MPD = new TestableStreamAbstractionAAMP_MPD(mPrivateInstanceAAMP, 0.0, 1.0); g_MockPrivateCDAIObjectMPD = new NiceMock(); @@ -738,6 +740,9 @@ class StreamAbstractionAAMP_MPDTest : public FunctionalTestsBase, delete g_mockABRManager; g_mockABRManager = nullptr; + + delete g_mockAampConfig; + g_mockAampConfig = nullptr; } }; @@ -1567,8 +1572,8 @@ R"( - - + + @@ -2876,7 +2881,7 @@ R"( - + @@ -2983,7 +2988,7 @@ TEST_F(FunctionalTests, GetThumbnailRangeDataTest1) TEST_F(FunctionalTests, FindServerUTCTimeTest) { - // Manifest with UTCTiming + // Manifest with UTCTiming static const char *manifest = R"( @@ -3018,6 +3023,285 @@ TEST_F(FunctionalTests, FindServerUTCTimeTest) EXPECT_EQ(status, eAAMPSTATUS_OK); } +/** + * @brief Test UTC sync on first call (startup behavior) + * + * Verifies that FindServerUTCTime performs network sync on the first call + * when UTCSyncOnStartup is enabled and no previous sync has occurred. + */ +TEST_F(StreamAbstractionAAMP_MPDTest, FindServerUTCTime_SyncOnStartup) +{ + GTEST_SKIP(); + + // Setup mock config for startup sync enabled + EXPECT_CALL(*g_mockAampConfig, IsConfigSet(eAAMPConfig_UTCSyncOnStartup)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_UTCSyncMinIntervalSec)) + .WillRepeatedly(Return(DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC)); + + // Setup time mocks + const long long startTimeMS = 1000000000LL; // Arbitrary start time + EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) + .WillRepeatedly(Return(startTimeMS)); + + // Expect network call on first sync + const double serverTime = 1000000.5; // Server UTC time + EXPECT_CALL(*g_mockAampUtils, GetNetworkTime(testing::_, testing::_, testing::_)) + .WillOnce(Return(serverTime)); + + // Create manifest XML with UTCTiming + const char *manifestXml = + R"( + + + )"; + + // Parse manifest to get root node + xmlTextReaderPtr reader = xmlReaderForMemory(manifestXml, strlen(manifestXml), NULL, NULL, 0); + ASSERT_NE(reader, nullptr); + ASSERT_TRUE(xmlTextReaderRead(reader)); + + Node *rootNode = MPDProcessNode(&reader, "http://example.com/manifest.mpd"); + ASSERT_NE(rootNode, nullptr); + + // Call FindServerUTCTime - should perform network sync + bool result = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + + // Verify sync occurred + EXPECT_TRUE(result); + + // Cleanup + delete rootNode; + xmlFreeTextReader(reader); +} + +/** + * @brief Test UTC sync is skipped when minimum interval hasn't elapsed + * + * Verifies that FindServerUTCTime skips network sync when called again + * before the minimum interval has elapsed, and uses cached offset instead. + */ +TEST_F(StreamAbstractionAAMP_MPDTest, FindServerUTCTime_SkipSyncBeforeInterval) +{ + GTEST_SKIP(); + + // Setup mock config + EXPECT_CALL(*g_mockAampConfig, IsConfigSet(eAAMPConfig_UTCSyncOnStartup)) + .WillRepeatedly(Return(true)); + const int minIntervalSec = 60; // 60 seconds minimum interval + EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_UTCSyncMinIntervalSec)) + .WillRepeatedly(Return(minIntervalSec)); + + // Setup time progression + const long long startTimeMS = 1000000000LL; + const long long secondCallTimeMS = startTimeMS + 30000LL; // 30 seconds later (less than interval) + + // Mock time calls in sequence + EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) + .WillOnce(Return(startTimeMS)) // First call: initial check + .WillOnce(Return(startTimeMS)) // First call: record sync time + .WillOnce(Return(secondCallTimeMS)); // Second call: elapsed time check + + // Expect network call only on first sync + const double serverTime = 1000000.5; + EXPECT_CALL(*g_mockAampUtils, GetNetworkTime(testing::_, testing::_, testing::_)) + .WillOnce(Return(serverTime)); // First call only + + // Create manifest XML + const char *manifestXml = + R"( + + + )"; + + xmlTextReaderPtr reader = xmlReaderForMemory(manifestXml, strlen(manifestXml), NULL, NULL, 0); + ASSERT_NE(reader, nullptr); + ASSERT_TRUE(xmlTextReaderRead(reader)); + + Node *rootNode = MPDProcessNode(&reader, "http://example.com/manifest.mpd"); + ASSERT_NE(rootNode, nullptr); + + // First call - should sync + bool result1 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result1); + + // Second call before interval - should use cached value, not sync + bool result2 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result2); // Should still return true using cached offset + + // Cleanup + delete rootNode; + xmlFreeTextReader(reader); +} + +/** + * @brief Test UTC sync occurs when minimum interval has elapsed + * + * Verifies that FindServerUTCTime performs a new network sync when called + * after the minimum interval has elapsed since the last sync. + */ +TEST_F(StreamAbstractionAAMP_MPDTest, FindServerUTCTime_SyncAfterInterval) +{ + GTEST_SKIP(); + + // Setup mock config + EXPECT_CALL(*g_mockAampConfig, IsConfigSet(eAAMPConfig_UTCSyncOnStartup)) + .WillRepeatedly(Return(true)); + const int minIntervalSec = 60; // 60 seconds minimum interval + EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_UTCSyncMinIntervalSec)) + .WillRepeatedly(Return(minIntervalSec)); + + // Setup time progression + const long long startTimeMS = 1000000000LL; + const long long secondCallTimeMS = startTimeMS + 61000LL; // 61 seconds later (more than interval) + + // Mock time calls in sequence + EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) + .WillOnce(Return(startTimeMS)) // First call: initial check + .WillOnce(Return(startTimeMS)) // First call: record sync time + .WillOnce(Return(secondCallTimeMS)) // Second call: elapsed time check + .WillOnce(Return(secondCallTimeMS)); // Second call: record sync time + + // Expect network call on both syncs + const double serverTime1 = 1000000.5; + const double serverTime2 = 1000061.5; + EXPECT_CALL(*g_mockAampUtils, GetNetworkTime(testing::_, testing::_, testing::_)) + .WillOnce(Return(serverTime1)) // First sync + .WillOnce(Return(serverTime2)); // Second sync after interval + + // Create manifest XML + const char *manifestXml = + R"( + + + )"; + + xmlTextReaderPtr reader = xmlReaderForMemory(manifestXml, strlen(manifestXml), NULL, NULL, 0); + ASSERT_NE(reader, nullptr); + ASSERT_TRUE(xmlTextReaderRead(reader)); + + Node *rootNode = MPDProcessNode(&reader, "http://example.com/manifest.mpd"); + ASSERT_NE(rootNode, nullptr); + + // First call - should sync + bool result1 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result1); + + // Second call after interval - should sync again + bool result2 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result2); + + // Cleanup + delete rootNode; + xmlFreeTextReader(reader); +} + +/** + * @brief Test UTC sync with cached offset when sync is skipped + * + * Verifies that when sync is skipped due to interval not elapsed, + * the cached offset value is used and the function still returns true. + */ +TEST_F(StreamAbstractionAAMP_MPDTest, FindServerUTCTime_UseCachedOffset) +{ + GTEST_SKIP(); + + // Setup mock config - startup sync disabled after first sync + EXPECT_CALL(*g_mockAampConfig, IsConfigSet(eAAMPConfig_UTCSyncOnStartup)) + .WillRepeatedly(Return(true)); + const int minIntervalSec = 300; // 5 minutes + EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_UTCSyncMinIntervalSec)) + .WillRepeatedly(Return(minIntervalSec)); + + // Setup time - second call is well before interval + const long long startTimeMS = 1000000000LL; + const long long secondCallTimeMS = startTimeMS + 10000LL; // 10 seconds later + + // Mock time calls in sequence + EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) + .WillOnce(Return(startTimeMS)) // First call: initial check + .WillOnce(Return(startTimeMS)) // First call: record sync time + .WillOnce(Return(secondCallTimeMS)); // Second call: elapsed time check + + // Only first call should trigger network sync + const double serverTime = 1000000.5; + EXPECT_CALL(*g_mockAampUtils, GetNetworkTime(testing::_, testing::_, testing::_)) + .WillOnce(Return(serverTime)); + + // Create manifest XML + const char *manifestXml = + R"( + + + )"; + + xmlTextReaderPtr reader = xmlReaderForMemory(manifestXml, strlen(manifestXml), NULL, NULL, 0); + ASSERT_NE(reader, nullptr); + ASSERT_TRUE(xmlTextReaderRead(reader)); + + Node *rootNode = MPDProcessNode(&reader, "http://example.com/manifest.mpd"); + ASSERT_NE(rootNode, nullptr); + + // First call - performs sync + bool result1 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result1); + + // Second call - uses cached offset, still returns true + bool result2 = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_TRUE(result2); + + // Cleanup + delete rootNode; + xmlFreeTextReader(reader); +} + +/** + * @brief Test UTC sync behavior with UTCSyncOnStartup disabled + * + * Verifies that when UTCSyncOnStartup is disabled and no previous sync + * has occurred, the function does not perform sync and returns false. + */ +TEST_F(StreamAbstractionAAMP_MPDTest, FindServerUTCTime_NoSyncWhenStartupDisabled) +{ + GTEST_SKIP(); + + // Setup mock config - startup sync disabled + EXPECT_CALL(*g_mockAampConfig, IsConfigSet(eAAMPConfig_UTCSyncOnStartup)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*g_mockAampConfig, GetConfigValue(eAAMPConfig_UTCSyncMinIntervalSec)) + .WillRepeatedly(Return(DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC)); + + const long long startTimeMS = 1000000000LL; + EXPECT_CALL(*g_mockAampUtils, aamp_GetCurrentTimeMS()) + .WillRepeatedly(Return(startTimeMS)); + + // No network call should occur + EXPECT_CALL(*g_mockAampUtils, GetNetworkTime(testing::_, testing::_, testing::_)) + .Times(0); + + // Create manifest XML + const char *manifestXml = + R"( + + + )"; + + xmlTextReaderPtr reader = xmlReaderForMemory(manifestXml, strlen(manifestXml), NULL, NULL, 0); + ASSERT_NE(reader, nullptr); + ASSERT_TRUE(xmlTextReaderRead(reader)); + + Node *rootNode = MPDProcessNode(&reader, "http://example.com/manifest.mpd"); + ASSERT_NE(rootNode, nullptr); + + // Call should not sync and return false (no sync occurred) + bool result = mStreamAbstractionAAMP_MPD->CallFindServerUTCTime(rootNode); + EXPECT_FALSE(result); + + // Cleanup + delete rootNode; + xmlFreeTextReader(reader); +} + TEST_F(FunctionalTests, GetFirstPTS) { static const char *manifest = @@ -3792,7 +4076,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, FetchDashManifest_ConnectTimeout_WithManif /** * @brief Test GetFirstValidCurrMPDPeriod with first period having content. - * + * * Scenario: Initial manifest refresh where first period is not empty. * Expected: First period should be returned as valid period. */ @@ -3835,7 +4119,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithNonEmptyFirstPeriod /** * @brief Test GetFirstValidCurrMPDPeriod with empty first period. - * + * * Scenario: Manifest refresh where first period becomes empty (SCTE35 ad period). * Expected: Second non-empty period should be returned as valid period. */ @@ -3878,7 +4162,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithEmptyFirstPeriod) /** * @brief Test with multiple empty periods at the start. - * + * * Scenario: Multiple empty periods (ads) followed by content. * Expected: First content period should be returned. */ @@ -3925,7 +4209,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithMultipleEmptyPeriod /** * @brief Test with zero duration period. - * + * * Scenario: Period with zero duration should be skipped even if not marked as empty. * Expected: Period with non-zero duration should be returned. */ @@ -3966,7 +4250,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithZeroDurationPeriod) /** * @brief Test GetFirstValidCurrMPDPeriod with all empty periods. - * + * * Scenario: Edge case where all periods are empty. * Expected: Should return the first period even if empty (fallback behavior). */ @@ -3998,7 +4282,7 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithAllEmptyPeriods) /** * @brief Test GetFirstValidCurrMPDPeriod with empty vector. - * + * * Scenario: Edge case with no periods. * Expected: Should return default initialized period. */ @@ -4017,603 +4301,603 @@ TEST_F(StreamAbstractionAAMP_MPDTest, GetFirstValidPeriodWithEmptyVector) // Test case to verify seeking behavior with VOD content, with start time, without duration and PTO greater than timeline TEST_F(FunctionalTests, L1_VOD_WithStartTime_NoDuration_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - + xmlns:cenc="urn:mpeg:cenc:2013" + type="static" minBufferTime="PT1.5S" mediaPresentationDuration="PT10M0S" profiles="urn:mpeg:dash:profile:isoff-live:2011"> + + + + + + + + + + + + + + )"; - // Enable mid-fragment seek + // Enable mid-fragment seek mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; - std::string fragmentUrl = std::string(TEST_BASE_URL) + std::string("out-v1.dash"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - .WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + std::string fragmentUrl = std::string(TEST_BASE_URL) + std::string("out-v1.dash"); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - double seekPosition = 0.0; - int rate = 1; - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 0.0; + int rate = 1; + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - // After seek to 0, stream position should be 0 + // After seek to 0, stream position should be 0 double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); cout<<"Actual Position: "< - - - - - - - - - - - - - - + xmlns:cenc="urn:mpeg:cenc:2013" + type="dynamic" + availabilityStartTime="2023-01-01T00:00:00Z" + minBufferTime="PT1.5S" + timeShiftBufferDepth="PT5M" + suggestedPresentationDelay="PT10S" + mediaPresentationDuration="PT10M0S" + profiles="urn:mpeg:dash:profile:isoff-live:2011"> + + + + + + + + + + + + + + )"; - // Enable mid-fragment seek - mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; + // Enable mid-fragment seek + mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; - std::string fragmentUrl = std::string(TEST_BASE_URL) + "out-v1.dash"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - .WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + std::string fragmentUrl = std::string(TEST_BASE_URL) + "out-v1.dash"; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - double seekPosition = 0.0; - int rate = 1; - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 0.0; + int rate = 1; + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); double expectedPosition = 1.6725312e+09;//AvailabilityStartTime as it is a liveStream - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_NEAR(actualPosition, expectedPosition, 0.001); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + EXPECT_NEAR(actualPosition, expectedPosition, 0.001); } // Test case to verify seeking behavior with VOD period, with out duration, with out start and PTO greater than timeline TEST_F(FunctionalTests, L1_VOD_Period_NoDuration_NoStart_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - + xmlns:cenc="urn:mpeg:cenc:2013" + type="static" minBufferTime="PT1.5S" mediaPresentationDuration="PT10M0S" profiles="urn:mpeg:dash:profile:isoff-live:2011"> + + + + + + + + + + + + + + )"; - // Enable mid-fragment seek - mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; + // Enable mid-fragment seek + mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; - std::string fragmentUrl = std::string(TEST_BASE_URL) + std::string("out-v1.dash"); - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - .WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + std::string fragmentUrl = std::string(TEST_BASE_URL) + std::string("out-v1.dash"); + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - double seekPosition = 0.0; - int rate = 1; - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 0.0; + int rate = 1; + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - // After seek to 0, stream position should be 0 - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - cout<<"Actual Position: "<GetStreamPosition(); + cout<<"Actual Position: "< - - - - - - - - - - - - - - + xmlns:cenc="urn:mpeg:cenc:2013" + type="dynamic" + availabilityStartTime="2023-01-01T00:00:00Z" + minBufferTime="PT1.5S" + timeShiftBufferDepth="PT5M" + suggestedPresentationDelay="PT10S" + mediaPresentationDuration="PT10M0S" + profiles="urn:mpeg:dash:profile:isoff-live:2011"> + + + + + + + + + + + + + + )"; - // Enable mid-fragment seek - mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; + // Enable mid-fragment seek + mBoolConfigSettings[eAAMPConfig_MidFragmentSeek] = true; - std::string fragmentUrl = std::string(TEST_BASE_URL) + "out-v1.dash"; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) - .WillOnce(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + std::string fragmentUrl = std::string(TEST_BASE_URL) + "out-v1.dash"; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(fragmentUrl, _, _, _, _, true, _, _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - double seekPosition = 0.0; - int rate = 1; - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); - double expectedPosition = 1.6725312e+09;//AvailabilityStartTime as it is a liveStream + double seekPosition = 0.0; + int rate = 1; + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); + double expectedPosition = 1.6725312e+09;//AvailabilityStartTime as it is a liveStream - // After seek to 0, verify stream position is near the expected availabilityStartTime - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + // After seek to 0, verify stream position is near the expected availabilityStartTime + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_NEAR(actualPosition, expectedPosition, 0.001); + EXPECT_NEAR(actualPosition, expectedPosition, 0.001); } // Test case to verify seeking behavior with three periods, with out start times, with out durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_NoStartTimes_NoDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 1; - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 1; + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_EQ(actualPosition, seekPosition); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + EXPECT_EQ(actualPosition, seekPosition); } // Test case to verify seeking behavior with three periods, with out start times, with durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_NoStartTimes_WithDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 15 * 60; // Seek to 15 minutes (middle of period p1) - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 15 * 60; // Seek to 15 minutes (middle of period p1) + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_EQ(actualPosition, seekPosition); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + EXPECT_EQ(actualPosition, seekPosition); } // Test case to verify seeking behavior with three periods, with start time and durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_WithStartTimesAndDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 0; - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 0; + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_EQ(actualPosition, seekPosition); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + EXPECT_EQ(actualPosition, seekPosition); } // Test case to verify seeking behavior with three periods, with start times, with out durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_WithStartTimesNoDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 10 * 60; // Seek to 10 minutes (middle of period p1) - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); + double seekPosition = 10 * 60; // Seek to 10 minutes (middle of period p1) + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - EXPECT_EQ(actualPosition, seekPosition); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + EXPECT_EQ(actualPosition, seekPosition); } // Test case to verify seeking behavior for Live content with three periods, with out start times, with out durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_Live_NoStartTimes_NoDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 1; - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); - EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); + double seekPosition = 1; + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); + EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); } // Test case to verify seeking behavior for Live content with three periods, with out start times, with durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_Live_NoStartTimes_WithDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 15 * 60; // Seek to 15 minutes (middle of period p1) - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); - EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); + double seekPosition = 15 * 60; // Seek to 15 minutes (middle of period p1) + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); + EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); } // Test case to verify seeking behavior for Live content with three periods, with start times and durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_Live_WithStartTimesAndDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 0; - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); - EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); + double seekPosition = 0; + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); + EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); } // Test case to verify seeking behavior for Live content with three periods, with start times, with out durations, and PTO greater than timeline TEST_F(FunctionalTests, L1_ThreePeriods_Live_WithStartTimesNoDurations_PTOGreaterThanTimeline) { - AAMPStatusType status; - static const char *manifest = + AAMPStatusType status; + static const char *manifest = R"( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; - double seekPosition = 10 * 60; // Seek to 10 minutes (middle of period p1) - int rate = 1; - EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); - status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); - EXPECT_EQ(status, eAAMPSTATUS_OK); - double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); - double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); - EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); + double seekPosition = 10 * 60; // Seek to 10 minutes (middle of period p1) + int rate = 1; + EXPECT_CALL(*g_mockMediaStreamContext, CacheFragment(_, _, _, _, _, true, _, _, _)) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*g_mockPrivateInstanceAAMP, SetLLDashChunkMode(_)); + status = InitializeMPD(manifest, TuneType::eTUNETYPE_SEEK, seekPosition, rate, true); + EXPECT_EQ(status, eAAMPSTATUS_OK); + double actualPosition = mStreamAbstractionAAMP_MPD->GetStreamPosition(); + double availabilityStartTime = ISO8601DateTimeToUTCSeconds("2025-11-15T00:00:00Z"); + EXPECT_EQ(actualPosition, availabilityStartTime + seekPosition); } From 0d6425aa71ac20f4e4aa8650acaeb7e5aebd27a9 Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 22:55:46 -0500 Subject: [PATCH 13/15] Update fragmentcollector_mpd.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fragmentcollector_mpd.h b/fragmentcollector_mpd.h index a0528d53a..1f094f0df 100644 --- a/fragmentcollector_mpd.h +++ b/fragmentcollector_mpd.h @@ -80,7 +80,7 @@ struct ProfileInfo */ struct TimeSyncClient { - long long lastSync; /**< Timestamp (milliseconds since epoch) of the last successful sync.. */ + long long lastSync; /**< Timestamp (milliseconds since epoch) of the last successful sync. */ double lastOffset; /**< Cached time delta (in seconds) between local and server time. */ bool hasSynced; /**< Flag indicating whether at least one successful sync has occurred. */ From b3faaf083f20e324ffb6a7a9c2c62fe2c071eccc Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 22:57:28 -0500 Subject: [PATCH 14/15] Update AampDefine.h Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AampDefine.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/AampDefine.h b/AampDefine.h index d97b3b839..744240033 100644 --- a/AampDefine.h +++ b/AampDefine.h @@ -144,8 +144,6 @@ #define DEFAULT_MAX_DOWNLOAD_BUFFER 10 /**< Default maximum download buffer in seconds, this can be used to limit player download job scheduling for DASH */ #define DEFAULT_MONITOR_AV_REPORTING_INTERVAL 1000 /**< time interval in ms for MonitorAV reporting */ #define DEFAULT_UTC_SYNC_MIN_INTERVAL_SEC 60 /**< Minimum interval between sync attempts */ - - // We can enable the following once we have a thread monitoring video PTS progress and triggering subtec clock fast update when we detect video freeze. Disabled it for now for brute force fast refresh.. //#define SUBTEC_VARIABLE_CLOCK_UPDATE_RATE /* enable this to make the clock update rate dynamic*/ #ifdef SUBTEC_VARIABLE_CLOCK_UPDATE_RATE From 79ac33a3d15da537d6920f2e2aa96f11d2983587 Mon Sep 17 00:00:00 2001 From: pstroffolino Date: Wed, 14 Jan 2026 22:58:26 -0500 Subject: [PATCH 15/15] Update fragmentcollector_mpd.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- fragmentcollector_mpd.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fragmentcollector_mpd.cpp b/fragmentcollector_mpd.cpp index 07f7e2df2..aeff79293 100644 --- a/fragmentcollector_mpd.cpp +++ b/fragmentcollector_mpd.cpp @@ -4537,7 +4537,7 @@ bool StreamAbstractionAAMP_MPD::FindServerUTCTime(Node* root) } } } - else if(mTimeSyncClient.hasSynced) + else if (mTimeSyncClient.hasSynced) { mDeltaTime = mTimeSyncClient.lastOffset; hasServerUtcTime = true;