@@ -185,13 +185,18 @@ function EventController() {
185185 */
186186 function _triggerEvents ( events , presentationTimeThreshold , currentVideoTime ) {
187187 try {
188+ // Skip event processing if currentVideoTime is null or undefined (playback hasn't started)
189+ if ( currentVideoTime === null || currentVideoTime === undefined ) {
190+ return ;
191+ }
192+
188193 const callback = function ( event , currentPeriodEvents ) {
189194 if ( event !== undefined ) {
190195 const duration = ! isNaN ( event . duration ) ? event . duration : 0 ;
191196 const isRetriggerable = _isRetriggerable ( event ) ;
192197 const hasNoJump = _hasNoJumpValue ( event ) ;
193198 const hasExecuteOnce = _hasExecuteOnceValue ( event ) ;
194-
199+
195200 // Check if event is ready to resolve (earliestResolutionTimeOffset feature)
196201 if ( _checkEventReadyToResolve ( event , currentVideoTime ) ) {
197202 _triggerEventReadyToResolve ( event ) ;
@@ -200,12 +205,20 @@ function EventController() {
200205 if ( isRetriggerable && _canEventRetrigger ( event , currentVideoTime , presentationTimeThreshold , hasExecuteOnce ) ) {
201206 event . triggeredStartEvent = false ;
202207 }
203-
208+
204209 // Handle noJump events first - these ignore duration and trigger when skipping ahead
205210 if ( hasNoJump && _shouldTriggerNoJumpEvent ( event , currentVideoTime , currentPeriodEvents ) ) {
206211 event . triggeredNoJumpEvent = true ;
207212 _startEvent ( event , MediaPlayerEvents . EVENT_MODE_ON_START ) ;
208213 }
214+ // Special handling for presentationTime=0 events - fire ON_START immediately when playback starts
215+ // These events semantically mean "fire at the start of content" and should trigger as soon as playback begins
216+ else if ( event . calculatedPresentationTime === 0 && currentVideoTime > 0 ) {
217+ _startEvent ( event , MediaPlayerEvents . EVENT_MODE_ON_START ) ;
218+ if ( hasNoJump ) {
219+ event . triggeredNoJumpEvent = true ;
220+ }
221+ }
209222 // Handle regular events - these check duration and timing
210223 else if ( event . calculatedPresentationTime <= currentVideoTime && event . calculatedPresentationTime + presentationTimeThreshold + duration >= currentVideoTime ) {
211224 _startEvent ( event , MediaPlayerEvents . EVENT_MODE_ON_START ) ;
@@ -267,7 +280,9 @@ function EventController() {
267280 let event = values [ i ] ;
268281 const currentTime = playbackController . getTime ( ) ;
269282 const duration = ! isNaN ( event . duration ) ? event . duration : 0 ;
270- if ( ! _eventHasExpired ( currentTime , duration , event . calculatedPresentationTime ) ) {
283+ const isExpired = _eventHasExpired ( currentTime , duration , event . calculatedPresentationTime , false , true ) ;
284+
285+ if ( ! isExpired ) {
271286 let result = _addOrUpdateEvent ( event , inlineEvents [ periodId ] , true ) ;
272287
273288 if ( result === EVENT_HANDLED_STATES . ADDED ) {
@@ -812,15 +827,21 @@ function EventController() {
812827 * @param {number } threshold
813828 * @param {number } calculatedPresentationTimeInSeconds
814829 * @param {boolean } isRetriggerable
830+ * @param {boolean } isInitialCheck - Whether this is the initial check during addInlineEvents
815831 * @return {boolean }
816832 * @private
817833 */
818- function _eventHasExpired ( currentVideoTime , threshold , calculatedPresentationTimeInSeconds , isRetriggerable = false ) {
834+ function _eventHasExpired ( currentVideoTime , threshold , calculatedPresentationTimeInSeconds , isRetriggerable = false , isInitialCheck = false ) {
819835 try {
820836 // Retriggerables events don't expire in the traditional sense
821837 if ( isRetriggerable ) {
822838 return false ;
823839 }
840+ // Events at presentationTime=0 should not be considered expired during the initial check
841+ // because they semantically mean "fire at the start of content" and should always be processed
842+ if ( isInitialCheck && calculatedPresentationTimeInSeconds === 0 ) {
843+ return false ;
844+ }
824845 return currentVideoTime - threshold > calculatedPresentationTimeInSeconds ;
825846 } catch ( e ) {
826847 logger . error ( e ) ;
@@ -876,6 +897,8 @@ function EventController() {
876897 logger . debug ( `Starting callback event ${ eventId } at ${ currentVideoTime } ` ) ;
877898 const url = event . messageData instanceof Uint8Array ? Utils . uint8ArrayToString ( event . messageData ) : event . messageData ;
878899 _sendCallbackRequest ( url ) ;
900+ // Also dispatch the callback event so external listeners can capture it (e.g., for CMCD reporting)
901+ eventBus . trigger ( event . eventStream . schemeIdUri , { event } , { mode } ) ;
879902 } else {
880903 logger . debug ( `Starting event ${ eventId } from period ${ event . eventStream . period . id } at ${ currentVideoTime } ` ) ;
881904 eventBus . trigger ( event . eventStream . schemeIdUri , { event } , { mode } ) ;
0 commit comments