The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

223 lines
5.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. struct MovieComponent::Pimpl
  18. {
  19. bool open (MovieComponent& parent, const String& newPath)
  20. {
  21. JUCE_AUTORELEASEPOOL
  22. {
  23. close();
  24. NSString* videoFile = [NSString stringWithUTF8String: newPath.toUTF8()];
  25. NSURL* url = [NSURL fileURLWithPath: videoFile];
  26. AVAsset* asset = [AVAsset assetWithURL: url];
  27. duration = CMTimeGetSeconds (asset.duration);
  28. nativeSize = [[[asset tracksWithMediaType: AVMediaTypeVideo] objectAtIndex: 0] naturalSize];
  29. if (duration <= 0)
  30. return false;
  31. auto frame = CGRectMake (0, 0, nativeSize.width, nativeSize.height);
  32. view = [[NSView alloc] initWithFrame: frame];
  33. [view setHidden: NO];
  34. [view setNeedsDisplay: YES];
  35. [view setWantsLayer: YES];
  36. [view makeBackingLayer];
  37. parent.setView (view);
  38. path = newPath;
  39. player = [[AVPlayer alloc] initWithURL: url];
  40. player.actionAtItemEnd = AVPlayerActionAtItemEndPause;
  41. player.masterClock = CMClockGetHostTimeClock();
  42. player.automaticallyWaitsToMinimizeStalling = NO;
  43. [player pause];
  44. playerLayer = [[AVPlayerLayer playerLayerWithPlayer: player] retain];
  45. [playerLayer setFrame: frame];
  46. [playerLayer setHidden: NO];
  47. [[view layer] addSublayer: playerLayer];
  48. parent.resized();
  49. }
  50. return true;
  51. }
  52. void close()
  53. {
  54. [playerLayer release];
  55. playerLayer = nil;
  56. [player release];
  57. player = nil;
  58. [view release];
  59. view = nil;
  60. playing = false;
  61. duration = 0;
  62. nativeSize = { 0, 0 };
  63. path = {};
  64. }
  65. String path;
  66. NSView* view = nil;
  67. AVPlayer* player = nil;
  68. AVPlayerLayer* playerLayer = nil;
  69. double duration = 0;
  70. CGSize nativeSize = { 0, 0 };
  71. bool playing = false;
  72. };
  73. MovieComponent::MovieComponent() : pimpl (new Pimpl()) {}
  74. MovieComponent::~MovieComponent()
  75. {
  76. closeMovie();
  77. pimpl = nullptr;
  78. }
  79. bool MovieComponent::loadMovie (const File& file)
  80. {
  81. return file.existsAsFile()
  82. && pimpl->open (*this, file.getFullPathName());
  83. }
  84. bool MovieComponent::loadMovie (const URL& file)
  85. {
  86. return pimpl->open (*this, file.toString (true));
  87. }
  88. void MovieComponent::closeMovie()
  89. {
  90. setView (nullptr);
  91. pimpl->close();
  92. }
  93. bool MovieComponent::isMovieOpen() const
  94. {
  95. return pimpl->player != nil && pimpl->player.status != AVPlayerStatusFailed;
  96. }
  97. String MovieComponent::getCurrentMoviePath() const
  98. {
  99. return pimpl->path;
  100. }
  101. void MovieComponent::play()
  102. {
  103. pimpl->playing = true;
  104. [pimpl->player play];
  105. }
  106. void MovieComponent::stop()
  107. {
  108. pimpl->playing = false;
  109. [pimpl->player pause];
  110. }
  111. double MovieComponent::getDuration() const
  112. {
  113. return pimpl->duration;
  114. }
  115. double MovieComponent::getPosition() const
  116. {
  117. return CMTimeGetSeconds ([pimpl->player currentTime]);
  118. }
  119. void MovieComponent::setPosition (double seconds)
  120. {
  121. auto delta = std::abs (seconds - getPosition());
  122. auto newTime = CMTimeMakeWithSeconds (seconds, 600);
  123. if (! pimpl->playing)
  124. {
  125. [pimpl->player seekToTime: newTime
  126. toleranceBefore: kCMTimeZero
  127. toleranceAfter: kCMTimeZero];
  128. }
  129. else if (delta > 0.035)
  130. {
  131. auto masterClock = CMClockGetTime (CMClockGetHostTimeClock());
  132. try
  133. {
  134. [pimpl->player setRate: 1.0f
  135. time: newTime
  136. atHostTime: masterClock];
  137. }
  138. catch (...)
  139. {
  140. // We should never end up in here, unless somehow automaticallyWaitsToMinimizeStalling
  141. // got reset to YES
  142. jassertfalse;
  143. if (delta > 0.3)
  144. [pimpl->player seekToTime: newTime
  145. toleranceBefore: kCMTimeZero
  146. toleranceAfter: kCMTimeZero];
  147. }
  148. }
  149. }
  150. void MovieComponent::setVolume (float newVolume)
  151. {
  152. pimpl->player.volume = (CGFloat) newVolume;
  153. }
  154. float MovieComponent::getVolume() const
  155. {
  156. return (float) pimpl->player.volume;
  157. }
  158. Rectangle<int> MovieComponent::getNativeSize() const
  159. {
  160. return { 0, 0, roundToInt (pimpl->nativeSize.width), roundToInt (pimpl->nativeSize.height) };
  161. }
  162. void MovieComponent::setBoundsWithCorrectAspectRatio (Rectangle<int> spaceToFitWithin, RectanglePlacement placement)
  163. {
  164. auto nativeSize = getNativeSize();
  165. setBounds ((spaceToFitWithin.isEmpty() || nativeSize.isEmpty())
  166. ? spaceToFitWithin
  167. : placement.appliedTo (nativeSize, spaceToFitWithin));
  168. }
  169. void MovieComponent::resized()
  170. {
  171. JUCE_AUTORELEASEPOOL
  172. {
  173. auto frame = CGRectMake (0, 0, (CGFloat) getWidth(), (CGFloat) getHeight());
  174. [pimpl->view setFrame: frame];
  175. [pimpl->playerLayer setFrame: frame];
  176. }
  177. }