Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MUXSDKStats/MUXSDKStats/MUXSDKPlayerBinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ typedef NS_ENUM(NSUInteger, MUXSDKViewOrientation) {
- (void)dispatchPause;
- (void)dispatchTimeUpdateEvent:(CMTime)time;
- (void)dispatchError;
- (void)dispatchVideoChange;
- (void)dispatchViewEnd;
- (void)dispatchOrientationChange:(MUXSDKViewOrientation) orientation;
- (void)dispatchAdEvent:(MUXSDKPlaybackEvent *)event;
Expand Down
68 changes: 43 additions & 25 deletions MUXSDKStats/MUXSDKStats/MUXSDKPlayerBinding.m
Original file line number Diff line number Diff line change
Expand Up @@ -403,36 +403,48 @@ - (void)detachAVPlayer {
}
}

- (void)dispatchVideoChange {
// end the current view if it's not already been ended
if (_state != MUXSDKPlayerStateViewEnd) {
[self dispatchViewEnd];
}

[self.playDispatchDelegate videoChangedForPlayer:_name];
// TODO: test that we have all the metadata we need on the subsequent view. On android we needed to catch up with the current state
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this refer to external stuff like MUXSDKCustomerData or something else?

Copy link
Collaborator Author

@daytime-em daytime-em Jan 6, 2025

Choose a reason for hiding this comment

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

The customer data, and also more general things like the private stuff in the player binding

I saw some odd stuff with playhead time during the subsequent views after changing, so I think there might still be something to clear. It could be here or in the player binding (edit: oops, here or in the stores I mean), I'm not really sure


//
// Special case for AVQueuePlayer
// In a normal videoChange: world - the KVO for "rate" will fire - and
// subsequently after that this binding will dispatchPlay. In fact, any time
// an AVPlayer gets an item loaded into it the KVO for "rate" changes.
//
// However, in AVQueuePlayer world - the "rate" doesn't fire when the video is
// changed. I don't know why, but I guess that is the intended behavior. For that
// reason, if we're handling a videoChange event and we're dealing with AVQueuePlayer
// then we have to fire the play event here.
//
if (_shouldHandleAVQueuePlayerItem) {
_shouldHandleAVQueuePlayerItem = false;
[self dispatchPlay];
}
}

- (void)monitorAVPlayerItem {
if ((!_automaticVideoChange && !_didTriggerManualVideoChange) || _isAdPlaying) {
// The player item could be the the ad itself, and we monitor ads differentlty than main content
if (_isAdPlaying) {
return;
}

// TODO: IMA has to disable automaticVideoChange and do it manually (for postrolls only)
if (_state != MUXSDKPlayerStateReady && (_automaticVideoChange || _didTriggerManualVideoChange)) {
[self dispatchVideoChange];
}

if (_playerItem) {
if (_didTriggerManualVideoChange) {
_didTriggerManualVideoChange = false;
}
[self dispatchViewEnd];
[self stopMonitoringAVPlayerItem];

if (_player.currentItem) {
[self.playDispatchDelegate videoChangedForPlayer:_name];
}

//
// Special case for AVQueuePlayer
// In a normal videoChange: world - the KVO for "rate" will fire - and
// subsequently after that this binding will dispatchPlay. In fact, any time
// an AVPlayer gets an item loaded into it the KVO for "rate" changes.
//
// However, in AVQueuePlayer world - the "rate" doesn't fire when the video is
// changed. I don't know why, but I guess that is the intended behavior. For that
// reason, if we're handling a videoChange event and we're dealing with AVQueuePlayer
// then we have to fire the play event here.
//
if (_shouldHandleAVQueuePlayerItem) {
_shouldHandleAVQueuePlayerItem = false;
[self dispatchPlay];
}
}
if (_player && _player.currentItem) {
_playerItem = _player.currentItem;
Expand Down Expand Up @@ -488,9 +500,11 @@ - (void)stopMonitoringAVPlayerItem {
[self safelyRemovePlayerItemObserverForKeyPath:@"status"];
[self safelyRemovePlayerItemObserverForKeyPath:@"playbackBufferEmpty"];
_playerItem = nil;
if (!_isAdPlaying) {
[MUXSDKCore destroyPlayer: _name];
}
}

/// Cleans up our Core monitor. Call when we are detaching from an AVPlayer
- (void)destroyPlayer {
[MUXSDKCore destroyPlayer: _name];
}

- (void) programChangedForPlayer {
Expand Down Expand Up @@ -775,6 +789,7 @@ - (void)dispatchViewInit {
return;
}
[self resetVideoData];

MUXSDKPlayerData *playerData = [self getPlayerData];
MUXSDKViewInitEvent *event = [[MUXSDKViewInitEvent alloc] init];
[event setPlayerData:playerData];
Expand Down Expand Up @@ -1232,6 +1247,9 @@ - (BOOL) doubleValueIsEqual:(NSNumber *) x toOther:(NSNumber *) n {
}

- (void)didTriggerManualVideoChange {
// TODO: Should we deprecate? we no longer need this method. videoChange can happen whenever it's needed now, not just when AVPlayerItem changes
Copy link
Contributor

Choose a reason for hiding this comment

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

Strong +1 to deprecate

Copy link
Contributor

@andrewjl-mux andrewjl-mux Dec 13, 2024

Choose a reason for hiding this comment

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

If keeping this functionality around makes sense we could also expose a better named API with the same functionality while deprecating this one. (And we'd drop the deprecated method in the next major version bump.)

// TODO: (continued) on the other hand, it *does* do something specific, since someone could call this method any time before the playeritem changes
// TODO: (continued again) on the Other other hand, this method is mostly here to support a manual video-change workflow that we want to repalce
_didTriggerManualVideoChange = true;
}
@end
Expand Down
29 changes: 24 additions & 5 deletions MUXSDKStats/MUXSDKStats/MUXSDKStats.m
Original file line number Diff line number Diff line change
Expand Up @@ -603,13 +603,10 @@ + (void)videoChangeForPlayer:(nonnull NSString *)name withCustomerData:(nullable
MUXSDKCustomerVideoData *videoData = [customerData customerVideoData];
MUXSDKCustomData *customData = [customerData customData];

if (!(videoData || viewData || customData)) {
return;
}
MUXSDKPlayerBinding *player = [_viewControllers valueForKey:name];
if (player) {
[player didTriggerManualVideoChange];
[player dispatchViewEnd];

if (videoData) {
[_customerVideoDataStore setVideoData:videoData forPlayerName:name];
}
Expand All @@ -622,6 +619,8 @@ + (void)videoChangeForPlayer:(nonnull NSString *)name withCustomerData:(nullable
if (customData) {
[_customerCustomDataStore setCustomData:customData forPlayerName:name];
}

[player dispatchVideoChange];
[player prepareForAvQueuePlayerNextItem];
}
}
Expand All @@ -630,8 +629,28 @@ + (void)videoChangeForPlayer:(nonnull NSString *)name withCustomerData:(nullable

+ (void)programChangeForPlayer:(nonnull NSString *)name
withCustomerData:(nullable MUXSDKCustomerData *)customerData {
[MUXSDKStats videoChangeForPlayer:name withCustomerData:customerData];
MUXSDKPlayerBinding *player = [_viewControllers valueForKey:name];
[player dispatchViewEnd];

if (customerData) {
MUXSDKCustomerPlayerData *playerData = customerData.customerPlayerData;
MUXSDKCustomerVideoData *videoData = customerData.customerVideoData;
MUXSDKCustomerViewData *viewData = customerData.customerViewData;
MUXSDKCustomData *customData = customerData.customData;
if (videoData) {
[_customerVideoDataStore setVideoData:videoData forPlayerName:name];
}
if (viewData) {
[_customerViewDataStore setViewData:viewData forPlayerName:name];
}
if (playerData) {
[_customerPlayerDataStore setPlayerData:playerData forPlayerName:name];
}
if (customData) {
[_customerCustomDataStore setCustomData:customData forPlayerName:name];
}
}

if (player) {
[player programChangedForPlayer];
}
Expand Down
4 changes: 3 additions & 1 deletion MUXSDKStats/MUXSDKStatsTests/MUXSDKPlayerBindingTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ - (void)testPlayerBindingManagerStartsNewViews {
[self setupViewControllerPlayerBinding:name
softwareName:@"TestSoftware"
softwareVersion:@"0.1.0"];


NSDictionary *events = [MUXSDKCore capturedEventsForPlayer:name];

XCTAssertEqual(3, [MUXSDKCore eventsCountForPlayer:name]);
id<MUXSDKEventTyping> event0 = [MUXSDKCore eventAtIndex:0 forPlayer:name];
id<MUXSDKEventTyping> event1 = [MUXSDKCore eventAtIndex:1 forPlayer:name];
Expand Down
36 changes: 27 additions & 9 deletions MUXSDKStats/MUXSDKStatsTests/MUXSDKStatsTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,11 @@ - (void)testVideoChangeForAVPlayerViewControllerWithCustomData{
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType
MUXSDKPlaybackEventViewEndEventType,
MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
];

[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
[self assertPlayer:playName dispatchedDataEventsAtIndex:1 withCustomData:@{@"c1": @"bar"}];
[MUXSDKStats destroyPlayer:playName];
Expand All @@ -206,10 +209,13 @@ - (void)testVideoChangeForAVPlayerViewControllerWithCustomerViewData{
XCTAssertNotNil(playerBinding, "expected monitorAVPlayerViewController to return a playerBinding");
[customerViewData setViewSessionId:@"bar"];
[MUXSDKStats videoChangeForPlayer:playName withCustomerData:customerData];
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType, // from first view
MUXSDKDataEventType, // from first view
MUXSDKPlaybackEventPlayerReadyEventType, // from first view
MUXSDKPlaybackEventViewEndEventType, // changing video for second view
MUXSDKPlaybackEventViewInitEventType, // from the second view starting
MUXSDKDataEventType, // from the second view starting
MUXSDKPlaybackEventViewEndEventType, // from destroyPlayer
];
[MUXSDKStats destroyPlayer:playName];
[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
Expand All @@ -233,7 +239,10 @@ - (void)testVideoChangeForAVPlayerViewController{
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType
MUXSDKPlaybackEventViewEndEventType,
MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventViewEndEventType,
];
[MUXSDKStats destroyPlayer:playName];
[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
Expand All @@ -258,7 +267,7 @@ - (void)testManualVideoChangeForAVPlayerViewController{

NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType
MUXSDKPlaybackEventPlayerReadyEventType,
];
[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
[self assertPlayer:playName dispatchedDataEventsAtIndex:1 withCustomerVideoData:@{@"vtt": @"01234"}];
Expand Down Expand Up @@ -329,7 +338,11 @@ - (void)testVideoChangeForAVPlayerLayerWithCustomerViewData API_UNAVAILABLE(visi
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType
MUXSDKPlaybackEventViewEndEventType, // from changing to the new view
MUXSDKPlaybackEventViewInitEventType, // from changing to the new view
MUXSDKDataEventType, // from changing to the new view
MUXSDKPlaybackEventViewEndEventType, // from destroying the player

];
[MUXSDKStats destroyPlayer:playName];
[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
Expand All @@ -353,7 +366,10 @@ - (void)testVideoChangeForAVPlayerLayer API_UNAVAILABLE(visionos) {
NSArray *expectedEventTypes = @[MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType
MUXSDKPlaybackEventViewEndEventType,
MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventViewEndEventType,
];
[MUXSDKStats destroyPlayer:playName];
[self assertPlayer:playName dispatchedEventTypes:expectedEventTypes];
Expand Down Expand Up @@ -449,6 +465,8 @@ - (void)testProgramChangeForAVPlayerViewController{
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayerReadyEventType,
MUXSDKPlaybackEventViewEndEventType,
MUXSDKPlaybackEventViewInitEventType,
MUXSDKDataEventType,
MUXSDKPlaybackEventPlayEventType,
MUXSDKPlaybackEventPlayingEventType,
MUXSDKPlaybackEventViewEndEventType,
Expand Down
1 change: 1 addition & 0 deletions MUXSDKStats/MUXSDKStatsTests/Utils/MUXSDKCore+Mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
+ (NSUInteger) eventsCountForPlayer:(NSString *)playerId;
+ (MUXSDKDataEvent *) globalEventAtIndex:(NSUInteger)index;
+ (NSUInteger) globalEventsCount;
+ (NSArray *) capturedEventsForPlayer: (NSString *)player;

@end

4 changes: 4 additions & 0 deletions MUXSDKStats/MUXSDKStatsTests/Utils/MUXSDKCore+Mock.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,8 @@ + (NSUInteger) globalEventsCount {
return globalEvents.count;
}

+ (NSArray *) capturedEventsForPlayer: (NSString *)player {
return [[events objectForKey:player] copy];
}

@end
Loading