I've been able to center the middle of a 16:9 landscape video, crop the video, and then create a 9:16 portrait version of the video similar to how Apple does it in the Photos album.
The only issue is the resulting portrait video isn't centered in the middle of the screen (images below).
How can I get the resulting portrait video in the center of the screen?
func createExportSession(for videoURL: URL) {
let asset = AVURLAsset(url: videoURL)
let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)!
exporter.videoComposition = turnHorizontalVideoToPortraitVideo(asset: asset)
exporter.outputURL = // ...
exporter.outputFileType = AVFileType.mp4
exporter.shouldOptimizeForNetworkUse = true
exporter.exportAsynchronously { [weak self] in
// ...
// the exporter.url is eventually added to an AVURLAsset and played inside an AVPlayer
}
}
func turnHorizontalVideoToPortraitVideo(asset: AVURLAsset) -> AVVideoComposition {
let track = asset.tracks(withMediaType: AVMediaType.video)[0]
let renderSize = CGSize(width: 720, height: 1280)
var transform1 = track.preferredTransform
transform1 = transform1.concatenating(CGAffineTransform(rotationAngle: CGFloat(90.0 * .pi / 180)))
transform1 = transform1.concatenating(CGAffineTransform(translationX: track.naturalSize.width, y: 0))
let transform2 = CGAffineTransform(translationX: track.naturalSize.height, y: (track.naturalSize.width - track.naturalSize.height) / 2)
let transform3 = transform2.rotated(by: CGFloat(Double.pi/2)).concatenating(transform1)
let translate = CGAffineTransform(translationX: renderSize.width, y: renderSize.height)
let rotateFromUpsideDown = translate.rotated(by: CGFloat(Double.pi)) // without this the portrait video is always upside down
let finalTransform = transform3.concatenating(rotateFromUpsideDown)
let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: track)
transformer.setTransform(finalTransform, at: .zero)
let instruction = AVMutableVideoCompositionInstruction()
instruction.timeRange = CMTimeRange(start: .zero, duration: asset.duration)
instruction.layerInstructions = [transformer]
let videoComposition = AVMutableVideoComposition()
videoComposition.frameDuration = CMTime(value: 1, timescale: 30)
videoComposition.renderSize = renderSize
videoComposition.instructions = [instruction]
return videoComposition
}
Initial horizontal video:
Resulting portrait video after running the above code. The portrait video is incorrectly centered:
This is the way that it should be centered:
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Created
On my purchase page I use RevenueCat to make the initial purchase and in my SettingsVC I have the in-app purchase API (as required) to later resubscribe:
// ...
try await AppStore.showManageSubscriptions(in: window as! UIWindowScene)
I followed these directions and these directions. Using an iPhone 8 simulator I logged into iCloud as a sandbox tester eg. sandboxtest%test.com, then logged into its Settings, loaded my app, made a purchase, the subscription went through and eventually expired. While in the iPhone 8 I checked my SettingsVC > in-app purchase API and it said Expired Nov 5, 2023 ... Select an option to resubscribe. So it worked. I send sandboxtest%test.com and the pw to App Review and got the below rejection:
Guideline 2.1 - Performance - App Completeness
We discovered one or more bugs in your app. Specifically, your app
displayed an error page when the Mange Subscription tab was tapped. We
found that while you have submitted in-app purchase products for your
app, the in-app purchase functionality is not present in your binary.
If you would like to include in-app purchases in your app, you will
need to upload a new binary that incorporates the in-app purchase API
to enable users to make a purchase
They sent me a screenshot and the in-app purchase API said Cannot Connect - Retry.
I later use my actual device and try these 3 ways:
1- While logged in as myself, without using any Sandbox Account, delete the app, run it again, then log into my app with sandboxtest%test.com
2- While logged in as myself, use a different sandbox tester such as whatever%test.com to log into the Sandbox Account, delete the app, run it again, then log into my app with sandboxtest%test.com
3- While logged in as myself, use sandboxtest%test.com for the Sandbox Account, delete the app, run it again, then log into my app with sandboxtest%test.com
For all 3 RevenueCat prints the initial subscription and the expiration date, but for some reason when I go to the in-app purchase API, it always returns You do not have any subscriptions.
What's strange is when I go back to the iPhone 8 while still logged in as sandboxtest%test.com, the in-app purchase API still shows Expired Nov 5, 2023 ... Select an option to resubscribe.
I'm kinda lost here because to use an actual device to login, it sends a SMS, so I don't see how giving the App Reviewer the sandboxtest%test.com/pw info to login into the device and iCloud will help him/her make a purchase because they can't get the SMS. I would assume they would only need sandboxtest%test.com, but that does't work for them.
Any advice?