mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
226 lines
6.1 KiB
Text
226 lines
6.1 KiB
Text
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2017 - ROLI Ltd.
|
|
|
|
JUCE is an open source library subject to commercial or open-source
|
|
licensing.
|
|
|
|
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
|
27th April 2017).
|
|
|
|
End User License Agreement: www.juce.com/juce-5-licence
|
|
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
|
|
|
Or: You may also use this code under the terms of the GPL v3 (see
|
|
www.gnu.org/licenses).
|
|
|
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
|
DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
struct MovieComponent::Pimpl
|
|
{
|
|
bool open (MovieComponent& parent, const String& newPath)
|
|
{
|
|
JUCE_AUTORELEASEPOOL
|
|
{
|
|
close();
|
|
|
|
NSURL* url = createNSURLFromFile (newPath);
|
|
AVAsset* asset = [AVAsset assetWithURL: url];
|
|
duration = CMTimeGetSeconds (asset.duration);
|
|
nativeSize = [[[asset tracksWithMediaType: AVMediaTypeVideo] objectAtIndex: 0] naturalSize];
|
|
|
|
if (duration <= 0)
|
|
return false;
|
|
|
|
auto frame = CGRectMake (0, 0, nativeSize.width, nativeSize.height);
|
|
|
|
view = [[NSView alloc] initWithFrame: NSRectFromCGRect (frame)];
|
|
[view setHidden: NO];
|
|
[view setNeedsDisplay: YES];
|
|
[view setWantsLayer: YES];
|
|
[view makeBackingLayer];
|
|
|
|
parent.setView (view);
|
|
path = newPath;
|
|
|
|
player = [[AVPlayer alloc] initWithURL: url];
|
|
player.actionAtItemEnd = AVPlayerActionAtItemEndPause;
|
|
player.masterClock = CMClockGetHostTimeClock();
|
|
|
|
if ([player respondsToSelector: @selector (setAutomaticallyWaitsToMinimizeStalling:)])
|
|
[player performSelector: @selector (setAutomaticallyWaitsToMinimizeStalling:)
|
|
withObject: nil];
|
|
|
|
[player pause];
|
|
|
|
playerLayer = [[AVPlayerLayer playerLayerWithPlayer: player] retain];
|
|
[playerLayer setFrame: frame];
|
|
[playerLayer setHidden: NO];
|
|
|
|
[[view layer] addSublayer: playerLayer];
|
|
|
|
parent.resized();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void close()
|
|
{
|
|
[playerLayer release];
|
|
playerLayer = nil;
|
|
|
|
[player release];
|
|
player = nil;
|
|
|
|
[view release];
|
|
view = nil;
|
|
|
|
playing = false;
|
|
duration = 0;
|
|
nativeSize = { 0, 0 };
|
|
path = {};
|
|
}
|
|
|
|
String path;
|
|
NSView* view = nil;
|
|
AVPlayer* player = nil;
|
|
AVPlayerLayer* playerLayer = nil;
|
|
double duration = 0;
|
|
CGSize nativeSize = { 0, 0 };
|
|
bool playing = false;
|
|
};
|
|
|
|
MovieComponent::MovieComponent() : pimpl (new Pimpl()) {}
|
|
|
|
MovieComponent::~MovieComponent()
|
|
{
|
|
closeMovie();
|
|
pimpl = nullptr;
|
|
}
|
|
|
|
bool MovieComponent::loadMovie (const File& file)
|
|
{
|
|
return file.existsAsFile()
|
|
&& pimpl->open (*this, file.getFullPathName());
|
|
}
|
|
|
|
bool MovieComponent::loadMovie (const URL& file)
|
|
{
|
|
return pimpl->open (*this, file.toString (true));
|
|
}
|
|
|
|
void MovieComponent::closeMovie()
|
|
{
|
|
setView (nullptr);
|
|
pimpl->close();
|
|
}
|
|
|
|
bool MovieComponent::isMovieOpen() const
|
|
{
|
|
return pimpl->player != nil && pimpl->player.status != AVPlayerStatusFailed;
|
|
}
|
|
|
|
String MovieComponent::getCurrentMoviePath() const
|
|
{
|
|
return pimpl->path;
|
|
}
|
|
|
|
void MovieComponent::play()
|
|
{
|
|
pimpl->playing = true;
|
|
[pimpl->player play];
|
|
}
|
|
|
|
void MovieComponent::stop()
|
|
{
|
|
pimpl->playing = false;
|
|
[pimpl->player pause];
|
|
}
|
|
|
|
double MovieComponent::getDuration() const
|
|
{
|
|
return pimpl->duration;
|
|
}
|
|
|
|
double MovieComponent::getPosition() const
|
|
{
|
|
return CMTimeGetSeconds ([pimpl->player currentTime]);
|
|
}
|
|
|
|
void MovieComponent::setPosition (double seconds)
|
|
{
|
|
auto delta = std::abs (seconds - getPosition());
|
|
auto newTime = CMTimeMakeWithSeconds (seconds, 600);
|
|
|
|
if (! pimpl->playing)
|
|
{
|
|
[pimpl->player seekToTime: newTime
|
|
toleranceBefore: kCMTimeZero
|
|
toleranceAfter: kCMTimeZero];
|
|
}
|
|
else if (delta > 0.035)
|
|
{
|
|
auto masterClock = CMClockGetTime (CMClockGetHostTimeClock());
|
|
|
|
try
|
|
{
|
|
[pimpl->player setRate: 1.0f
|
|
time: newTime
|
|
atHostTime: masterClock];
|
|
}
|
|
catch (...)
|
|
{
|
|
// We should never end up in here, unless somehow automaticallyWaitsToMinimizeStalling
|
|
// got reset to YES
|
|
jassertfalse;
|
|
|
|
if (delta > 0.3)
|
|
[pimpl->player seekToTime: newTime
|
|
toleranceBefore: kCMTimeZero
|
|
toleranceAfter: kCMTimeZero];
|
|
}
|
|
}
|
|
}
|
|
|
|
void MovieComponent::setVolume (float newVolume)
|
|
{
|
|
pimpl->player.volume = static_cast<float> (newVolume);
|
|
}
|
|
|
|
float MovieComponent::getVolume() const
|
|
{
|
|
return (float) pimpl->player.volume;
|
|
}
|
|
|
|
Rectangle<int> MovieComponent::getNativeSize() const
|
|
{
|
|
return { 0, 0, roundToInt (pimpl->nativeSize.width), roundToInt (pimpl->nativeSize.height) };
|
|
}
|
|
|
|
void MovieComponent::setBoundsWithCorrectAspectRatio (Rectangle<int> spaceToFitWithin, RectanglePlacement placement)
|
|
{
|
|
auto nativeSize = getNativeSize();
|
|
|
|
setBounds ((spaceToFitWithin.isEmpty() || nativeSize.isEmpty())
|
|
? spaceToFitWithin
|
|
: placement.appliedTo (nativeSize, spaceToFitWithin));
|
|
}
|
|
|
|
void MovieComponent::resized()
|
|
{
|
|
JUCE_AUTORELEASEPOOL
|
|
{
|
|
auto frame = CGRectMake (0, 0, (CGFloat) getWidth(), (CGFloat) getHeight());
|
|
[pimpl->view setFrame: NSRectFromCGRect (frame)];
|
|
[pimpl->playerLayer setFrame: frame];
|
|
}
|
|
}
|