Browse Source

Replaced QuickTimeMovieComponent with MovieComponent, using AVFoundation on OSX

tags/2021-05-28
hogliux 8 years ago
parent
commit
1c0b935bf6
8 changed files with 298 additions and 1056 deletions
  1. +2
    -10
      modules/juce_video/juce_video.cpp
  2. +9
    -3
      modules/juce_video/juce_video.h
  3. +222
    -0
      modules/juce_video/native/juce_mac_MovieComponent.mm
  4. +0
    -341
      modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm
  5. +0
    -483
      modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp
  6. +0
    -2
      modules/juce_video/playback/juce_DirectShowComponent.h
  7. +65
    -0
      modules/juce_video/playback/juce_MovieComponent.h
  8. +0
    -217
      modules/juce_video/playback/juce_QuickTimeMovieComponent.h

+ 2
- 10
modules/juce_video/juce_video.cpp View File

@@ -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


+ 9
- 3
modules/juce_video/juce_video.h View File

@@ -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"
}


+ 222
- 0
modules/juce_video/native/juce_mac_MovieComponent.mm View File

@@ -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];
}
}

+ 0
- 341
modules/juce_video/native/juce_mac_QuickTimeMovieComponent.mm View File

@@ -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

+ 0
- 483
modules/juce_video/native/juce_win32_QuickTimeMovieComponent.cpp View File

@@ -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);
}

+ 0
- 2
modules/juce_video/playback/juce_DirectShowComponent.h View File

@@ -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

+ 65
- 0
modules/juce_video/playback/juce_MovieComponent.h View File

@@ -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)
};

+ 0
- 217
modules/juce_video/playback/juce_QuickTimeMovieComponent.h View File

@@ -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

Loading…
Cancel
Save