From 1c0b935bf6f1e429708cf375ca79ef4d4c81d55e Mon Sep 17 00:00:00 2001 From: hogliux Date: Wed, 1 Feb 2017 15:36:52 +0000 Subject: [PATCH] Replaced QuickTimeMovieComponent with MovieComponent, using AVFoundation on OSX --- modules/juce_video/juce_video.cpp | 12 +- modules/juce_video/juce_video.h | 12 +- .../native/juce_mac_MovieComponent.mm | 222 ++++++++ .../juce_mac_QuickTimeMovieComponent.mm | 341 ------------- .../juce_win32_QuickTimeMovieComponent.cpp | 483 ------------------ .../playback/juce_DirectShowComponent.h | 2 - .../juce_video/playback/juce_MovieComponent.h | 65 +++ .../playback/juce_QuickTimeMovieComponent.h | 217 -------- 8 files changed, 298 insertions(+), 1056 deletions(-) create mode 100644 modules/juce_video/native/juce_mac_MovieComponent.mm delete mode 100644 modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm delete mode 100644 modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp create mode 100644 modules/juce_video/playback/juce_MovieComponent.h delete mode 100644 modules/juce_video/playback/juce_QuickTimeMovieComponent.h diff --git a/modules/juce_video/juce_video.cpp b/modules/juce_video/juce_video.cpp index ca5b01da5e..dd9196c927 100644 --- a/modules/juce_video/juce_video.cpp +++ b/modules/juce_video/juce_video.cpp @@ -38,9 +38,7 @@ #include "juce_video.h" #if JUCE_MAC - #if JUCE_QUICKTIME - #import - #endif + #import //============================================================================== #elif JUCE_WINDOWS @@ -104,9 +102,7 @@ namespace juce #include "native/juce_mac_CameraDevice.mm" #endif - #if JUCE_QUICKTIME - #include "native/juce_mac_QuickTimeMovieComponent.mm" - #endif + #include "native/juce_mac_MovieComponent.mm" #elif JUCE_WINDOWS @@ -118,10 +114,6 @@ namespace juce #include "native/juce_win32_DirectShowComponent.cpp" #endif - #if JUCE_QUICKTIME - #include "native/juce_win32_QuickTimeMovieComponent.cpp" - #endif - #elif JUCE_LINUX #elif JUCE_ANDROID diff --git a/modules/juce_video/juce_video.h b/modules/juce_video/juce_video.h index 6cf159ccb4..758114a623 100644 --- a/modules/juce_video/juce_video.h +++ b/modules/juce_video/juce_video.h @@ -41,7 +41,7 @@ license: GPL/Commercial dependencies: juce_data_structures juce_cryptography - OSXFrameworks: QTKit QuickTime + OSXFrameworks: AVFoundation CoreMedia END_JUCE_MODULE_DECLARATION @@ -99,8 +99,14 @@ namespace juce { -#include "playback/juce_DirectShowComponent.h" -#include "playback/juce_QuickTimeMovieComponent.h" +#if JUCE_DIRECTSHOW || DOXYGEN + #include "playback/juce_DirectShowComponent.h" +#endif + +#if JUCE_MAC || DOXYGEN + #include "playback/juce_MovieComponent.h" +#endif + #include "capture/juce_CameraDevice.h" } diff --git a/modules/juce_video/native/juce_mac_MovieComponent.mm b/modules/juce_video/native/juce_mac_MovieComponent.mm new file mode 100644 index 0000000000..738c887c6b --- /dev/null +++ b/modules/juce_video/native/juce_mac_MovieComponent.mm @@ -0,0 +1,222 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +struct MovieComponent::Pimpl +{ + bool open (MovieComponent& parent, const String& newPath) + { + JUCE_AUTORELEASEPOOL + { + close(); + + NSString* videoFile = [NSString stringWithUTF8String: newPath.toUTF8()]; + NSURL* url = [NSURL fileURLWithPath: videoFile]; + + 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: 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(); + player.automaticallyWaitsToMinimizeStalling = NO; + [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 = (CGFloat) newVolume; +} + +float MovieComponent::getVolume() const +{ + return (float) pimpl->player.volume; +} + +Rectangle MovieComponent::getNativeSize() const +{ + return { 0, 0, roundToInt (pimpl->nativeSize.width), roundToInt (pimpl->nativeSize.height) }; +} + +void MovieComponent::setBoundsWithCorrectAspectRatio (Rectangle 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: frame]; + [pimpl->playerLayer setFrame: frame]; + } +} diff --git a/modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm b/modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm deleted file mode 100644 index a47e447260..0000000000 --- a/modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm +++ /dev/null @@ -1,341 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2015 - ROLI Ltd. - - Permission is granted to use this software under the terms of either: - a) the GPL v2 (or any later version) - b) the Affero GPL v3 - - Details of these licenses can be found at: www.gnu.org/licenses - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.juce.com for more information. - - ============================================================================== -*/ - -#if JUCE_QUICKTIME - -struct NonInterceptingQTMovieViewClass : public ObjCClass -{ - NonInterceptingQTMovieViewClass() : ObjCClass ("JUCEQTMovieView_") - { - addMethod (@selector (hitTest:), hitTest, "@@:", @encode (NSPoint)); - addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@"); - - registerClass(); - } - -private: - static NSView* hitTest (id self, SEL, NSPoint point) - { - if (! [(QTMovieView*) self isControllerVisible]) - return nil; - - objc_super s = { self, [QTMovieView class] }; - return objc_msgSendSuper (&s, @selector (hitTest:), point); - } - - static BOOL acceptsFirstMouse (id, SEL, NSEvent*) - { - return YES; - } -}; - -//============================================================================== -#define theMovie (static_cast (movie)) - -//============================================================================== -QuickTimeMovieComponent::QuickTimeMovieComponent() - : movie (0) -{ - setOpaque (true); - setVisible (true); - - static NonInterceptingQTMovieViewClass cls; - QTMovieView* view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]; - setView (view); - [view setNextResponder: [view superview]]; - [view setWantsLayer: YES]; // prevents the view failing to redraw correctly when paused. - [view release]; -} - -QuickTimeMovieComponent::~QuickTimeMovieComponent() -{ - closeMovie(); - setView (nil); -} - -bool QuickTimeMovieComponent::isQuickTimeAvailable() noexcept -{ - return true; -} - -static QTMovie* openMovieFromStream (InputStream* movieStream, File& movieFile) -{ - // unfortunately, QTMovie objects can only be created on the main thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - QTMovie* movie = nil; - - if (FileInputStream* const fin = dynamic_cast (movieStream)) - { - movieFile = fin->getFile(); - movie = [QTMovie movieWithFile: juceStringToNS (movieFile.getFullPathName()) - error: nil]; - } - else - { - MemoryBlock temp; - movieStream->readIntoMemoryBlock (temp); - - static const char* const suffixesToTry[] = { ".mov", ".mp3", ".avi", ".m4a" }; - - for (int i = 0; i < numElementsInArray (suffixesToTry); ++i) - { - movie = [QTMovie movieWithDataReference: [QTDataReference dataReferenceWithReferenceToData: [NSData dataWithBytes: temp.getData() - length: temp.getSize()] - name: [NSString stringWithUTF8String: suffixesToTry[i]] - MIMEType: nsEmptyString()] - error: nil]; - - if (movie != 0) - break; - } - } - - return movie; -} - -bool QuickTimeMovieComponent::loadMovie (const File& file, const bool showController) -{ - return loadMovie (file.createInputStream(), showController); -} - -bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, const bool showController) -{ - const ScopedPointer movieStreamDeleter (movieStream); - - closeMovie(); - - if (getPeer() == nullptr) - { - // To open a movie, this component must be visible inside a functioning window, so that - // the QT control can be assigned to the window. - jassertfalse; - return false; - } - - if (movieStream == nullptr) - return false; - - movie = openMovieFromStream (movieStream, movieFile); - - [theMovie retain]; - QTMovieView* view = (QTMovieView*) getView(); - [view setMovie: theMovie]; - - controllerVisible = showController; - [view setControllerVisible: controllerVisible]; - setLooping (looping); - - return movie != nil; -} - -bool QuickTimeMovieComponent::loadMovie (const URL& movieURL, const bool showController) -{ - // unfortunately, QTMovie objects can only be created on the main thread.. - jassert (MessageManager::getInstance()->isThisTheMessageThread()); - - closeMovie(); - - if (getPeer() == nullptr) - { - // To open a movie, this component must be visible inside a functioning window, so that - // the QT control can be assigned to the window. - jassertfalse; - return false; - } - - NSURL* url = [NSURL URLWithString: juceStringToNS (movieURL.toString (true))]; - NSError* err; - if ([QTMovie canInitWithURL: url]) - movie = [QTMovie movieWithURL: url error: &err]; - - [theMovie retain]; - QTMovieView* view = (QTMovieView*) getView(); - [view setMovie: theMovie]; - - controllerVisible = showController; - [view setControllerVisible: controllerVisible]; - setLooping (looping); - - return movie != nil; -} - -void QuickTimeMovieComponent::closeMovie() -{ - stop(); - QTMovieView* view = (QTMovieView*) getView(); - [view setMovie: nil]; - [theMovie release]; - movie = 0; - movieFile = File(); -} - -bool QuickTimeMovieComponent::isMovieOpen() const -{ - return movie != nil; -} - -File QuickTimeMovieComponent::getCurrentMovieFile() const -{ - return movieFile; -} - -void QuickTimeMovieComponent::play() -{ - [theMovie play]; -} - -void QuickTimeMovieComponent::stop() -{ - [theMovie stop]; -} - -bool QuickTimeMovieComponent::isPlaying() const -{ - return movie != 0 && [theMovie rate] != 0; -} - -void QuickTimeMovieComponent::setPosition (const double seconds) -{ - if (movie != 0) - { - QTTime t; - t.timeValue = (long long) (100000.0 * seconds); - t.timeScale = 100000; - t.flags = 0; - - [theMovie setCurrentTime: t]; - } -} - -double QuickTimeMovieComponent::getPosition() const -{ - if (movie == 0) - return 0.0; - - QTTime t = [theMovie currentTime]; - return t.timeValue / (double) t.timeScale; -} - -void QuickTimeMovieComponent::setSpeed (const float newSpeed) -{ - [theMovie setRate: newSpeed]; -} - -double QuickTimeMovieComponent::getMovieDuration() const -{ - if (movie == 0) - return 0.0; - - QTTime t = [theMovie duration]; - return t.timeValue / (double) t.timeScale; -} - -void QuickTimeMovieComponent::setLooping (const bool shouldLoop) -{ - looping = shouldLoop; - - [theMovie setAttribute: [NSNumber numberWithBool: shouldLoop] - forKey: QTMovieLoopsAttribute]; -} - -bool QuickTimeMovieComponent::isLooping() const -{ - return looping; -} - -void QuickTimeMovieComponent::setMovieVolume (const float newVolume) -{ - [theMovie setVolume: newVolume]; -} - -float QuickTimeMovieComponent::getMovieVolume() const -{ - return movie != 0 ? [theMovie volume] : 0.0f; -} - -void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const -{ - width = 0; - height = 0; - - if (movie != 0) - { - NSSize s = [[theMovie attributeForKey: QTMovieNaturalSizeAttribute] sizeValue]; - width = (int) s.width; - height = (int) s.height; - } -} - -void QuickTimeMovieComponent::paint (Graphics& g) -{ - if (movie == 0) - g.fillAll (Colours::black); -} - -bool QuickTimeMovieComponent::isControllerVisible() const -{ - return controllerVisible; -} - -//============================================================================== -void QuickTimeMovieComponent::goToStart() -{ - setPosition (0.0); -} - -void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, - RectanglePlacement placement) -{ - int normalWidth, normalHeight; - getMovieNormalSize (normalWidth, normalHeight); - - const Rectangle normalSize (normalWidth, normalHeight); - - if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty())) - setBounds (placement.appliedTo (normalSize, spaceToFitWithin)); - else - setBounds (spaceToFitWithin); -} - - -//============================================================================== -#if ! (JUCE_MAC && JUCE_64BIT) - -bool juce_OpenQuickTimeMovieFromStream (InputStream* movieStream, Movie& result, Handle&) -{ - if (movieStream == nullptr) - return false; - - File file; - QTMovie* movie = openMovieFromStream (movieStream, file); - - if (movie != nil) - result = [movie quickTimeMovie]; - - return movie != nil; -} - -#endif -#endif diff --git a/modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp b/modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp deleted file mode 100644 index c60ad41a7a..0000000000 --- a/modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2015 - ROLI Ltd. - - Permission is granted to use this software under the terms of either: - a) the GPL v2 (or any later version) - b) the Affero GPL v3 - - Details of these licenses can be found at: www.gnu.org/licenses - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.juce.com for more information. - - ============================================================================== -*/ - -using namespace QTOLibrary; -using namespace QTOControlLib; - -bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); - -static bool isQTAvailable = false; - - -//============================================================================== -class QuickTimeMovieComponent::Pimpl -{ -public: - Pimpl() : dataHandle (0) - { - } - - ~Pimpl() - { - clearHandle(); - } - - void clearHandle() - { - if (dataHandle != 0) - { - DisposeHandle (dataHandle); - dataHandle = 0; - } - } - - IQTControlPtr qtControl; - IQTMoviePtr qtMovie; - Handle dataHandle; -}; - -//============================================================================== -QuickTimeMovieComponent::QuickTimeMovieComponent() - : movieLoaded (false), - controllerVisible (true) -{ - pimpl = new Pimpl(); - setMouseEventsAllowed (false); -} - -QuickTimeMovieComponent::~QuickTimeMovieComponent() -{ - closeMovie(); - pimpl->qtControl = 0; - deleteControl(); - pimpl = nullptr; -} - -bool QuickTimeMovieComponent::isQuickTimeAvailable() noexcept -{ - if (! isQTAvailable) - isQTAvailable = (InitializeQTML (0) == noErr) && (EnterMovies() == noErr); - - return isQTAvailable; -} - -//============================================================================== -void QuickTimeMovieComponent::createControlIfNeeded() -{ - if (isShowing() && ! isControlCreated()) - { - const IID qtIID = __uuidof (QTControl); - - if (createControl (&qtIID)) - { - const IID qtInterfaceIID = __uuidof (IQTControl); - pimpl->qtControl = (IQTControl*) queryInterface (&qtInterfaceIID); - - if (pimpl->qtControl != nullptr) - { - pimpl->qtControl->Release(); // it has one ref too many at this point - - pimpl->qtControl->QuickTimeInitialize(); - pimpl->qtControl->PutSizing (qtMovieFitsControl); - - if (movieFile != File()) - loadMovie (movieFile, controllerVisible); - } - } - } -} - -bool QuickTimeMovieComponent::isControlCreated() const -{ - return isControlOpen(); -} - -bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, - const bool isControllerVisible) -{ - const ScopedPointer movieStreamDeleter (movieStream); - - movieFile = File(); - movieLoaded = false; - pimpl->qtMovie = 0; - controllerVisible = isControllerVisible; - createControlIfNeeded(); - - if (isControlCreated()) - { - if (pimpl->qtControl != 0) - { - pimpl->qtControl->Put_MovieHandle (0); - pimpl->clearHandle(); - - Movie movie; - if (juce_OpenQuickTimeMovieFromStream (movieStream, movie, pimpl->dataHandle)) - { - pimpl->qtControl->Put_MovieHandle ((long) (pointer_sized_int) movie); - - pimpl->qtMovie = pimpl->qtControl->GetMovie(); - - if (pimpl->qtMovie != 0) - pimpl->qtMovie->PutMovieControllerType (isControllerVisible ? qtMovieControllerTypeStandard - : qtMovieControllerTypeNone); - } - - if (movie == 0) - pimpl->clearHandle(); - } - - movieLoaded = (pimpl->qtMovie != 0); - } - else - { - // You're trying to open a movie when the control hasn't yet been created, probably because - // you've not yet added this component to a Window and made the whole component hierarchy visible. - jassertfalse; - } - - return movieLoaded; -} - -void QuickTimeMovieComponent::closeMovie() -{ - stop(); - movieFile = File(); - movieLoaded = false; - pimpl->qtMovie = 0; - - if (pimpl->qtControl != 0) - pimpl->qtControl->Put_MovieHandle (0); - - pimpl->clearHandle(); -} - -File QuickTimeMovieComponent::getCurrentMovieFile() const -{ - return movieFile; -} - -bool QuickTimeMovieComponent::isMovieOpen() const -{ - return movieLoaded; -} - -double QuickTimeMovieComponent::getMovieDuration() const -{ - if (pimpl->qtMovie != 0) - return pimpl->qtMovie->GetDuration() / (double) pimpl->qtMovie->GetTimeScale(); - - return 0.0; -} - -void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const -{ - if (pimpl->qtMovie != 0) - { - struct QTRECT r = pimpl->qtMovie->GetNaturalRect(); - - width = r.right - r.left; - height = r.bottom - r.top; - } - else - { - width = height = 0; - } -} - -void QuickTimeMovieComponent::play() -{ - if (pimpl->qtMovie != 0) - pimpl->qtMovie->Play(); -} - -void QuickTimeMovieComponent::stop() -{ - if (pimpl->qtMovie != 0) - pimpl->qtMovie->Stop(); -} - -bool QuickTimeMovieComponent::isPlaying() const -{ - return pimpl->qtMovie != 0 && pimpl->qtMovie->GetRate() != 0.0f; -} - -void QuickTimeMovieComponent::setPosition (const double seconds) -{ - if (pimpl->qtMovie != 0) - pimpl->qtMovie->PutTime ((long) (seconds * pimpl->qtMovie->GetTimeScale())); -} - -double QuickTimeMovieComponent::getPosition() const -{ - if (pimpl->qtMovie != 0) - return pimpl->qtMovie->GetTime() / (double) pimpl->qtMovie->GetTimeScale(); - - return 0.0; -} - -void QuickTimeMovieComponent::setSpeed (const float newSpeed) -{ - if (pimpl->qtMovie != 0) - pimpl->qtMovie->PutRate (newSpeed); -} - -void QuickTimeMovieComponent::setMovieVolume (const float newVolume) -{ - if (pimpl->qtMovie != 0) - { - pimpl->qtMovie->PutAudioVolume (newVolume); - pimpl->qtMovie->PutAudioMute (newVolume <= 0); - } -} - -float QuickTimeMovieComponent::getMovieVolume() const -{ - if (pimpl->qtMovie != 0) - return pimpl->qtMovie->GetAudioVolume(); - - return 0.0f; -} - -void QuickTimeMovieComponent::setLooping (const bool shouldLoop) -{ - if (pimpl->qtMovie != 0) - pimpl->qtMovie->PutLoop (shouldLoop); -} - -bool QuickTimeMovieComponent::isLooping() const -{ - return pimpl->qtMovie != 0 && pimpl->qtMovie->GetLoop(); -} - -bool QuickTimeMovieComponent::isControllerVisible() const -{ - return controllerVisible; -} - -void QuickTimeMovieComponent::parentHierarchyChanged() -{ - createControlIfNeeded(); - QTCompBaseClass::parentHierarchyChanged(); -} - -void QuickTimeMovieComponent::visibilityChanged() -{ - createControlIfNeeded(); - QTCompBaseClass::visibilityChanged(); -} - -void QuickTimeMovieComponent::paint (Graphics& g) -{ - if (! isControlCreated()) - g.fillAll (Colours::black); -} - - -//============================================================================== -static Handle createHandleDataRef (Handle dataHandle, const char* fileName) -{ - Handle dataRef = 0; - OSStatus err = PtrToHand (&dataHandle, &dataRef, sizeof (Handle)); - if (err == noErr) - { - Str255 suffix; - - #if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4244 4996) - #endif - - suffix[0] = strlen (fileName); - strncpy ((char*) suffix + 1, fileName, 128); - - #if JUCE_MSVC - #pragma warning (pop) - #endif - - err = PtrAndHand (suffix, dataRef, suffix[0] + 1); - - if (err == noErr) - { - long atoms[3]; - atoms[0] = EndianU32_NtoB (3 * sizeof (long)); - atoms[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType); - atoms[2] = EndianU32_NtoB (MovieFileType); - - err = PtrAndHand (atoms, dataRef, 3 * sizeof (long)); - - if (err == noErr) - return dataRef; - } - - DisposeHandle (dataRef); - } - - return 0; -} - -static CFStringRef juceStringToCFString (const String& s) -{ - return CFStringCreateWithCString (kCFAllocatorDefault, s.toUTF8(), kCFStringEncodingUTF8); -} - -static bool openMovie (QTNewMoviePropertyElement* props, int prop, Movie& movie) -{ - Boolean trueBool = true; - props[prop].propClass = kQTPropertyClass_MovieInstantiation; - props[prop].propID = kQTMovieInstantiationPropertyID_DontResolveDataRefs; - props[prop].propValueSize = sizeof (trueBool); - props[prop].propValueAddress = &trueBool; - ++prop; - - props[prop].propClass = kQTPropertyClass_MovieInstantiation; - props[prop].propID = kQTMovieInstantiationPropertyID_AsyncOK; - props[prop].propValueSize = sizeof (trueBool); - props[prop].propValueAddress = &trueBool; - ++prop; - - Boolean isActive = true; - props[prop].propClass = kQTPropertyClass_NewMovieProperty; - props[prop].propID = kQTNewMoviePropertyID_Active; - props[prop].propValueSize = sizeof (isActive); - props[prop].propValueAddress = &isActive; - ++prop; - - MacSetPort (0); - - jassert (prop <= 5); - OSStatus err = NewMovieFromProperties (prop, props, 0, 0, &movie); - - return err == noErr; -} - -bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle) -{ - if (input == nullptr) - return false; - - dataHandle = 0; - bool ok = false; - - QTNewMoviePropertyElement props[5] = { 0 }; - int prop = 0; - - DataReferenceRecord dr; - props[prop].propClass = kQTPropertyClass_DataLocation; - props[prop].propID = kQTDataLocationPropertyID_DataReference; - props[prop].propValueSize = sizeof (dr); - props[prop].propValueAddress = &dr; - ++prop; - - FileInputStream* const fin = dynamic_cast (input); - - if (fin != nullptr) - { - CFStringRef filePath = juceStringToCFString (fin->getFile().getFullPathName()); - - QTNewDataReferenceFromFullPathCFString (filePath, (QTPathStyle) kQTNativeDefaultPathStyle, 0, - &dr.dataRef, &dr.dataRefType); - - - ok = openMovie (props, prop, movie); - - DisposeHandle (dr.dataRef); - CFRelease (filePath); - } - else - { - // sanity-check because this currently needs to load the whole stream into memory.. - jassert (input->getTotalLength() < 50 * 1024 * 1024); - - dataHandle = NewHandle ((Size) input->getTotalLength()); - HLock (dataHandle); - // read the entire stream into memory - this is a pain, but can't get it to work - // properly using a custom callback to supply the data. - input->read (*dataHandle, (int) input->getTotalLength()); - HUnlock (dataHandle); - - // different types to get QT to try. (We should really be a bit smarter here by - // working out in advance which one the stream contains, rather than just trying - // each one) - static const char* const suffixesToTry[] = { "\04.mov", "\04.mp3", - "\04.avi", "\04.m4a" }; - - for (int i = 0; i < numElementsInArray (suffixesToTry) && ! ok; ++i) - { - /* // this fails for some bizarre reason - it can be bodged to work with - // movies, but can't seem to do it for other file types.. - QTNewMovieUserProcRecord procInfo; - procInfo.getMovieUserProc = NewGetMovieUPP (readMovieStreamProc); - procInfo.getMovieUserProcRefcon = this; - procInfo.defaultDataRef.dataRef = dataRef; - procInfo.defaultDataRef.dataRefType = HandleDataHandlerSubType; - - props[prop].propClass = kQTPropertyClass_DataLocation; - props[prop].propID = kQTDataLocationPropertyID_MovieUserProc; - props[prop].propValueSize = sizeof (procInfo); - props[prop].propValueAddress = (void*) &procInfo; - ++prop; */ - - dr.dataRef = createHandleDataRef (dataHandle, suffixesToTry [i]); - dr.dataRefType = HandleDataHandlerSubType; - ok = openMovie (props, prop, movie); - - DisposeHandle (dr.dataRef); - } - } - - return ok; -} - -bool QuickTimeMovieComponent::loadMovie (const File& movieFile_, - const bool isControllerVisible) -{ - const bool ok = loadMovie (static_cast (movieFile_.createInputStream()), isControllerVisible); - movieFile = movieFile_; - return ok; -} - -bool QuickTimeMovieComponent::loadMovie (const URL& movieURL, - const bool isControllerVisible) -{ - return loadMovie (static_cast (movieURL.createInputStream (false)), isControllerVisible); -} - -void QuickTimeMovieComponent::goToStart() -{ - setPosition (0.0); -} - -void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, - RectanglePlacement placement) -{ - int normalWidth, normalHeight; - getMovieNormalSize (normalWidth, normalHeight); - - const Rectangle normalSize (0, 0, normalWidth, normalHeight); - - if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty())) - setBounds (placement.appliedTo (normalSize, spaceToFitWithin)); - else - setBounds (spaceToFitWithin); -} diff --git a/modules/juce_video/playback/juce_DirectShowComponent.h b/modules/juce_video/playback/juce_DirectShowComponent.h index c1d191beac..a8e8e5b875 100644 --- a/modules/juce_video/playback/juce_DirectShowComponent.h +++ b/modules/juce_video/playback/juce_DirectShowComponent.h @@ -25,7 +25,6 @@ #ifndef JUCE_DIRECTSHOWCOMPONENT_H_INCLUDED #define JUCE_DIRECTSHOWCOMPONENT_H_INCLUDED -#if JUCE_DIRECTSHOW || DOXYGEN //============================================================================== /** @@ -212,5 +211,4 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowComponent) }; -#endif #endif // JUCE_DIRECTSHOWCOMPONENT_H_INCLUDED diff --git a/modules/juce_video/playback/juce_MovieComponent.h b/modules/juce_video/playback/juce_MovieComponent.h new file mode 100644 index 0000000000..ddd0234c33 --- /dev/null +++ b/modules/juce_video/playback/juce_MovieComponent.h @@ -0,0 +1,65 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2015 - ROLI Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#pragma once + +//============================================================================== +/** + A component that can play back a video file. +*/ +class JUCE_API MovieComponent : public NSViewComponent +{ +public: + MovieComponent(); + ~MovieComponent(); + + bool loadMovie (const File& file); + bool loadMovie (const URL& file); + + void closeMovie(); + bool isMovieOpen() const; + String getCurrentMoviePath() const; + + void play(); + void stop(); + + double getDuration() const; + double getPosition() const; + void setPosition (double seconds); + void setVolume (float newVolume); + float getVolume() const; + + Rectangle getNativeSize() const; + void setBoundsWithCorrectAspectRatio (Rectangle spaceToFitWithin, + RectanglePlacement placement); + +protected: + void resized() override; + +private: + struct Pimpl; + ScopedPointer pimpl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponent) +}; diff --git a/modules/juce_video/playback/juce_QuickTimeMovieComponent.h b/modules/juce_video/playback/juce_QuickTimeMovieComponent.h deleted file mode 100644 index ae158b2f84..0000000000 --- a/modules/juce_video/playback/juce_QuickTimeMovieComponent.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2015 - ROLI Ltd. - - Permission is granted to use this software under the terms of either: - a) the GPL v2 (or any later version) - b) the Affero GPL v3 - - Details of these licenses can be found at: www.gnu.org/licenses - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.juce.com for more information. - - ============================================================================== -*/ - -#ifndef JUCE_QUICKTIMEMOVIECOMPONENT_H_INCLUDED -#define JUCE_QUICKTIMEMOVIECOMPONENT_H_INCLUDED - -#ifndef DOXYGEN - #if JUCE_WINDOWS - typedef ActiveXControlComponent QTCompBaseClass; - #elif JUCE_MAC - typedef NSViewComponent QTCompBaseClass; - #endif -#endif - -#if JUCE_QUICKTIME || DOXYGEN - -//============================================================================== -/** - A window that can play back a QuickTime movie. - -*/ -class JUCE_API QuickTimeMovieComponent : public QTCompBaseClass -{ -public: - //============================================================================== - /** Creates a QuickTimeMovieComponent, initially blank. - - Use the loadMovie() method to load a movie once you've added the - component to a window, (or put it on the desktop as a heavyweight window). - Loading a movie when the component isn't visible can cause problems, as - QuickTime needs a window handle to initialise properly. - */ - QuickTimeMovieComponent(); - - /** Destructor. */ - ~QuickTimeMovieComponent(); - - /** Returns true if QT is installed and working on this machine. - */ - static bool isQuickTimeAvailable() noexcept; - - //============================================================================== - /** Tries to load a QuickTime movie from a file into the player. - - It's best to call this function once you've added the component to a window, - (or put it on the desktop as a heavyweight window). Loading a movie when the - component isn't visible can cause problems, because QuickTime needs a window - handle to do its stuff. - - @param movieFile the .mov file to open - @param isControllerVisible whether to show a controller bar at the bottom - @returns true if the movie opens successfully - */ - bool loadMovie (const File& movieFile, - bool isControllerVisible); - - /** Tries to load a QuickTime movie from a URL into the player. - - It's best to call this function once you've added the component to a window, - (or put it on the desktop as a heavyweight window). Loading a movie when the - component isn't visible can cause problems, because QuickTime needs a window - handle to do its stuff. - - @param movieURL the .mov file to open - @param isControllerVisible whether to show a controller bar at the bottom - @returns true if the movie opens successfully - */ - bool loadMovie (const URL& movieURL, - bool isControllerVisible); - - /** Tries to load a QuickTime movie from a stream into the player. - - It's best to call this function once you've added the component to a window, - (or put it on the desktop as a heavyweight window). Loading a movie when the - component isn't visible can cause problems, because QuickTime needs a window - handle to do its stuff. - - @param movieStream a stream containing a .mov file. The component may try - to read the whole stream before playing, rather than - streaming from it. - @param isControllerVisible whether to show a controller bar at the bottom - @returns true if the movie opens successfully - */ - bool loadMovie (InputStream* movieStream, - bool isControllerVisible); - - /** Closes the movie, if one is open. */ - void closeMovie(); - - /** Returns the movie file that is currently open. - If there isn't one, this returns File() - */ - File getCurrentMovieFile() const; - - /** Returns true if there's currently a movie open. */ - bool isMovieOpen() const; - - /** Returns the length of the movie, in seconds. */ - double getMovieDuration() const; - - /** Returns the movie's natural size, in pixels. - - You can use this to resize the component to show the movie at its preferred - scale. - - If no movie is loaded, the size returned will be 0 x 0. - */ - void getMovieNormalSize (int& width, int& height) const; - - /** This will position the component within a given area, keeping its aspect - ratio correct according to the movie's normal size. - - The component will be made as large as it can go within the space, and will - be aligned according to the justification value if this means there are gaps at - the top or sides. - */ - void setBoundsWithCorrectAspectRatio (const Rectangle& spaceToFitWithin, - RectanglePlacement placement); - - /** Starts the movie playing. */ - void play(); - - /** Stops the movie playing. */ - void stop(); - - /** Returns true if the movie is currently playing. */ - bool isPlaying() const; - - /** Moves the movie's position back to the start. */ - void goToStart(); - - /** Sets the movie's position to a given time. */ - void setPosition (double seconds); - - /** Returns the current play position of the movie. */ - double getPosition() const; - - /** Changes the movie playback rate. - - A value of 1 is normal speed, greater values play it proportionately faster, - smaller values play it slower. - */ - void setSpeed (float newSpeed); - - /** Changes the movie's playback volume. - @param newVolume the volume in the range 0 (silent) to 1.0 (full) - */ - void setMovieVolume (float newVolume); - - /** Returns the movie's playback volume. - @returns the volume in the range 0 (silent) to 1.0 (full) - */ - float getMovieVolume() const; - - /** Tells the movie whether it should loop. */ - void setLooping (bool shouldLoop); - - /** Returns true if the movie is currently looping. - @see setLooping - */ - bool isLooping() const; - - /** True if the native QuickTime controller bar is shown in the window. - @see loadMovie - */ - bool isControllerVisible() const; - - - //============================================================================== - /** @internal */ - void paint (Graphics&) override; - - -private: - //============================================================================== - File movieFile; - bool movieLoaded, controllerVisible, looping; - - #if JUCE_WINDOWS - void parentHierarchyChanged() override; - void visibilityChanged() override; - void createControlIfNeeded(); - bool isControlCreated() const; - - class Pimpl; - friend struct ContainerDeletePolicy; - ScopedPointer pimpl; - #else - void* movie; - #endif - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuickTimeMovieComponent) -}; - -#endif -#endif // JUCE_QUICKTIMEMOVIECOMPONENT_H_INCLUDED