Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.8.9

* Resolve `tracksWithMediaType:` deprecations.
* Use `loadTracksWithMediaType:completionHandler:` for iOS 15.0+/macOS 12.0+.

## 2.8.8

* Refactors Dart internals for maintainability.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1051,4 +1051,41 @@ - (nonnull AVPlayerItem *)playerItemWithURL:(NSURL *)url {
return [AVPlayerItem playerItemWithAsset:[AVURLAsset URLAssetWithURL:url options:nil]];
}

- (void)testLoadTracksWithMediaTypeIsCalledOnNewerOS {
if (@available(iOS 15.0, macOS 12.0, *)) {
AVAsset *mockAsset = OCMClassMock([AVAsset class]);
AVPlayerItem *mockItem = OCMClassMock([AVPlayerItem class]);
OCMStub([mockItem asset]).andReturn(mockAsset);

// Stub loadValuesAsynchronouslyForKeys to immediately call completion
OCMStub([mockAsset loadValuesAsynchronouslyForKeys:[OCMArg any]
completionHandler:[OCMArg invokeBlock]]);

// Stub statusOfValueForKey to return Loaded
OCMStub([mockAsset statusOfValueForKey:@"tracks" error:[OCMArg anyObjectRef]])
.andReturn(AVKeyValueStatusLoaded);

// Expect loadTracksWithMediaType:completionHandler:
XCTestExpectation *expectation =
[self expectationWithDescription:@"loadTracksWithMediaType called"];
OCMExpect([mockAsset loadTracksWithMediaType:AVMediaTypeVideo completionHandler:[OCMArg any]])
.andDo(^(NSInvocation *invocation) {
[expectation fulfill];
// Invoke the completion handler to prevent leaks or hangs if the code waits for it
void (^completion)(NSArray<AVAssetTrack *> *, NSError *);
[invocation getArgument:&completion atIndex:3];
completion(@[], nil);
});

StubFVPAVFactory *stubAVFactory = [[StubFVPAVFactory alloc] initWithPlayer:nil output:nil];
FVPVideoPlayer *player =
[[FVPVideoPlayer alloc] initWithPlayerItem:mockItem
avFactory:stubAVFactory
viewProvider:[[StubViewProvider alloc] initWithView:nil]];
(void)player; // Keep reference

[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -82,32 +82,54 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item
AVAsset *asset = [item asset];
void (^assetCompletionHandler)(void) = ^{
if ([asset statusOfValueForKey:@"tracks" error:nil] == AVKeyValueStatusLoaded) {
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if ([tracks count] > 0) {
AVAssetTrack *videoTrack = tracks[0];
void (^trackCompletionHandler)(void) = ^{
if (self->_disposed) return;
if ([videoTrack statusOfValueForKey:@"preferredTransform"
error:nil] == AVKeyValueStatusLoaded) {
// Rotate the video by using a videoComposition and the preferredTransform
self->_preferredTransform = FVPGetStandardizedTransformForTrack(videoTrack);
// Do not use video composition when it is not needed.
if (CGAffineTransformIsIdentity(self->_preferredTransform)) {
return;
void (^processVideoTracks)(NSArray<AVAssetTrack *> *) = ^(NSArray<AVAssetTrack *> *tracks) {
if ([tracks count] > 0) {
AVAssetTrack *videoTrack = tracks[0];
void (^trackCompletionHandler)(void) = ^{
if (self->_disposed) return;
if ([videoTrack statusOfValueForKey:@"preferredTransform"
error:nil] == AVKeyValueStatusLoaded) {
// Rotate the video by using a videoComposition and the preferredTransform
self->_preferredTransform = FVPGetStandardizedTransformForTrack(videoTrack);
// Do not use video composition when it is not needed.
if (CGAffineTransformIsIdentity(self->_preferredTransform)) {
return;
}
// Note:
// https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition
// Video composition can only be used with file-based media and is not supported for
// use with media served using HTTP Live Streaming.
AVMutableVideoComposition *videoComposition =
[self getVideoCompositionWithTransform:self->_preferredTransform
withAsset:asset
withVideoTrack:videoTrack];
item.videoComposition = videoComposition;
}
// Note:
// https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition
// Video composition can only be used with file-based media and is not supported for
// use with media served using HTTP Live Streaming.
AVMutableVideoComposition *videoComposition =
[self getVideoCompositionWithTransform:self->_preferredTransform
withAsset:asset
withVideoTrack:videoTrack];
item.videoComposition = videoComposition;
}
};
[videoTrack loadValuesAsynchronouslyForKeys:@[ @"preferredTransform" ]
completionHandler:trackCompletionHandler];
};
[videoTrack loadValuesAsynchronouslyForKeys:@[ @"preferredTransform" ]
completionHandler:trackCompletionHandler];
}
};

// Use the new async API on iOS 15.0+/macOS 12.0+, fall back to deprecated API on older
// versions
if (@available(iOS 15.0, macOS 12.0, *)) {
[asset loadTracksWithMediaType:AVMediaTypeVideo
completionHandler:^(NSArray<AVAssetTrack *> *_Nullable tracks,
NSError *_Nullable error) {
if (error == nil && tracks != nil) {
processVideoTracks(tracks);
} else if (error != nil) {
NSLog(@"Error loading tracks: %@", error);
}

Choose a reason for hiding this comment

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

medium

The completion handler for loadTracksWithMediaType:completionHandler: checks for error == nil, but if an error occurs, it is not handled or logged. It's good practice to at least log the error to aid in debugging, even if no further action is taken.

                       if (error == nil && tracks != nil) {
                         processVideoTracks(tracks);
                       } else if (error != nil) {
                         NSLog(@"Error loading tracks: %@", error);
                       }

Copy link
Contributor

Choose a reason for hiding this comment

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

i think this is good feedback

}];
} else {
// For older OS versions, use the deprecated API with warning suppression
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
#pragma clang diagnostic pop
processVideoTracks(tracks);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: video_player_avfoundation
description: iOS and macOS implementation of the video_player plugin.
repository: https://github.com/flutter/packages/tree/main/packages/video_player/video_player_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
version: 2.8.8
version: 2.8.9

environment:
sdk: ^3.9.0
Expand Down