Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit d45e721

Browse files
committed
Apply the correct transform for videos with different orientations
1 parent 04f64ec commit d45e721

File tree

6 files changed

+165
-26
lines changed

6 files changed

+165
-26
lines changed

packages/video_player/video_player_avfoundation/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.3.2
2+
3+
* Apply the correct transform for videos with different orientations.
4+
15
## 2.3.1
26

37
* Renames internal method channels to avoid potential confusion with the

packages/video_player/video_player_avfoundation/example/ios/RunnerTests/VideoPlayerTests.m

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,59 @@
77
@import XCTest;
88

99
#import <OCMock/OCMock.h>
10+
#import <video_player_avfoundation/AVAssetTrack+Utils.h>
1011

1112
@interface FLTVideoPlayer : NSObject <FlutterStreamHandler>
1213
@property(readonly, nonatomic) AVPlayer *player;
14+
@property(readonly, nonatomic) CGAffineTransform preferredTransform;
1315
@end
1416

1517
@interface FLTVideoPlayerPlugin (Test) <FLTAVFoundationVideoPlayerApi>
1618
@property(readonly, strong, nonatomic)
1719
NSMutableDictionary<NSNumber *, FLTVideoPlayer *> *playersByTextureId;
1820
@end
1921

22+
@interface FakeAVAssetTrack : AVAssetTrack
23+
@property(readonly, nonatomic) CGAffineTransform preferredTransform;
24+
@property(readonly, nonatomic) CGSize naturalSize;
25+
@property(readonly, nonatomic) UIImageOrientation orientation;
26+
- (instancetype)initWithOrientation:(UIImageOrientation)orientation;
27+
@end
28+
2029
@interface VideoPlayerTests : XCTestCase
2130
@end
2231

32+
@implementation FakeAVAssetTrack
33+
34+
- (instancetype)initWithOrientation:(UIImageOrientation)orientation {
35+
_orientation = orientation;
36+
_naturalSize = CGSizeMake(800, 600);
37+
return self;
38+
}
39+
40+
- (CGAffineTransform) preferredTransform {
41+
switch (_orientation) {
42+
case UIImageOrientationUp:
43+
return CGAffineTransformMake(1, 0, 0, 1, 0, 0);
44+
case UIImageOrientationUpMirrored:
45+
return CGAffineTransformMake(-1, 0, 0, 1, 0, 0);
46+
case UIImageOrientationDown:
47+
return CGAffineTransformMake(-1, 0, 0, -1, 0, 0);
48+
case UIImageOrientationDownMirrored:
49+
return CGAffineTransformMake(1, 0, 0, -1, 0, 0);
50+
case UIImageOrientationLeft:
51+
return CGAffineTransformMake(0, -1, 1, 0, 0, 0);
52+
case UIImageOrientationLeftMirrored:
53+
return CGAffineTransformMake(0, -1, -1, 0, 0, 0);
54+
case UIImageOrientationRight:
55+
return CGAffineTransformMake(0, 1, -1, 0, 0, 0);
56+
case UIImageOrientationRightMirrored:
57+
return CGAffineTransformMake(0, 1, 1, 0, 0, 0);
58+
}
59+
}
60+
61+
@end
62+
2363
@implementation VideoPlayerTests
2464

2565
- (void)testSeekToInvokesTextureFrameAvailableOnTextureRegistry {
@@ -121,6 +161,17 @@ - (void)testHLSControls {
121161
XCTAssertEqualWithAccuracy([videoInitialization[@"duration"] intValue], 4000, 200);
122162
}
123163

164+
- (void)testTransformFix {
165+
[self testTransformFixByOrientation:UIImageOrientationUp];
166+
[self testTransformFixByOrientation:UIImageOrientationDown];
167+
[self testTransformFixByOrientation:UIImageOrientationLeft];
168+
[self testTransformFixByOrientation:UIImageOrientationRight];
169+
[self testTransformFixByOrientation:UIImageOrientationUpMirrored];
170+
[self testTransformFixByOrientation:UIImageOrientationDownMirrored];
171+
[self testTransformFixByOrientation:UIImageOrientationLeftMirrored];
172+
[self testTransformFixByOrientation:UIImageOrientationRightMirrored];
173+
}
174+
124175
- (NSDictionary<NSString *, id> *)testPlugin:(FLTVideoPlayerPlugin *)videoPlayerPlugin
125176
uri:(NSString *)uri {
126177
FlutterError *error;
@@ -175,4 +226,47 @@ - (void)testHLSControls {
175226
return initializationEvent;
176227
}
177228

229+
- (void)testTransformFixByOrientation:(UIImageOrientation)orientation {
230+
AVAssetTrack *track = [[FakeAVAssetTrack alloc] initWithOrientation:orientation];
231+
CGAffineTransform t = [track fixTransform];
232+
CGSize size = track.naturalSize;
233+
CGFloat expectX, expectY;
234+
switch (orientation) {
235+
case UIImageOrientationUp:
236+
expectX = 0;
237+
expectY = 0;
238+
break;
239+
case UIImageOrientationDown:
240+
expectX = size.width;
241+
expectY = size.height;
242+
break;
243+
case UIImageOrientationLeft:
244+
expectX = 0;
245+
expectY = size.width;
246+
break;
247+
case UIImageOrientationRight:
248+
expectX = size.height;
249+
expectY = 0;
250+
break;
251+
case UIImageOrientationUpMirrored:
252+
expectX = size.width;
253+
expectY = 0;
254+
break;
255+
case UIImageOrientationDownMirrored:
256+
expectX = 0;
257+
expectY = size.height;
258+
break;
259+
case UIImageOrientationLeftMirrored:
260+
expectX = size.height;
261+
expectY = size.width;
262+
break;
263+
case UIImageOrientationRightMirrored:
264+
expectX = 0;
265+
expectY = 0;
266+
break;
267+
}
268+
XCTAssertEqual(t.tx, expectX);
269+
XCTAssertEqual(t.ty, expectY);
270+
}
271+
178272
@end
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <AVFoundation/AVFoundation.h>
6+
7+
NS_ASSUME_NONNULL_BEGIN
8+
9+
// Fix the transform for the track.
10+
// Each fix case corresponding to `UIImage.Orientation`, with 8 cases in total.
11+
@interface AVAssetTrack (Utils)
12+
- (CGAffineTransform)fixTransform;
13+
@end
14+
15+
NS_ASSUME_NONNULL_END
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "AVAssetTrack+Utils.h"
6+
7+
@implementation AVAssetTrack (Utils)
8+
9+
- (CGAffineTransform)fixTransform {
10+
CGAffineTransform t = self.preferredTransform;
11+
CGSize size = self.naturalSize;
12+
if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == 1) {
13+
// UIImageOrientationUp
14+
t.tx = 0;
15+
t.ty = 0;
16+
} else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == -1) {
17+
// UIImageOrientationDown
18+
t.tx = size.width;
19+
t.ty = size.height;
20+
} else if (t.a == 0 && t.b == -1 && t.c == 1 && t.d == 0) {
21+
// UIImageOrientationLeft
22+
t.tx = 0;
23+
t.ty = size.width;
24+
} else if (t.a == 0 && t.b == 1 && t.c == -1 && t.d == 0) {
25+
// UIImageOrientationRight
26+
t.tx = size.height;
27+
t.ty = 0;
28+
} else if (t.a == -1 && t.b == 0 && t.c == 0 && t.d == 1) {
29+
// UIImageOrientationUpMirrored
30+
t.tx = size.width;
31+
t.ty = 0;
32+
} else if (t.a == 1 && t.b == 0 && t.c == 0 && t.d == -1) {
33+
// UIImageOrientationDownMirrored
34+
t.tx = 0;
35+
t.ty = size.height;
36+
} else if (t.a == 0 && t.b == -1 && t.c == -1 && t.d == 0) {
37+
// UIImageOrientationLeftMirrored
38+
t.tx = size.height;
39+
t.ty = size.width;
40+
} else if (t.a == 0 && t.b == 1 && t.c == 1 && t.d == 0) {
41+
// UIImageOrientationRightMirrored
42+
t.tx = 0;
43+
t.ty = 0;
44+
}
45+
return t;
46+
}
47+
48+
@end

packages/video_player/video_player_avfoundation/ios/Classes/FLTVideoPlayerPlugin.m

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#import "FLTVideoPlayerPlugin.h"
65
#import <AVFoundation/AVFoundation.h>
76
#import <GLKit/GLKit.h>
7+
#import "AVAssetTrack+Utils.h"
8+
#import "FLTVideoPlayerPlugin.h"
89
#import "messages.g.h"
910

1011
#if !__has_feature(objc_arc)
@@ -187,29 +188,6 @@ - (instancetype)initWithURL:(NSURL *)url
187188
return [self initWithPlayerItem:item frameUpdater:frameUpdater];
188189
}
189190

190-
- (CGAffineTransform)fixTransform:(AVAssetTrack *)videoTrack {
191-
CGAffineTransform transform = videoTrack.preferredTransform;
192-
// TODO(@recastrodiaz): why do we need to do this? Why is the preferredTransform incorrect?
193-
// At least 2 user videos show a black screen when in portrait mode if we directly use the
194-
// videoTrack.preferredTransform Setting tx to the height of the video instead of 0, properly
195-
// displays the video https://github.com/flutter/flutter/issues/17606#issuecomment-413473181
196-
if (transform.tx == 0 && transform.ty == 0) {
197-
NSInteger rotationDegrees = (NSInteger)round(radiansToDegrees(atan2(transform.b, transform.a)));
198-
NSLog(@"TX and TY are 0. Rotation: %ld. Natural width,height: %f, %f", (long)rotationDegrees,
199-
videoTrack.naturalSize.width, videoTrack.naturalSize.height);
200-
if (rotationDegrees == 90) {
201-
NSLog(@"Setting transform tx");
202-
transform.tx = videoTrack.naturalSize.height;
203-
transform.ty = 0;
204-
} else if (rotationDegrees == 270) {
205-
NSLog(@"Setting transform ty");
206-
transform.tx = 0;
207-
transform.ty = videoTrack.naturalSize.width;
208-
}
209-
}
210-
return transform;
211-
}
212-
213191
- (instancetype)initWithPlayerItem:(AVPlayerItem *)item
214192
frameUpdater:(FLTFrameUpdater *)frameUpdater {
215193
self = [super init];
@@ -226,7 +204,7 @@ - (instancetype)initWithPlayerItem:(AVPlayerItem *)item
226204
if ([videoTrack statusOfValueForKey:@"preferredTransform"
227205
error:nil] == AVKeyValueStatusLoaded) {
228206
// Rotate the video by using a videoComposition and the preferredTransform
229-
self->_preferredTransform = [self fixTransform:videoTrack];
207+
self->_preferredTransform = [videoTrack fixTransform];
230208
// Note:
231209
// https://developer.apple.com/documentation/avfoundation/avplayeritem/1388818-videocomposition
232210
// Video composition can only be used with file-based media and is not supported for

packages/video_player/video_player_avfoundation/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: video_player_avfoundation
22
description: iOS implementation of the video_player plugin.
33
repository: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player_avfoundation
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+video_player%22
5-
version: 2.3.1
5+
version: 2.3.2
66

77
environment:
88
sdk: ">=2.14.0 <3.0.0"

0 commit comments

Comments
 (0)