Skip to content

Conversation

@jfagunde
Copy link
Contributor

Reason for Change: Avoid potential edge case and make code correct

Summary of Changes:

  • Use timescale of fragment injected in ProcessFragmentChunk()
  • Verify timescale used by InjectFragment() in L1 tests

Test Procedure: L1 tests should pass and no AAMP TSB + CDA regressions

Risk: Low

Reason for Change: Avoid potential edge case and make code correct

Summary of Changes:
* Use timescale of fragment injected in ProcessFragmentChunk()
* Verify timescale used by InjectFragment() in L1 tests

Test Procedure: L1 tests should pass and no AAMP TSB + CDA regressions

Risk: Low
@jfagunde jfagunde requested a review from a team as a code owner January 23, 2026 10:14
Copilot AI review requested due to automatic review settings January 23, 2026 10:14
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 pull request fixes a timing correctness issue in the AAMP player's fragment processing logic. The fix ensures that ProcessFragmentChunk() uses the timescale associated with the specific fragment being injected, rather than relying on global timescales. This is critical for Time-Shift Buffer (TSB) scenarios where segments from different sources (e.g., ads vs. base content) may have different timescales.

Changes:

  • Modified ProcessFragmentChunk() in streamabstraction.cpp to prioritize the cached fragment's timescale over global timescales
  • Added comprehensive L1 parameterized tests to verify correct timescale usage in both primary and fallback scenarios
  • Improved error logging when fallback to global timescale is needed

Reviewed changes

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

File Description
streamabstraction.cpp Updated ProcessFragmentChunk() to use cachedFragment->timeScale as the primary source, with fallback to global timescales when it's 0
test/utests/tests/MediaTrackTests/MediaTrackTests.cpp Added parameterized test suite with 5 test cases covering different timescale scenarios (video, audio, 100ns units, and fallback cases)

Copy link
Contributor

@DomSyna DomSyna left a comment

Choose a reason for hiding this comment

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

Not looked at tests yet, just thought better to iron out code changes as they may impact tests

Copilot AI review requested due to automatic review settings January 23, 2026 11:49
@jfagunde jfagunde marked this pull request as draft January 23, 2026 11:51
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 2 changed files in this pull request and generated 9 comments.

Comment on lines +868 to +870
// Set up mock to return the global timescale (used as fallback when cached fragment timescale is 0)
EXPECT_CALL(*g_mockPrivateInstanceAAMP, GetVidTimeScale())
.WillRepeatedly(Return(testData.globalTimeScale));
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The comment states "used as fallback when cached fragment timescale is 0" but the production code at streamabstraction.cpp:1040-1043 does NOT implement this fallback. The mock for GetVidTimeScale() is set up but will never be called because the production code returns early when timeScale is 0. This mock setup is misleading and the comment should be corrected.

Copilot uses AI. Check for mistakes.
Comment on lines +927 to +928
{0, 90000, 90000}, // Fragment timescale 0, global 90000 -> use global
{0, 48000, 48000}, // Fragment timescale 0, global 48000 -> use global
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

These test cases will FAIL because they expect ParseChunkData to be called with the global timescale when cachedFragment->timeScale is 0, but the production code at streamabstraction.cpp:1040-1043 returns early without calling ParseChunkData. Either these test cases need to be removed, or the production code needs to implement the fallback behavior as the old code did (using GetVidTimeScale/GetAudTimeScale/GetSubTimeScale based on track type).

Copilot uses AI. Check for mistakes.
AAMPLOG_WARN("[%s] Invalid play context maybe test setup, timeScale=%d", name, timeScale);
}
AAMPLOG_ERR("[%s] Cached fragment timescale is 0, fragment URI: %s", name, cachedFragment->uri.c_str());
return true;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

Returning 'true' (success) when timeScale is 0 is semantically incorrect. The function ProcessFragmentChunk returns true to indicate success in processing a chunk, but in this case the chunk is not processed at all. Consider returning 'false' to indicate failure, or implement the fallback logic to use global timescales as the old code did. This silent failure could mask real issues where fragments legitimately have timeScale set to 0.

Suggested change
return true;
return false;

Copilot uses AI. Check for mistakes.
Comment on lines +845 to +855
* @brief Test that ProcessFragmentChunk uses the cached fragment's timescale
*
* This test verifies that when processing fragment chunks, the timescale from
* the cached fragment is used (if set), rather than falling back to global
* timescales. This is critical for TSB (Time-Shift Buffer) scenarios where:
* - The segment being downloaded at the live edge may be from an ad with one timescale
* - The segment being injected from TSB may be from base content with a different timescale
*
* The test would fail if ProcessFragmentChunk used global timescales instead of the
* cached fragment's timescale, as the ParseChunkData mock expects the exact timescale
* value from the cached fragment.
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The test documentation states "rather than falling back to global timescales", but the test data at lines 927-928 actually expects fallback to global timescales when cachedFragment->timeScale is 0. This is contradictory and confusing. The test intention needs to be clarified: should fallback be supported or not? The PR title and description suggest using the fragment's timescale, which implies no fallback, but the test cases suggest fallback should work.

Suggested change
* @brief Test that ProcessFragmentChunk uses the cached fragment's timescale
*
* This test verifies that when processing fragment chunks, the timescale from
* the cached fragment is used (if set), rather than falling back to global
* timescales. This is critical for TSB (Time-Shift Buffer) scenarios where:
* - The segment being downloaded at the live edge may be from an ad with one timescale
* - The segment being injected from TSB may be from base content with a different timescale
*
* The test would fail if ProcessFragmentChunk used global timescales instead of the
* cached fragment's timescale, as the ParseChunkData mock expects the exact timescale
* value from the cached fragment.
* @brief Test that ProcessFragmentChunk prefers the cached fragment's timescale
*
* This test verifies that when processing fragment chunks, the timescale from
* the cached fragment is used when it is non-zero, and that when the cached
* fragment's timescale is zero, ProcessFragmentChunk falls back to the global
* timescale from the AAMP instance. This is critical for TSB (Time-Shift Buffer)
* scenarios where:
* - The segment being downloaded at the live edge may be from an ad with one timescale
* - The segment being injected from TSB may be from base content with a different timescale
*
* The test would fail if ProcessFragmentChunk did not select the correct effective
* timescale (cached fragment's timescale when non-zero, otherwise the global
* timescale), as the ParseChunkData mock expects the timescale value provided
* by the test parameters.

Copilot uses AI. Check for mistakes.
Comment on lines +1034 to +1043

// Use the timescale stored in the cached fragment, which represents the timescale
// of the segment being injected. This is critical when using TSB, as the segment
// being downloaded at the live edge may have a different timescale (e.g., an ad)
// than the segment being injected from TSB (e.g., base content).
uint32_t timeScale = cachedFragment->timeScale;
if(!timeScale)
{
//FIX-ME-Read from MPD INSTEAD
if(pContext)
{
timeScale = pContext->GetCurrPeriodTimeScale();
if(!timeScale)
{
timeScale = 10000000.0;
AAMPLOG_WARN("[%s] Empty timeScale!!! Using default timeScale=%d", name, timeScale);
}
}
else
{
timeScale = 1000.0;
AAMPLOG_WARN("[%s] Invalid play context maybe test setup, timeScale=%d", name, timeScale);
}
AAMPLOG_ERR("[%s] Cached fragment timescale is 0, fragment URI: %s", name, cachedFragment->uri.c_str());
return true;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The new implementation removes all fallback logic that existed in the old code. The old code had a comprehensive fallback chain: track-type-specific global timescale → period timescale → hardcoded default (10000000 or 1000). While the PR description states the goal is to use the fragment's timescale, removing ALL fallback mechanisms could cause issues if cachedFragment->timeScale is legitimately 0 due to incomplete initialization or parsing errors. Consider at minimum keeping the track-type-specific global timescale fallback (GetVidTimeScale/GetAudTimeScale/GetSubTimeScale) as the first fallback option.

Copilot uses AI. Check for mistakes.
* cached fragment's timescale, as the ParseChunkData mock expects the exact timescale
* value from the cached fragment.
*/
TEST_P(MediaTrackProcessFragmentChunkTimescaleTests, UseCachedFragmentTimescaleInParseChunkData)
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The PR description states "Verify timescale used by InjectFragment() in L1 tests" but the tests are actually for ProcessFragmentChunk(), not InjectFragment(). While InjectFragment() internally calls ProcessFragmentChunk() (when in chunk mode), the test naming and PR description should be consistent about which function is being tested. The test name "UseCachedFragmentTimescaleInParseChunkData" correctly indicates it's testing ProcessFragmentChunk's behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +1042 to +1043
AAMPLOG_ERR("[%s] Cached fragment timescale is 0, fragment URI: %s", name, cachedFragment->uri.c_str());
return true;
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

Critical bug: The code returns early when timeScale is 0 without processing the chunk, but the test cases expect fallback to global timescale. Test cases at MediaTrackTests.cpp:927-928 expect that when cachedFragment->timeScale is 0, the code should fall back to using GetVidTimeScale() (for video) or equivalent global timescales. However, this implementation logs an error and returns true without calling ParseChunkData at all, which means the fragment chunk is silently skipped. Either:

  1. The tests need to be updated to remove the fallback test cases (lines 927-928), OR
  2. The production code needs to implement the fallback behavior by calling GetVidTimeScale()/GetAudTimeScale()/GetSubTimeScale() based on track type when cachedFragment->timeScale is 0, as the old code did.
Suggested change
AAMPLOG_ERR("[%s] Cached fragment timescale is 0, fragment URI: %s", name, cachedFragment->uri.c_str());
return true;
// Fallback to global track-specific timescale when the cached fragment
// timescale is zero. This matches legacy behavior and unit test
// expectations (see MediaTrackTests.cpp).
switch (type)
{
case eTRACK_VIDEO:
timeScale = GetVidTimeScale();
break;
case eTRACK_AUDIO:
timeScale = GetAudTimeScale();
break;
case eTRACK_SUBTITLE:
timeScale = GetSubTimeScale();
break;
default:
timeScale = 0;
break;
}
if (!timeScale)
{
AAMPLOG_ERR("[%s] Cached fragment timescale is 0 and no valid global "
"timescale available, fragment URI: %s", name,
cachedFragment->uri.c_str());
return true;
}
AAMPLOG_WARN("[%s] Cached fragment timescale is 0, using global "
"timescale %u for fragment URI: %s", name, timeScale,
cachedFragment->uri.c_str());

Copilot uses AI. Check for mistakes.
Comment on lines +926 to +928
// When cached fragment timescale is 0, fall back to global timescale
{0, 90000, 90000}, // Fragment timescale 0, global 90000 -> use global
{0, 48000, 48000}, // Fragment timescale 0, global 48000 -> use global
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

This comment suggests fallback behavior is expected and supported, but the production code at streamabstraction.cpp:1040-1043 does not implement fallback - it returns early with an error. This creates confusion about whether timeScale=0 should be treated as an error condition or as a valid scenario requiring fallback to global timescales.

Copilot uses AI. Check for mistakes.
Comment on lines +911 to +912
// This tests that ProcessFragmentChunk uses cachedFragment->timeScale when set,
// or falls back to global timescale when cachedFragment->timeScale is 0
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

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

The comment at line 912 states "or falls back to global timescale when cachedFragment->timeScale is 0", but the production code at streamabstraction.cpp:1040-1043 does NOT implement this fallback behavior. Instead, it logs an error and returns early. Either this comment needs to be updated to reflect that fallback is NOT supported, or the production code needs to be changed to implement the fallback. This mismatch will cause test failures for the test cases at lines 927-928.

Suggested change
// This tests that ProcessFragmentChunk uses cachedFragment->timeScale when set,
// or falls back to global timescale when cachedFragment->timeScale is 0
// This tests that ProcessFragmentChunk uses cachedFragment->timeScale when set.
// Note: Fallback to a global timescale when cachedFragment->timeScale is 0 is not implemented.

Copilot uses AI. Check for mistakes.
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.

3 participants