Skip to content

Feature Cmcdv2#72

Open
cotid-qualabs wants to merge 57 commits intodevelopmentfrom
feature/cmcdv2
Open

Feature Cmcdv2#72
cotid-qualabs wants to merge 57 commits intodevelopmentfrom
feature/cmcdv2

Conversation

@cotid-qualabs
Copy link
Copy Markdown

@cotid-qualabs cotid-qualabs commented Apr 7, 2025

This Pull Request introduces comprehensive support for CMCD (Common Media Client Data) Version 2, significantly expanding the player's capabilities for media analytics and client-side data reporting. The changes allow for more granular control over what data is sent, how it's sent, and to where, aligning with the latest CMCD specifications.

Key Features & Enhancements:

  1. CMCD v2 Specification Adherence:

    • Supported CMCD v2 keys:
      • ltc - rc - bg - sta - ttfb - ttlb - ts - url - ec - msd - e - cmsdd - cmsds - df - sn - pb - tpb - bsd
    • Ensures correct mapping to HTTP headers or query parameters for each new mode.
    • Includes a mechanism to differentiate and handle CMCD v1 and v2 logic based on player settings, with a temporary conversion function to manage the transition or ensure backward compatibility where needed, until main conversion function is merged into the Common Media Library.
  2. New CMCD Reporting Modes:

    • Event Mode (event): Enables CMCD data reporting triggered by specific player events (e.g., playback state changes, errors) or at configurable time intervals. This allows for real-time or near real-time feedback on player behavior.
      • Supported CMCD Events:
        • ps - e - t
        • Supported Play State events:
          • s - p - k - r -a - w - e - f
    • Response Mode (response): Facilitates CMCD data reporting after a media segment or manifest response is received. This mode includes response-specific metrics like Time To First Byte (ttfb), Time To Last Byte (ttlb), and response code (rc).
  3. Multiple Reporting Targets & Granular Configuration:

    • Introduces a new streaming.cmcd.targets array in the player settings. This allows developers to configure multiple CMCD reporting endpoints.
    • Each target can be independently configured with:
      • url: The destination URL for the CMCD report.
      • mode: The data transmission mode to use for this target (query or header ).
      • cmcdMode: The reporting mode to use for this target (event or response).
      • enabledKeys: A specific list of CMCD keys to include for this target. If not defined, sends all available keys for the target. If the list is empty, no keys are sent.
      • events (for event mode): An array of player events that should trigger a CMCD report. If events is not defined, all available events trigger a CMCD report. If the array is empty, no event-specific CMCD data will be sent.
      • timeInterval (for event mode): The interval in seconds for periodic CMCD reports. If value is 0, periodic reports are disabled.
      • includeInRequests (for request/response modes): Specifies which types of HTTP requests (e.g., manifestmediaSegmentInitmediaSegment) should include CMCD data for this target.
  4. Refined Orchestration of CMCD data via CmcdController:

    • The CmcdController is responsible for orchestrating all CMCD v2 data collection, formatting, and reporting.
  5. Refined State Management via CmcdModel:

    • The CmcdModel has been updated to manage and provide the necessary player and media state for all supported CMCD modes and keys, including new v2 parameters.
  6. Request/Response Interceptors:

    • The CMCD logic is now more cleanly integrated using request and response interceptors.
    • _cmcdRequestModeInterceptor injects CMCD data into outgoing requests (for query and header modes).
    • _cmcdResponseModeInterceptor processes incoming responses to gather data for response mode and triggers reports.
  7. New Sample Pages

    1. CMCD v2
      • CMCD v2 Reporting sample page showcases how to set up CMCD v2 and its new target configurations for each new mode.
    2. Custom Keys and Callback after server response
      • CMCD v2 Callbacks with Network Interceptors sample page introduces a demonstration of using Interceptors to add custom keys as a callback before a report and also processing CMCD data on a callback after the server response.
  8. Batching
    The CmcdBatchController is a new component designed to efficiently manage and send Common Media Client Data (CMCD) reports. Instead of sending each report as a separate HTTP request, this controller aggregates them into batches, reducing network traffic and server load.

    1. Batching Mechanism: The controller collects individual CMCD reports and sends them together in a single POST request. The reports are newline-separated in the request body, as specified by the CMCD standard for batched data.
      • Batching by Time (batchTimer): You can configure a batchTimer (in seconds) for a reporting target. When the first report for a target is received, a timer is started. When the timer expires, all collected reports for that target are sent as a single batch.
      • Batching by Size (batchSize): You can configure a batchSize for a reporting target. The controller tracks the number of reports in the current batch. Once the count reaches the batchSize, the batch is sent immediately, even if the batchTimer has not expired.
    2. Error Handling: CmcdBatchController includes logic to handle specific HTTP error responses
      • 429 (Too Many Requests): If a reporting server responds with a 429 status, the controller will not discard the report. Instead, it will attempt to resend the batch after a delay. It uses a retry mechanism with increasing backoff periods to avoid overwhelming the server.
      • 410 (Gone): If a server responds with a 410 status, it indicates that the endpoint is no longer available. The controller will mark that URL as "gone" and will not attempt to send any further reports to it for the remainder of the session.
  9. Unit Testing

    • Expanded the existing unit test suite to cover CMCD v2 scenarios and different configurations.
  10. Common Media Library Integration

    • Refactoring of the Common Media Client Data (CMCD) implementation. By updating the @svta/common-media-library dependency and leveraging its enhanced encodeCmcd function, the project centralizes CMCD key filtering, whitelisting, and custom key management within the external library.

Main Files Affected:

  • src/streaming/controllers/CmcdController.js: Contains the core logic for CMCD v2, including handling for new modes, multiple targets, key filtering, and integration with the CMCD library.
  • src/streaming/models/CmcdModel.js: Updated to store and manage state for new CMCD v2 parameters and reporting modes.
  • src/core/Settings.js (implied changes): Updated to include new configuration options for CMCD v2, particularly the streaming.cmcd.targets array and CMCD version selection.

These changes provide a much more flexible and powerful CMCD implementation, enabling richer analytics and better interoperability with CDNs and other services that consume CMCD data.

Future Work

While the current implementation of CMCD v2 in dash.js provides basic functionality, there are several areas identified for future enhancements and development:

  • Implement Remaining CMCDv2 Keys: Review the CMCDv2 specification and implement any currently unsupported keys to provide a more complete and spec-compliant CMCD reporting solution. Prioritize implementation based on common use cases and community feedback.

@cotid-qualabs cotid-qualabs changed the title rename CmcdModel to CmcdController + extract CmcdController from HTTP… Cmcdv2 Apr 7, 2025
@cotid-qualabs cotid-qualabs changed the title Cmcdv2 Feature Cmcdv2 Apr 7, 2025
@cotid-qualabs cotid-qualabs marked this pull request as ready for review May 22, 2025 16:29
Comment thread src/streaming/constants/Constants.js Outdated
Comment thread src/core/Settings.js Outdated
Comment thread src/streaming/controllers/CmcdController.js Outdated
@cotid-qualabs cotid-qualabs force-pushed the feature/cmcdv2 branch 2 times, most recently from cdbf737 to 18964ea Compare January 14, 2026 14:18
cotid-qualabs and others added 17 commits January 16, 2026 15:37
* cmcd reporter initialization

* request mode migration

* event mode migration

* cmcd model migration

* fix cmcd model unit tests

* fixes for cmcd parameters and cleanup -
protection controller fixes WIP

* cleanup and update unit tests

* refactor unit tests and fixes

* fix unit tests and remove batchTimer
* ab, lab and tab inner list and request mode

* ab, tab and lab inner list for v2

* bl inner list and event mode

* br inner list and event mode

* toInnerList helper

* bsd inner list

* mtp inner list and event mode

* nor inner list

* pb inner list and event mode

* tp inner list and event mode

* tpb inner list and event mode

* fix unit tests
…rum#4925)

* Catch errors triggered by changeType

* If changeType() triggers a NotSupportedError then disable changeType and resetSourceBuffer for track switches

* Remove firstFragmentedSubtitleStart from cts calculation as it leads to wrong buffered ranges for multiperiod content

* Refactoring
* chore: update CML dependencies

* fix: update common media request

* fix: update resourceTiming properties to use performance.now()

* fix: update cmcd data formatting

* fix: remove redundant rr values

* refactor: simplify cmcd reporting

* fix: test mock requests missing parameters

* chore: update cml cmcd version

* should not send report if events are undefined

* fix unit tests

---------

Co-authored-by: cotid-qualabs <constanzad@qualabs.com>
* Run npm audit fix

* Sort by k attribute in case all parameters like bandwidth etc are the same to avoid oscillating ABR switches

* For SegmentSequenceProperties use information about bootstrap representations when ordering

* Refactor _getMaxFragmentDurationForLiveDelayCalculation

* Fix a typo
…m#4937)

* perf(abr): replace Object.keys().forEach with for..in loop

Replace nested Object.keys().forEach calls with for..in loops in
_onVideoElementResized to avoid array allocations on video resize events.

This reduces memory pressure on devices with limited resources like Smart TVs.

* perf(net): replace Object.keys().map with for..in loop

Replace Object.keys().map() with a for..in loop in _addPathwayCloningParameters
to reduce array allocations during HTTP requests with query parameters.

This optimization benefits devices with constrained memory like Smart TVs.

* perf(events): optimize Object.keys usage in hot paths

- Add _isEmptyObject() helper to avoid array allocation for length check
- Replace Object.keys() loops with for..in in _iterateAndTriggerCallback
- Replace .forEach() with standard for loop for array iteration

These functions are called every 100ms during playback, so reducing
allocations helps minimize garbage collection on resource-constrained
devices like Smart TVs.
…ash-Industry-Forum#4943)

Replace hardcoded 100ms GAP_HANDLER_INTERVAL with configurable
settings.get().streaming.gaps.checkInterval (default 250ms), replace
Object.keys().some() with for..in loop, and cache settings.get().streaming.gaps
in _jumpGap to eliminate redundant property traversals.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…mer cleanup (Dash-Industry-Forum#4946)

The `1` was incorrectly passed as the `fromIndex` parameter to `indexOf()`
instead of as the `deleteCount` parameter to `splice()`. This caused two bugs:

1. `indexOf(timer, 1)` skips index 0, so timers at position 0 are never found
2. `splice(index)` without a second argument removes ALL elements from that
   index onward, instead of removing just the one timer

This results in timer references leaking in the array and valid timers being
incorrectly removed, causing memory leaks during long content steering sessions.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…try-Forum#4947)

The while loop that removes segments prior to the DVR availability start
time did not check if the segments array became empty after each splice.
When all segments fall before availabilityStartTime, the array is fully
emptied and accessing segments[0] throws a TypeError.

Add array length checks both in the loop condition and after the loop
before accessing segments[0] for the DVR range update.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
dsilhavy and others added 30 commits February 23, 2026 08:16
* Add a new method getUnfilteredRepresentationsByType

* Add a new method getRepresentationsByTypeUnfiltered. This allows applications to get all Representations regardless of any filter settings

* Fix wrong JSDoc
* CertUrl support added

Co-authored-by: Daniel Silhavy <daniel.silhavy@fokus.fraunhofer.de>
Co-authored-by: bjoern altmann <bjoern.altmann@fokus.fraunhofer.de>
* add check for properties in areTracksEqual
add some info to logger

* making use of feature in utils
adding unit tests
* Fix: DVB buffer level always zero if subs disabled

* Feat: Provide setting to override DVB reporting URL

* Feat: Add maxDrift to reference player, allow it to be negative for disabling

* Feat: Add step algorithm to catchup controller

* Feat: Add liveThreshold to catchup controller

* Fix: Typo "in In"

* Update JSDocs for default maxDrift settings

* Clarify liveThreshold setting description

* Fix: dvb_reportingUrl updated to dvbReportingUrl

* Simplify _updateBufferLevel logic
)

* Drop CERT_URL_LOWER_CASE constant and use case-insensitive lookup instead

* Add Apple FairPlay Streaming (FPS) DRM support
…try-Forum#4934)

EventBus: use splice() instead of setting handlers to null on off().
Setting to null left ghost entries in the handlers array, causing it
to grow indefinitely over the lifetime of the player.

XHRLoader: clear onabort and ontimeout handlers after abort() to
prevent stale references from keeping the XHR object in memory.

Fixes Dash-Industry-Forum#4933
* Drop CERT_URL_LOWER_CASE constant and use case-insensitive lookup instead

* Add Apple FairPlay Streaming (FPS) DRM support

* Sort ProtectionConstants.js

* Additional refactoring

* WiP: Rework certificate logic in ProtectionController.js and work with model classes CertificateRequest.js and CertificateResponse.js. Allow adding filters

* Further refactoring of the Fairplay flow

* Catch the error in case certificate was invalid

* Remove certificate URL for clearkey from reference UI

* Change smoke vector config

* Rework verify_pull_request.yml workflow

* Checkout outside of action.yml

* Expose certificate request and response filters

* Add example for certificate wrapping

* Add Fairplay example

* Add API to query protection data

* Add missing typescript definitions

* Add missing typescript definition for certificate request and response filters

---------

Co-authored-by: Torbjörn Einarsson <torbjorn@einarssons.se>
* Claude generated reference UI - First version

* Add light mode

* Fix DRM errors

* Fix controlbar issues

* Fix controlbar issues

* Fix controlbar issues

* Fix quality change

* Use settings from Settings.js

* Fix playback ended seek bug

* Add option to increase playback rate

* Add additional color schemes

* Enable fast switching

* WiP: Commit ID

* Make new Reference UI the default one

* Add external libraries

* Reformatting

* Remove unneccessary folder

* Sync ref options with settings

* Settings changes

* Add tooltips to reference UI

* Move controlbar to contrib directory

* Fix visualization of playback rate

* Add framerate to metrics panel

* Additional metrics and controlbar changes

* Remove outdated contributor entry

* Remove custom throughput rule from reference ui

* Only rename new ref ui before merge

* Add catchup options to referenceUI

* Update list of contributors

* Add option to set server certificate URLs

* Additional changes to controlbar

* Fix thumbnail rendering

* Add missing fields to be synced from the URL parameters

* Add additional unit tests

* Add Qualabs as a contributor

* Rename current reference UI to "old" and use the new one by default

* Reuse images and fix paths

* Add link to dashjs.org
* Refactor GitHub actions

* Add comments to explain how things are merged
* feat: add CMCD v2 Playwright E2E tests with spec validation

Adds comprehensive E2E tests using Playwright to validate CMCD v2 payloads
at the network level during real playback. Uses @svta/cml-cmcd validation
functions (validateCmcd, validateCmcdHeaders, validateCmcdEvent, validateCmcdKeys)
for spec-level compliance checks across query, header, and event modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: replace Playwright E2E tests with Karma functional tests for CMCD v2

Replace the Playwright-based E2E test suite with Karma functional tests
that use XHR interceptors to validate CMCD v2 spec compliance. This
eliminates the @playwright/test dependency (~100MB) while maintaining
full test coverage across all three transmission modes (query, header,
event), key filtering, and version validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: remove double parsing from CMCD v2 tests

CmcdRequestCollector now stores raw CMCD strings instead of pre-parsed
objects, removing its dependency on @svta/cml-cmcd entirely. Tests use
validation function return values (result.data) for both spec validation
and data assertions in a single pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: update cml packages

* fix: add logging to xhr collector

* refactor: use unified collector and validateCmcdRequest for CMCD v2 tests

Rewrite CmcdRequestCollector with a single requests array storing
httpRequest objects compatible with CML's validateCmcdRequest(). Replace
mode-specific validators (validateCmcd, validateCmcdHeaders) with the
unified validateCmcdRequest() and remove the invalid validateCmcdKeys
test that was passing a string to a function expecting a parsed object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* make track filtering more verbose

* add missing methods to JSDoc
…ion (-185 KB) (Dash-Industry-Forum#4970)

* refactor: replace bcp-47-normalize with lightweight BCP-47 normalization

Remove the bcp-47-normalize dependency (~185 KB minified including
transitive deps) and replace it with a ~75-line internal utility that
covers the two features dash.js actually uses: RFC 5646 case
normalization and ISO 639-2 to 639-1 conversion.

Fixes Dash-Industry-Forum#4969

* fix: add BSD license header, use null-prototype map, update lockfile

- Add standard dash.js BSD license header to BCP47Utils.js
- Use Object.create(null) for ISO_639_2_TO_1 to prevent prototype pollution
- Regenerate package-lock.json to remove bcp-47-normalize and transitive deps

* test: add unit tests for BCP47Utils normalization

Addresses reviewer feedback from Dash-Industry-Forum#4969 (stschr, dsilhavy):
- Case normalization per RFC 5646 §2.1.1
- ISO 639-2 to 639-1 conversion (B and T variants)
- Numeric subtag preservation (es-419, de-DE-1996)
- Prototype pollution safety (Object.create(null) guard)
- Table integrity: 202 entries, 3-letter keys, 2-letter values
…ed" error (Dash-Industry-Forum#4929)

Wrap the auto-creation logic in a browser environment check to allow
dash.js to be imported in SSR environments like Next.js without throwing
a "window is not defined" error.

The module-level code that accesses window APIs (addEventListener,
setInterval, etc.) is now only executed when running in a browser context.

Fixes Dash-Industry-Forum#4728
Dash-Industry-Forum#4982)

* fix(dvb): correct off-by-one in DVBSelector weighted BaseURL selection

Remove erroneous `- 1` in random number generation that shrank the
selection range, causing the last BaseURL in a weighted group to never
be selected. With 2 equal-weight CDNs, only the first was ever picked.

Also add stub.restore() to existing DVB A168 test to prevent sinon
stub leak, and add two unit tests for equal-weight selection (2 and 3
CDNs).

Fixes Dash-Industry-Forum#4981

* style: address Copilot review feedback

- Clarify comment: random number range is [0, totalWeight) exclusive
- Rename test: "fair distribution" → "be able to select both" (we test
  reachability, not statistical distribution)

* fix(test): use valid Math.random() return value in DVB A168 test

Replace stub.returns(1) with stub.returns(0.99) since Math.random()
returns [0, 1) exclusive. The value 1.0 masked the off-by-one bug
because it produced the same result with both formulas.
…-Industry-Forum#4986)

When a TTML cue has no explicit end attribute, mediaTimeEvents[i+1]
is undefined on the last iteration. undefined + offsetTime = NaN,
and startTime < NaN is always false, so the cue is silently dropped.

Use endTimeSegment (already declared, documented in JSDoc, and passed
by TextSourceBuffer) as the fallback end time for the last cue.

Fixes Dash-Industry-Forum#4985
* npm audit fix

* Small fixes in the unit tests
…y-Forum#4996)

* docs: rewrite AGENTS.md — gotchas-only, DRY, verifiable

Rewrites AGENTS.md from 170 lines to ~50. Removes duplicated content
(ESLint rules, FactoryMaker examples, project tree, naming conventions)
that had already drifted out of sync. Fixes inaccuracies (npm run build
pipeline, entry point exports, webpack bundle counts). Keeps only
non-derivable gotchas and a "where to look" index.

Closes Dash-Industry-Forum#4995

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs: clarify error handling — playback errors are events, API misuse throws

Addresses Copilot review feedback: MediaPlayer methods throw synchronously
on misuse (calling before initialize/attachSource, bad arguments), while
playback/runtime errors are dispatched via EventBus.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…6 KB) (Dash-Industry-Forum#4972)

* refactor: replace ua-parser-js with lightweight browser detection

Remove the ua-parser-js dependency (~26 KB minified) and replace it
with simple regex-based detection in Utils.parseUserAgent(). dash.js
only uses this to check for Safari and Edge browser names in two
call sites.

Fixes Dash-Industry-Forum#4971

* fix: address review feedback on ua-parser-js removal

- Fix ua parameter being ignored when provided as string
- Exclude iOS browsers (CriOS, FxiOS) from Safari detection
- Add unit tests for parseUserAgent
- Move ua-parser-js to devDependencies for functional tests

* test: add comprehensive parseUserAgent coverage

Add 7 additional tests for browser detection edge cases:
- Safari on iOS (real Safari, not CriOS/FxiOS)
- Safari on iPadOS (desktop UA since iPadOS 13)
- Edge on Android (EdgA token)
- Opera exclusion (OPR token)
- Firefox desktop (no Safari/Edge token)
- Empty string input
- Explicit null parameter (navigator fallback)

* fix: extend parseUserAgent to detect all major browsers

Address review feedback from dsilhavy: Chrome and Firefox were returning
empty string instead of their browser name. Reorder detection cascade
(edge → opera → chrome → firefox → safari) so the most specific tokens
match first, removing the need for Safari exclusion regexes.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ing runtime (Dash-Industry-Forum#4997)

* Dynamically read out request timeout in case settings are updated during runtime

* Add an additional test to validate timeouts are updated
* enable RegEx for Exceptions

* move local RegEx into Constants

* fixing regression issue with MapNode exceptions

* move code into sub-function

* rename some variables

* remove merge flag form MapNode

* enable RegEx for Exceptions

* move code into sub-function

* rename some variables

* remove merge flag form MapNode

* - reestablish new file lost
- remove duplicated code
while merging

* Revert changes to clean history

* add one unit test for an example of a DASH XML element that can be present only once

* rename variables to improve intelligibility of their semantics

* implement merging of attributes for non-array Nodes per DASH spec

* add type check prior to merging values
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.