MediaPreviewVideoViewController.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. //
  2. // MediaPreviewVideoViewController.swift
  3. // Mastodon
  4. //
  5. // Created by MainasuK on 2022-2-9.
  6. //
  7. import os.log
  8. import UIKit
  9. import AVKit
  10. import Combine
  11. import func AVFoundation.AVMakeRect
  12. final class MediaPreviewVideoViewController: UIViewController {
  13. let logger = Logger(subsystem: "MediaPreviewVideoViewController", category: "ViewController")
  14. var disposeBag = Set<AnyCancellable>()
  15. var viewModel: MediaPreviewVideoViewModel!
  16. let playerViewController = AVPlayerViewController()
  17. let previewImageView = UIImageView()
  18. deinit {
  19. os_log("%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
  20. playerViewController.player?.pause()
  21. try? AVAudioSession.sharedInstance().setCategory(.ambient)
  22. try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
  23. }
  24. }
  25. extension MediaPreviewVideoViewController {
  26. override func viewDidLoad() {
  27. super.viewDidLoad()
  28. addChild(playerViewController)
  29. playerViewController.view.translatesAutoresizingMaskIntoConstraints = false
  30. view.addSubview(playerViewController.view)
  31. NSLayoutConstraint.activate([
  32. playerViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
  33. playerViewController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
  34. playerViewController.view.widthAnchor.constraint(equalTo: view.widthAnchor),
  35. playerViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor),
  36. ])
  37. if let contentOverlayView = playerViewController.contentOverlayView {
  38. previewImageView.translatesAutoresizingMaskIntoConstraints = false
  39. contentOverlayView.addSubview(previewImageView)
  40. NSLayoutConstraint.activate([
  41. previewImageView.topAnchor.constraint(equalTo: contentOverlayView.topAnchor),
  42. previewImageView.leadingAnchor.constraint(equalTo: contentOverlayView.leadingAnchor),
  43. previewImageView.trailingAnchor.constraint(equalTo: contentOverlayView.trailingAnchor),
  44. previewImageView.bottomAnchor.constraint(equalTo: contentOverlayView.bottomAnchor),
  45. ])
  46. }
  47. playerViewController.delegate = self
  48. playerViewController.view.backgroundColor = .clear
  49. playerViewController.player = viewModel.player
  50. playerViewController.allowsPictureInPicturePlayback = true
  51. switch viewModel.item {
  52. case .video:
  53. break
  54. case .gif:
  55. playerViewController.showsPlaybackControls = false
  56. }
  57. viewModel.player?.play()
  58. viewModel.playbackState = .playing
  59. if let previewURL = viewModel.item.previewURL {
  60. previewImageView.contentMode = .scaleAspectFit
  61. previewImageView.af.setImage(
  62. withURL: previewURL,
  63. placeholderImage: .placeholder(color: .systemFill)
  64. )
  65. playerViewController.publisher(for: \.isReadyForDisplay)
  66. .receive(on: DispatchQueue.main)
  67. .sink { [weak self] isReadyForDisplay in
  68. guard let self = self else { return }
  69. self.previewImageView.isHidden = isReadyForDisplay
  70. }
  71. .store(in: &disposeBag)
  72. }
  73. }
  74. override func viewDidLayoutSubviews() {
  75. super.viewDidLayoutSubviews()
  76. playerViewController.didMove(toParent: self)
  77. }
  78. }
  79. // MARK: - ShareActivityProvider
  80. //extension MediaPreviewVideoViewController: ShareActivityProvider {
  81. // var activities: [Any] {
  82. // return []
  83. // }
  84. //
  85. // var applicationActivities: [UIActivity] {
  86. // switch viewModel.item {
  87. // case .gif(let mediaContext):
  88. // guard let url = mediaContext.assetURL else { return [] }
  89. // return [
  90. // SavePhotoActivity(context: viewModel.context, url: url, resourceType: .video)
  91. // ]
  92. // default:
  93. // return []
  94. // }
  95. // }
  96. //}
  97. // MARK: - AVPlayerViewControllerDelegate
  98. extension MediaPreviewVideoViewController: AVPlayerViewControllerDelegate {
  99. }
  100. // MARK: - MediaPreviewTransitionViewController
  101. extension MediaPreviewVideoViewController: MediaPreviewTransitionViewController {
  102. var mediaPreviewTransitionContext: MediaPreviewTransitionContext? {
  103. guard let playerView = playerViewController.view else { return nil }
  104. let _currentFrame: UIImage? = {
  105. guard let player = playerViewController.player else { return nil }
  106. guard let asset = player.currentItem?.asset else { return nil }
  107. let assetImageGenerator = AVAssetImageGenerator(asset: asset)
  108. assetImageGenerator.appliesPreferredTrackTransform = true // fix orientation
  109. do {
  110. let cgImage = try assetImageGenerator.copyCGImage(at: player.currentTime(), actualTime: nil)
  111. let image = UIImage(cgImage: cgImage)
  112. return image
  113. } catch {
  114. return previewImageView.image
  115. }
  116. }()
  117. let _snapshot: UIView? = {
  118. guard let currentFrame = _currentFrame else { return nil }
  119. let size = AVMakeRect(aspectRatio: currentFrame.size, insideRect: view.frame).size
  120. let imageView = UIImageView(frame: CGRect(origin: .zero, size: size))
  121. imageView.image = currentFrame
  122. return imageView
  123. }()
  124. guard let snapshot = _snapshot else {
  125. return nil
  126. }
  127. return MediaPreviewTransitionContext(
  128. transitionView: playerView,
  129. snapshot: snapshot,
  130. snapshotTransitioning: snapshot
  131. )
  132. }
  133. }