| @@ -38,9 +38,7 @@ | |||
| #include "juce_video.h" | |||
| #if JUCE_MAC | |||
| #if JUCE_QUICKTIME | |||
| #import <QTKit/QTKit.h> | |||
| #endif | |||
| #import <AVFoundation/AVFoundation.h> | |||
| //============================================================================== | |||
| #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 | |||
| @@ -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" | |||
| } | |||
| @@ -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<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: frame]; | |||
| [pimpl->playerLayer setFrame: frame]; | |||
| } | |||
| } | |||
| @@ -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 <QTMovieView> | |||
| { | |||
| NonInterceptingQTMovieViewClass() : ObjCClass <QTMovieView> ("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<QTMovie*> (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<FileInputStream*> (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<InputStream> 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<int>& spaceToFitWithin, | |||
| RectanglePlacement placement) | |||
| { | |||
| int normalWidth, normalHeight; | |||
| getMovieNormalSize (normalWidth, normalHeight); | |||
| const Rectangle<int> 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 | |||
| @@ -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<InputStream> 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<FileInputStream*> (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<InputStream*> (movieFile_.createInputStream()), isControllerVisible); | |||
| movieFile = movieFile_; | |||
| return ok; | |||
| } | |||
| bool QuickTimeMovieComponent::loadMovie (const URL& movieURL, | |||
| const bool isControllerVisible) | |||
| { | |||
| return loadMovie (static_cast<InputStream*> (movieURL.createInputStream (false)), isControllerVisible); | |||
| } | |||
| void QuickTimeMovieComponent::goToStart() | |||
| { | |||
| setPosition (0.0); | |||
| } | |||
| void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin, | |||
| RectanglePlacement placement) | |||
| { | |||
| int normalWidth, normalHeight; | |||
| getMovieNormalSize (normalWidth, normalHeight); | |||
| const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight); | |||
| if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty())) | |||
| setBounds (placement.appliedTo (normalSize, spaceToFitWithin)); | |||
| else | |||
| setBounds (spaceToFitWithin); | |||
| } | |||
| @@ -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 | |||
| @@ -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<int> getNativeSize() const; | |||
| void setBoundsWithCorrectAspectRatio (Rectangle<int> spaceToFitWithin, | |||
| RectanglePlacement placement); | |||
| protected: | |||
| void resized() override; | |||
| private: | |||
| struct Pimpl; | |||
| ScopedPointer<Pimpl> pimpl; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MovieComponent) | |||
| }; | |||
| @@ -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<int>& 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<Pimpl>; | |||
| ScopedPointer<Pimpl> pimpl; | |||
| #else | |||
| void* movie; | |||
| #endif | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuickTimeMovieComponent) | |||
| }; | |||
| #endif | |||
| #endif // JUCE_QUICKTIMEMOVIECOMPONENT_H_INCLUDED | |||