--- old/modules/media/src/main/native/jfxmedia/platform/osx/OSXMediaPlayer.mm 2017-12-07 16:44:38.000000000 -0800 +++ new/modules/media/src/main/native/jfxmedia/platform/osx/OSXMediaPlayer.mm 2017-12-07 16:44:38.000000000 -0800 @@ -66,7 +66,6 @@ + (BOOL) initPlayerPlatform { BOOL enableAVF = YES; - BOOL enableQTK = YES; // Check environment to see if platforms are enabled char *value = getenv("JFXMEDIA_AVF"); @@ -74,11 +73,6 @@ enableAVF = NO; } - value = getenv("JFXMEDIA_QTKIT"); - if (value ? strncasecmp(value, "yes", 3) != 0 : NO) { - enableQTK = NO; - } - // Determine if we can use OSX native player libs, without linking directly Class klass; @@ -94,19 +88,6 @@ } } - if (enableQTK) { - klass = objc_getClass("QTKMediaPlayer"); - if (klass) { - // And make sure it conforms to the OSXPlayerProtocol - if ([klass conformsToProtocol:@protocol(OSXPlayerProtocol)]) { - if ([klass respondsToSelector:@selector(playerAvailable)] ? [klass playerAvailable] : YES) { - gMediaPlayerClass = klass; - return YES; - } - } - } - } - return NO; } --- old/modules/media/src/main/native/jfxmedia/platform/osx/avf/AVFAudioProcessor.mm 2017-12-07 16:44:40.000000000 -0800 +++ new/modules/media/src/main/native/jfxmedia/platform/osx/avf/AVFAudioProcessor.mm 2017-12-07 16:44:40.000000000 -0800 @@ -27,74 +27,25 @@ #import "AVFMediaPlayer.h" #import +#import #import "AVFKernelProcessor.h" #import #import -#import #import -/* - * MTAudioProcessingTap is a feature new to 10.9 but also exists in - * MediaToolbox.framework in 10.8. Unfortunately the SDK we build with does not - * have the header file needed to compile our audio tap, so we will have to - * supply the missing pieces here. We will use dlsym to find the - * MTAudioProcessingTap calls we need, this will prevent crashing on systems that - * don't implement it. - */ -extern "C" { -#pragma pack(push, 4) - - // This is MTAudioProcessingTapCallbacks in MediaToolbox.framework -struct __MTAudioTapCallbacks { - int version; - void *clientInfo; - void (*init)(CFTypeRef tapRef, void *clientInfo, void **tapStorageOut); - void (*finalize)(CFTypeRef tapRef); - void (*prepare)(CFTypeRef tapRef, - CMItemCount maxFrames, - const AudioStreamBasicDescription *processingFormat); - void (*unprepare)(CFTypeRef tapRef); - void (*process)(CFTypeRef tapRef, - CMItemCount numberFramesIn, uint32_t flagsIn, - AudioBufferList *bufferListInOut, - CMItemCount *numberFramesOut, uint32_t *flagsOut); -}; - -#pragma pack(pop) -}; - -typedef OSStatus (*AudioTapCreateProc)(CFAllocatorRef allocator, - const __MTAudioTapCallbacks *callbacks, - uint32_t flags, - CFTypeRef *tapOut); -AudioTapCreateProc gAudioTapCreate = NULL; - -typedef void *(*AudioTapGetStorageProc)(CFTypeRef tap); -AudioTapGetStorageProc gAudioTapGetStorage = NULL; - -typedef OSStatus (*AudioTapGetSourceAudioProc)(CFTypeRef tap, - CMItemCount numberFrames, - AudioBufferList *bufferListInOut, - uint32_t *flagsOut, - CMTimeRange *timeRangeOut, - CMItemCount *numberFramesOut); -AudioTapGetSourceAudioProc gAudioTapGetSourceAudio = NULL; - -pthread_mutex_t gAVFTapProcsLock = PTHREAD_MUTEX_INITIALIZER; - -static void InitAudioTap(CFTypeRef tapRef, void *clientInfo, void **tapStorageOut); -static void FinalizeAudioTap(CFTypeRef tapRef); -static void PrepareAudioTap(CFTypeRef tapRef, +static void InitAudioTap(MTAudioProcessingTapRef tapRef, void *clientInfo, void **tapStorageOut); +static void FinalizeAudioTap(MTAudioProcessingTapRef tapRef); +static void PrepareAudioTap(MTAudioProcessingTapRef tapRef, CMItemCount maxFrames, const AudioStreamBasicDescription *processingFormat); -static void UnprepareAudioTap(CFTypeRef tapRef); -static void ProcessAudioTap(CFTypeRef tapRef, CMItemCount numberFrames, - uint32_t /*MTAudioProcessingTapFlags*/ flags, +static void UnprepareAudioTap(MTAudioProcessingTapRef tapRef); +static void ProcessAudioTap(MTAudioProcessingTapRef tapRef, CMItemCount numberFrames, + MTAudioProcessingTapFlags flags, AudioBufferList *bufferListInOut, CMItemCount *numberFramesOut, - uint32_t /*MTAudioProcessingTapFlags*/ *flagsOut); + MTAudioProcessingTapFlags *flagsOut); static OSStatus AVFTapRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, @@ -130,32 +81,6 @@ AVFAudioEqualizerPtr audioEQ; }; -static bool FindAudioTap() { - static bool checkPerformed = false; - - pthread_mutex_lock(&gAVFTapProcsLock); - if (!checkPerformed) { - if (!gAudioTapCreate) { - gAudioTapCreate = (AudioTapCreateProc) - dlsym(RTLD_DEFAULT, "MTAudioProcessingTapCreate"); - } - if (!gAudioTapGetStorage) { - gAudioTapGetStorage = (AudioTapGetStorageProc) - dlsym(RTLD_DEFAULT, "MTAudioProcessingTapGetStorage"); - } - if (!gAudioTapGetSourceAudio) { - gAudioTapGetSourceAudio = (AudioTapGetSourceAudioProc) - dlsym(RTLD_DEFAULT, "MTAudioProcessingTapGetSourceAudio"); - } - checkPerformed = true; - } - pthread_mutex_unlock(&gAVFTapProcsLock); - - return (gAudioTapCreate != NULL) - && (gAudioTapGetStorage != NULL) - && (gAudioTapGetSourceAudio != NULL); -} - @implementation AVFAudioProcessor - (id) init { @@ -190,10 +115,6 @@ if (!self.audioTrack) { return nil; } - if (!FindAudioTap()) { - NSLog(@"Audio tap is not available, cannot post-process audio"); - return nil; - } if (!_mixer) { AVMutableAudioMix *mixer = [AVMutableAudioMix audioMix]; if (mixer) { @@ -201,19 +122,19 @@ [AVMutableAudioMixInputParameters audioMixInputParametersWithTrack:self.audioTrack]; if (audioMixInputParameters && [audioMixInputParameters respondsToSelector:@selector(setAudioTapProcessor:)]) { - __MTAudioTapCallbacks callbacks; + MTAudioProcessingTapCallbacks callbacks; - callbacks.version = 0; // kMTAudioProcessingTapCallbacksVersion_0 - callbacks.clientInfo = (__bridge void *)self, + callbacks.version = kMTAudioProcessingTapCallbacksVersion_0; + callbacks.clientInfo = (__bridge void *)self; callbacks.init = InitAudioTap; callbacks.finalize = FinalizeAudioTap; callbacks.prepare = PrepareAudioTap; callbacks.unprepare = UnprepareAudioTap; callbacks.process = ProcessAudioTap; - CFTypeRef audioProcessingTap; - if (noErr == gAudioTapCreate(kCFAllocatorDefault, &callbacks, - 1, // kMTAudioProcessingTapCreationFlag_PreEffects + MTAudioProcessingTapRef audioProcessingTap; + if (noErr == MTAudioProcessingTapCreate(kCFAllocatorDefault, &callbacks, + kMTAudioProcessingTapCreationFlag_PreEffects, &audioProcessingTap)) { objc_msgSend(audioMixInputParameters, @@ -247,7 +168,7 @@ @end -void InitAudioTap(CFTypeRef tapRef, void *clientInfo, void **tapStorageOut) +void InitAudioTap(MTAudioProcessingTapRef tapRef, void *clientInfo, void **tapStorageOut) { // retain the AU kernels so they don't get freed while we're running AVFAudioProcessor *processor = (__bridge AVFAudioProcessor *)clientInfo; @@ -259,15 +180,9 @@ } } -void FinalizeAudioTap(CFTypeRef tapRef) +void FinalizeAudioTap(MTAudioProcessingTapRef tapRef) { - // NULL check is for safety, this should never be called if we don't have all - // the audio tap functions - if (!gAudioTapGetStorage) { - // should not happen - return; - } - AVFTapContext *context = (AVFTapContext*)gAudioTapGetStorage(tapRef); + AVFTapContext *context = (AVFTapContext*)MTAudioProcessingTapGetStorage(tapRef); if (context) { delete context; @@ -329,15 +244,11 @@ return audioUnit; } -void PrepareAudioTap(CFTypeRef tapRef, - CMItemCount maxFrames, - const AudioStreamBasicDescription *processingFormat) +void PrepareAudioTap(MTAudioProcessingTapRef tapRef, + CMItemCount maxFrames, + const AudioStreamBasicDescription *processingFormat) { - if (!gAudioTapGetStorage) { - // should not happen - return; - } - AVFTapContext *context = (AVFTapContext*)gAudioTapGetStorage(tapRef); + AVFTapContext *context = (AVFTapContext*)MTAudioProcessingTapGetStorage(tapRef); // Validate the audio format before we enable the processor @@ -459,13 +370,9 @@ context->totalFrames = 0; } -void UnprepareAudioTap(CFTypeRef tapRef) +void UnprepareAudioTap(MTAudioProcessingTapRef tapRef) { - if (!gAudioTapGetStorage) { - // should not happen - return; - } - AVFTapContext *context = (AVFTapContext*)gAudioTapGetStorage(tapRef); + AVFTapContext *context = (AVFTapContext*)MTAudioProcessingTapGetStorage(tapRef); context->renderUnit = NULL; if (context->spectrumUnit) { @@ -485,18 +392,14 @@ } } -void ProcessAudioTap(CFTypeRef tapRef, +void ProcessAudioTap(MTAudioProcessingTapRef tapRef, CMItemCount numberFrames, uint32_t flags, AudioBufferList *bufferListInOut, CMItemCount *numberFramesOut, uint32_t *flagsOut) { - if (!gAudioTapGetStorage) { - // should not happen - return; - } - AVFTapContext *context = (AVFTapContext*)gAudioTapGetStorage(tapRef); + AVFTapContext *context = (AVFTapContext*)MTAudioProcessingTapGetStorage(tapRef); OSStatus status = noErr; if (context->renderUnit) { @@ -516,10 +419,8 @@ context->totalFrames += numberFrames; *numberFramesOut = numberFrames; } else { - if (gAudioTapGetSourceAudio) { - gAudioTapGetSourceAudio(tapRef, numberFrames, bufferListInOut, - flagsOut, NULL, numberFramesOut); - } + MTAudioProcessingTapGetSourceAudio(tapRef, numberFrames, bufferListInOut, + flagsOut, NULL, numberFramesOut); } } @@ -530,10 +431,6 @@ UInt32 inNumberFrames, AudioBufferList *ioData) { - if (!gAudioTapGetSourceAudio) { - // should not happen - return noErr; - } - CFTypeRef tapRef = static_cast(inRefCon); - return gAudioTapGetSourceAudio(tapRef, inNumberFrames, ioData, NULL, NULL, NULL); + MTAudioProcessingTapRef tapRef = static_cast(inRefCon); + return MTAudioProcessingTapGetSourceAudio(tapRef, inNumberFrames, ioData, NULL, NULL, NULL); } --- old/modules/media/src/main/native/jfxmedia/projects/mac/Makefile 2017-12-07 16:44:41.000000000 -0800 +++ new/modules/media/src/main/native/jfxmedia/projects/mac/Makefile 2017-12-07 16:44:41.000000000 -0800 @@ -17,10 +17,6 @@ TARGET_NAME = lib$(BASE_NAME).dylib TARGET = $(BUILD_DIR)/$(TARGET_NAME) -# separate library for QTKit based platform -QTK_NAME = lib$(BASE_NAME)_qtkit.dylib -QTK_LIB = $(BUILD_DIR)/$(QTK_NAME) - AVF_NAME = lib$(BASE_NAME)_avf.dylib AVF_LIB = $(BUILD_DIR)/$(AVF_NAME) @@ -144,7 +140,7 @@ .PHONY: default, checklibs -default: $(TARGET) $(QTK_LIB) $(AVF_LIB) +default: $(TARGET) $(AVF_LIB) @echo "Ensuring $(TARGET_NAME) does not link against QuickTime..." ! nm -m "$(TARGET)" | grep -E "(QTKit|QuickTime)" @echo "Ensuring $(AVF_NAME) does not link against QuickTime..." @@ -251,26 +247,3 @@ $(AVF_LIB): $(TARGET) $(AVF_OBJECTS) @mkdir -p $(dir $@) $(LINK) $(AVF_LDFLAGS) -dynamiclib $(AVF_OBJECTS) -l$(BASE_NAME) -o $@ - - -# ------------------------------------------------------------------ -# QTKit platform lib rules - -QTK_OBJBASE = $(OBJBASE_DIR)/qtk -QTK_CCFLAGS = -std=c++11 -stdlib=libc++ $(CFLAGS) -QTK_LDFLAGS = $(LDFLAGS) \ - -Wl,-install_name,@rpath/$(QTK_NAME) \ - -framework QTKit - -QTK_SOURCES = platform/osx/QTKMediaPlayer.mm -QTK_OBJECTS = $(patsubst %.mm,$(QTK_OBJBASE)/%.o,$(QTK_SOURCES)) - --include $(QTK_OBJECTS:.o=.d) - -$(QTK_OBJBASE)/%.o: %.mm - @mkdir -p $(dir $@) - $(CC) $(QTK_CCFLAGS) $(INCLUDES) -MD -MF $(QTK_OBJBASE)/$*.d -x objective-c++ -c $< -o $@ - -$(QTK_LIB): $(TARGET) $(QTK_OBJECTS) - @mkdir -p $(dir $@) - $(LINK) $(QTK_LDFLAGS) -dynamiclib $(QTK_OBJECTS) -l$(BASE_NAME) -o $@ --- old/modules/media/src/main/native/xcode_project/JFXMedia.xcodeproj/project.pbxproj 2017-12-07 16:44:43.000000000 -0800 +++ new/modules/media/src/main/native/xcode_project/JFXMedia.xcodeproj/project.pbxproj 2017-12-07 16:44:42.000000000 -0800 @@ -87,7 +87,6 @@ 65989AB01991570800319296 /* MTObjectProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 659896EA1991570800319296 /* MTObjectProxy.m */; }; 65989AB11991570800319296 /* posix_critical_section.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 659896EC1991570800319296 /* posix_critical_section.cpp */; }; 65989ABC1991574B00319296 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65989ABB1991574B00319296 /* CoreVideo.framework */; }; - 65989ACE199160EC00319296 /* QTKMediaPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 659896D41991570800319296 /* QTKMediaPlayer.mm */; }; 65989AED199160EC00319296 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65989ABB1991574B00319296 /* CoreVideo.framework */; }; 65989AEE199160EC00319296 /* QTKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65989AB91991574400319296 /* QTKit.framework */; }; 65989AEF199160EC00319296 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65989AB71991573600319296 /* AVFoundation.framework */; }; @@ -256,8 +255,6 @@ 659896D01991570800319296 /* OSXMediaPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSXMediaPlayer.mm; sourceTree = ""; }; 659896D11991570800319296 /* OSXPlatform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OSXPlatform.mm; sourceTree = ""; }; 659896D21991570800319296 /* OSXPlayerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSXPlayerProtocol.h; sourceTree = ""; }; - 659896D31991570800319296 /* QTKMediaPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QTKMediaPlayer.h; sourceTree = ""; }; - 659896D41991570800319296 /* QTKMediaPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QTKMediaPlayer.mm; sourceTree = ""; }; 659896DD1991570800319296 /* AutoLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutoLock.h; sourceTree = ""; }; 659896DE1991570800319296 /* ColorConverter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ColorConverter.c; sourceTree = ""; }; 659896DF1991570800319296 /* ColorConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorConverter.h; sourceTree = ""; }; @@ -607,8 +604,6 @@ 659896D01991570800319296 /* OSXMediaPlayer.mm */, 659896D11991570800319296 /* OSXPlatform.mm */, 659896D21991570800319296 /* OSXPlayerProtocol.h */, - 659896D31991570800319296 /* QTKMediaPlayer.h */, - 659896D41991570800319296 /* QTKMediaPlayer.mm */, ); path = osx; sourceTree = ""; @@ -816,7 +811,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 65989ACE199160EC00319296 /* QTKMediaPlayer.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; --- old/modules/media/src/main/native/jfxmedia/platform/osx/QTKMediaPlayer.h 2017-12-07 16:44:44.000000000 -0800 +++ /dev/null 2017-12-07 16:44:44.000000000 -0800 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import -#import - -#import "OSXPlayerProtocol.h" -#import - -@interface QTKMediaPlayer : NSObject -{ - NSURL *movieURL; - - QTMovie *movie; - BOOL movieReady; - id frameHandler; - - NSMutableSet *notificationCookies; // need these to deregister from the notification center - - CJavaPlayerEventDispatcher *eventHandler; - - int64_t audioSyncDelay; - - BOOL isLiveStream; // YES if the stream is indeterminate - - int requestedState; // 0 - stop, 1 - play, 2 - pause - float requestedRate; - - uint64_t hostTimeBase; // Host time for media time 0.0, updated every time we get a time changed notification - double hostTimeFreq; // frequency of the host clock, for conversion to seconds - BOOL updateHostTimeBase; - - double currentTime; - - BOOL suppressDurationEvents; - - BOOL mute; - float volume; - float balance; - - int previousWidth; - int previousHeight; - - int previousPlayerState; // avoid repeated states - - BOOL isDisposed; - - CAudioEqualizer *_audioEqualizer; - CAudioSpectrum *_audioSpectrum; -} - -- (id) initWithURL:(NSURL *)source eventHandler:(CJavaPlayerEventDispatcher*)hdlr; - -- (void) rateChanged:(float)newRate; -- (void) setPlayerState:(int)newState; -- (void) setMovieReady; -- (void) createMovie; - -@end --- old/modules/media/src/main/native/jfxmedia/platform/osx/QTKMediaPlayer.mm 2017-12-07 16:44:44.000000000 -0800 +++ /dev/null 2017-12-07 16:44:45.000000000 -0800 @@ -1,987 +0,0 @@ -/* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#import "QTKMediaPlayer.h" -#import -#import -#import "CVVideoFrame.h" -#import -#import - -#import - -#define DUMP_TRACK_INFO 0 - -// this is annoying... all because we had to have a STOPPED state... -#define kPlaybackState_Stop 0 -#define kPlaybackState_Play 1 -#define kPlaybackState_Pause 2 -#define kPlaybackState_Finished 3 - -// Non-public selectors for QTMovie -// WARNING: These aren't guaranteed to be there, you must check -// the movie object with respondsToSelector: first! -@interface QTMovie(HiddenStuff) - -- (void) setAudioDevice:(id)device error:(NSError**)err; - -- (float) balance; -- (void) setBalance:(float)b; - -- (BOOL) isBuffering; -- (BOOL) hasEqualizer; - -- (NSSet*) imageConsumers; -- (void) removeImageConsumer:(id)consumer flush:(BOOL)flush; -- (void) addImageConsumer:(id)consumer; - -- (NSArray *) availableRanges; -- (NSArray *) loadedRanges; - -@end - -@interface QTTrack(HiddenStuff) - -- (NSString*) channels; // ex: "Stereo (L R)" -- (int) audioChannelCount; -- (float) floatFrameRate; -- (float) audioSampleRate; -- (NSString*) codecName; // ex: "H.264" -- (NSString*) isoLanguageCodeAsString; - -@end - -@interface QTKMediaPlayer(PrivateStuff) - -- (void) sendVideoFrame:(CVPixelBufferRef)pixelBuffer hostTime:(uint64_t)hostTime; - -@end - - -@interface ImageConsumerProxy : NSObject -{ - QTKMediaPlayer *player; -} - -@end - -@implementation ImageConsumerProxy - -- (id) initWithPlayer:(QTKMediaPlayer*)inPlayer -{ - if ((self = [super init]) != nil) { - // don't retain the player or we'll cause a retain loop - player = inPlayer; - } - return self; -} - -- (void) dealloc -{ - [super dealloc]; -} - -- (NSDictionary *) preferredAttributes -{ - NSDictionary *pba = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], @"IOSurfaceCoreAnimationCompatibility", // doesn't seem necessary - [NSArray arrayWithObjects: - [NSNumber numberWithLong:k2vuyPixelFormat], - nil], @"PixelFormatType", - nil]; - - NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys: - [NSColorSpace genericRGBColorSpace], @"colorspace", - pba, @"pixelBufferAttributes", - nil]; - return attr; -} - -- (void) flushImageBuffersAfterHostTime:(unsigned long long)hostTime -{ - // FIXME: Can't do anything? All the frames are pushed up... -} - -- (void) setImageBuffer:(CVBufferRef)buf forHostTime:(unsigned long long)hostTime -{ - [player sendVideoFrame:buf hostTime:hostTime]; -} - -@end - - -@implementation QTKMediaPlayer - -- (id) initWithURL:(NSURL *)source eventHandler:(CJavaPlayerEventDispatcher*)hdlr -{ - if ((self = [self init]) != nil) { - movieURL = [source retain]; - - movie = nil; - movieReady = NO; - - frameHandler = [[ImageConsumerProxy alloc] initWithPlayer:self]; - - notificationCookies = [[NSMutableSet alloc] init]; - - isLiveStream = NO; // we'll determine later - - audioSyncDelay = 0; - requestedRate = 1.0; - updateHostTimeBase = NO; - currentTime = 0.0; - suppressDurationEvents = NO; - mute = NO; - volume = 1.0; - balance = 0.0; - - eventHandler = hdlr; - - previousWidth = -1; - previousHeight = -1; - - previousPlayerState = kPlayerState_UNKNOWN; - - requestedState = kPlaybackState_Stop; - - isDisposed = NO; - - _audioEqualizer = new CNullAudioEqualizer(); - _audioSpectrum = new CNullAudioSpectrum(); - - // create the movie on the main thread, but don't wait for it to happen - if (![NSThread isMainThread]) { - [self performSelectorOnMainThread:@selector(createMovie) withObject:nil waitUntilDone:NO]; - } else { - [self createMovie]; - } - } - return self; -} - -- (void) dealloc -{ - [self dispose]; // just in case - - [frameHandler release]; - frameHandler = nil; - - [movieURL release]; - - if (_audioEqualizer) { - delete _audioEqualizer; - } - - if (_audioSpectrum) { - delete _audioSpectrum; - } - - [super dealloc]; -} - -- (CAudioEqualizer*) audioEqualizer -{ - return _audioEqualizer; -} - -- (CAudioSpectrum*) audioSpectrum -{ - return _audioSpectrum; -} - -- (void) dispose -{ - @synchronized(self) { - [movie invalidate]; - if (frameHandler) { - // remove the image consumer - [movie removeImageConsumer:frameHandler flush:NO]; - } - [movie release]; - movie = nil; - - if (notificationCookies) { - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - for (id cookie in notificationCookies) { - [center removeObserver:cookie]; - } - [notificationCookies release]; - notificationCookies = nil; - } - - // eventHandler is cleaned up separately, just drop the reference - eventHandler = NULL; - - isDisposed = YES; - } -} - -- (void) registerForNotification:(NSString*)name object:(id)object withBlock:(void (^)(NSNotification*))block -{ - id cookie = [[NSNotificationCenter defaultCenter] - addObserverForName:name - object:object - queue:nil - usingBlock:block]; - - if (cookie) { - [notificationCookies addObject:cookie]; - } -} - -- (void) createMovie -{ - @synchronized(self) { - if (isDisposed) { - return; - } - - if (![NSThread isMainThread]) { - LOGGER_ERRORMSG_CM("QTKMediaPlayer", "createMovie", "was NOT called on the main app thread!\n"); - if (eventHandler) { - eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT); - } - return; - } - - NSError *err = nil; - QTMovie *qtMovie = - [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys: - movieURL, QTMovieURLAttribute, - [NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute, - [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, - // [NSNumber numberWithBool:NO], QTMovieAskUnresolvedDataRefsAttribute, - [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, - nil] - error:&err]; - if (err || !qtMovie) { - LOGGER_ERRORMSG_CM("QTKMediaPlayer", "createMovie", ([[NSString stringWithFormat:@"Error creating QTMovie: %@\n", err] UTF8String])); - if (eventHandler) { - eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT); - } - qtMovie = nil; - } - - /* - ******************************************************************************* - BIG FAT WARNING!!!!!!!!!!!!! - ******************************************************************************* - - Do NOT reference "self" inside a block registered with the - Notification Center or you will create a retain loop and prevent this - object from ever releasing. Instead, use the stack variable "blockSelf" - defined below, this will prevent the retain loop. - - ******************************************************************************* - */ - - __block __typeof__(self) blockSelf = self; - [self registerForNotification:QTMovieDidEndNotification - object:qtMovie - withBlock: - ^(NSNotification*note) { - [blockSelf finish]; - }]; - - [self registerForNotification:QTMovieLoadStateDidChangeNotification - object:qtMovie - withBlock: - ^(NSNotification *note) { - /* - * QTMovieLoadStateError - an error occurred while loading the movie - * QTMovieLoadStateLoading - the movie is loading - * QTMovieLoadStateLoaded - the movie atom has loaded; it's safe to query movie properties - * QTMovieLoadStatePlayable - the movie has loaded enough media data to begin playing - * QTMovieLoadStatePlaythroughOK - the movie has loaded enough media data to play through to the end - * QTMovieLoadStateComplete - the movie has loaded completely - */ - long loadState = [(NSNumber*)[movie attributeForKey:QTMovieLoadStateAttribute] longValue]; - NSError *loadError = (NSError*)[movie attributeForKey:QTMovieLoadStateErrorAttribute]; - if (loadError) { - LOGGER_ERRORMSG(([[NSString stringWithFormat:@"Error loading QTMovie: %@\n", loadError] UTF8String])); - if (eventHandler) { - if (loadError.domain == NSOSStatusErrorDomain) { - eventHandler->SendPlayerMediaErrorEvent(ERROR_LOCATOR_CONNECTION_LOST); - } else { - eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT); - } - } - } - - if (!movieReady) { - if (loadState > QTMovieLoadStateLoaded) { - [blockSelf setMovieReady]; - } - } else if (requestedState == kPlaybackState_Play) { - // if state is QTMovieLoadStatePlayable then we've stalled - // if state is QTMovieLoadStatePlaythroughOK then we're playing - if (loadState == QTMovieLoadStatePlayable && previousPlayerState == kPlayerState_PLAYING) { - [blockSelf setPlayerState:kPlayerState_STALLED]; - } else if (loadState == QTMovieLoadStatePlaythroughOK) { - [blockSelf setPlayerState:kPlayerState_PLAYING]; - } - } - }]; - - [self registerForNotification:QTMovieTimeDidChangeNotification - object:qtMovie - withBlock: - ^(NSNotification *note) { - // grab currentTime and current host time and set our host time base accordingly - double now = blockSelf.currentTime; - uint64_t hostTime = CVGetCurrentHostTime(); - hostTimeFreq = CVGetHostClockFrequency(); - uint64_t nowDelta = (uint64_t)(now * hostTimeFreq); // current time in host frequency units - hostTimeBase = hostTime - nowDelta; // Host time at movie time zero - LOGGER_DEBUGMSG(([[NSString stringWithFormat:@"Movie time changed %lf", currentTime] UTF8String])); - - // http://javafx-jira.kenai.com/browse/RT-27041 - // TODO: flush video buffers - }]; - - [self registerForNotification:QTMovieRateDidChangeNotification - object:qtMovie - withBlock: - ^(NSNotification *note) { - NSNumber *newRate = [note.userInfo objectForKey:QTMovieRateDidChangeNotificationParameter]; - [blockSelf rateChanged:newRate.floatValue]; - }]; - - // QTMovieNaturalSizeDidChangeNotification is unreliable, especially with HTTP live streaming - // so just use the pixel buffer sizes to send frame size changed events - - // QTMovieAvailableRangesDidChangeNotification - [self registerForNotification:@"QTMovieAvailableRangesDidChangeNotification" - object:qtMovie - withBlock: - ^(NSNotification *note) { - NSArray *ranges = nil; - if ([movie respondsToSelector:@selector(availableRanges)]) { - ranges = [movie performSelector:@selector(availableRanges)]; - } - if (!suppressDurationEvents && ranges) { - for (NSValue *rangeVal in ranges) { - QTTimeRange timeRange = [rangeVal QTTimeRangeValue]; // .time, .duration - // if duration is indefinite then it's a live stream and we need to report as such - if (QTTimeIsIndefinite(timeRange.duration)) { - eventHandler->SendDurationUpdateEvent(INFINITY); - // and suppress all other subsequent events - suppressDurationEvents = YES; - isLiveStream = YES; - break; - } - } - } - }]; - - // QTMovieLoadedRangesDidChangeNotification - [self registerForNotification:@"QTMovieLoadedRangesDidChangeNotification" - object:qtMovie - withBlock: - ^(NSNotification *note) { - NSArray *ranges = nil; - if ([movie respondsToSelector:@selector(loadedRanges)]) { - ranges = [movie performSelector:@selector(loadedRanges)]; - } - // don't emit progress events for live streams - if (!suppressDurationEvents && ranges) { - int64_t total = 0; - for (NSValue *rangeVal in ranges) { - QTTimeRange timeRange = [rangeVal QTTimeRangeValue]; // .time, .duration - NSTimeInterval duration; - QTGetTimeInterval(timeRange.duration, &duration); - - total += (int64_t)(duration * 1000); - } - // send buffer progress event - double movieDur = blockSelf.duration; - eventHandler->SendBufferProgressEvent(movieDur, 0, (int64_t)(movieDur * 1000), total); - } - }]; - -#if 0 - // show all notifications, use to find possibly missed notifications - [[NSNotificationCenter defaultCenter] - addObserverForName:nil - object:qtMovie - queue:nil - usingBlock:^(NSNotification *note) { - NSLog(@"Movie notification: %@", note.name); - } - ]; -#endif - -#if 0 - // Template notification block, remember to use blockSelf instead of self - [[NSNotificationCenter defaultCenter] - addObserverForName:QTMovieXXX - object:qtMovie - queue:nil - usingBlock: - ^(NSNotification *note) { - - } - ]; -#endif - // http://javafx-jira.kenai.com/browse/RT-27041 - // TODO: test for addImageConsumer first, fall back on CARenderer hack if it's not available - [qtMovie addImageConsumer:frameHandler]; - - movie = (QTMovie*)[[MTObjectProxy objectProxyWithTarget:qtMovie] retain]; - } -} - - -- (void) play -{ - requestedState = kPlaybackState_Play; - if (movie && movieReady) { - [movie play]; - [movie setRate:requestedRate]; - } -} - -- (void) pause -{ - if (requestedState == kPlaybackState_Stop) { - requestedState = kPlaybackState_Pause; - [self setPlayerState:kPlayerState_PAUSED]; - } else { - requestedState = kPlaybackState_Pause; - if (movie && movieReady) { - [movie stop]; - } - - if (previousPlayerState == kPlayerState_STALLED) { - [self setPlayerState:kPlayerState_PAUSED]; - } - } -} - -- (void) finish -{ - requestedState = kPlaybackState_Finished; - [self setPlayerState:kPlayerState_FINISHED]; - if (movie && movieReady) { - [movie stop]; - } -} - -- (void) stop -{ - if (requestedState == kPlaybackState_Finished || requestedState == kPlaybackState_Pause) { - requestedState = kPlaybackState_Stop; - [self setPlayerState:kPlayerState_STOPPED]; - } else { - requestedState = kPlaybackState_Stop; - if (movie && movieReady) { - // we need to just nuke the "STOPPED" state... - [movie stop]; - } else { - currentTime = 0.0; - } - - if (previousPlayerState == kPlayerState_STALLED) { - [self setPlayerState:kPlayerState_STOPPED]; - } - } -} - - -- (void) rateChanged:(float)newRate -{ - /* - * Relevant PlayerState values: - * PLAYING - rate != 0 - * PAUSED - reqRate == 0, rate == 0 - * STOPPED - stopFlag && reqRate == 0, rate == 0 - * STALLED - detected by load state or reqRate != 0, rate == 0 and state is PLAYING - */ - if (newRate == 0.0) { - // slop for FP/timescale error - if (requestedState == kPlaybackState_Stop) { - [self setPlayerState:kPlayerState_STOPPED]; - } else if (requestedState == kPlaybackState_Play && previousPlayerState == kPlayerState_PLAYING && requestedRate != 0.0) { - [self setPlayerState:kPlayerState_STALLED]; - } else if (requestedState != kPlaybackState_Finished) { - [self setPlayerState:kPlayerState_PAUSED]; - } - - } else { - // non-zero is always playing - [self setPlayerState:kPlayerState_PLAYING]; - } -} - -@synthesize audioSyncDelay; - -- (BOOL) mute -{ - if (movie && movieReady) { - mute = movie.muted; - return mute; - } - return mute; -} - -- (void) setMute:(BOOL)state -{ - mute = state; - if (movie && movieReady) { - movie.muted = state; - } -} - -- (float) volume -{ - if (movie && movieReady) { - volume = movie.volume; - } - return volume; -} - -- (void) setVolume:(float)newVolume -{ - volume = newVolume; - if (movie && movieReady) { - movie.volume = (float)volume; - } -} - -- (float) balance -{ - if (movie && movieReady) { - if ([movie respondsToSelector:@selector(balance)]) { - balance = [movie balance]; - } else if (eventHandler) { - eventHandler->Warning(WARNING_JFXMEDIA_BALANCE, NULL); - } - } - return balance; -} - -- (void) setBalance:(float)newBalance -{ - balance = newBalance; - if (movie && movieReady) { - if ([movie respondsToSelector:@selector(setBalance:)]) { - [movie setBalance:balance]; - } else if (eventHandler) { - eventHandler->Warning(WARNING_JFXMEDIA_BALANCE, NULL); - } - } -} - -- (double) duration -{ - if (movie && movieReady) { - NSNumber *hasDuration = [movie attributeForKey:QTMovieHasDurationAttribute]; - if (hasDuration.boolValue) { - QTTime movieDur = movie.duration; - NSTimeInterval duration; - if (QTGetTimeInterval(movieDur, &duration)) { - return duration; - } - } - } - return -1.0; // hack value for UNKNOWN, since duration must be >= 0 -} - -- (float) rate -{ - return requestedRate; -} - -- (void) setRate:(float)newRate -{ - if (isLiveStream) { - LOGGER_WARNMSG("Cannot set playback rate on LIVE stream!"); - return; - } - - requestedRate = newRate; - if (movie && movieReady && requestedState == kPlaybackState_Play) { - [movie setRate:requestedRate]; - } -} - -- (double) currentTime -{ - if (movie && movieReady) { - QTTime time = movie.currentTime; - NSTimeInterval timeIval; - if (QTGetTimeInterval(time, &timeIval)) { - currentTime = timeIval; - } - } - return currentTime; -} - -- (void) setCurrentTime:(double)newTime -{ - if (isLiveStream) { - LOGGER_WARNMSG("Cannot seek LIVE stream!"); - return; - } - - currentTime = newTime; - - if (movie && movieReady) { - movie.currentTime = QTMakeTimeWithTimeInterval(newTime); - - // make sure we're playing if requested - if (requestedState == kPlaybackState_Play) { - [movie play]; - [movie setRate:requestedRate]; - } else if (requestedState == kPlaybackState_Finished) { - requestedState = kPlaybackState_Play; - [movie play]; - [movie setRate:requestedRate]; - } - } -} - -- (void) setPlayerState:(int)newState -{ - if (newState != previousPlayerState) { - if (newState == kPlayerState_PLAYING) { - updateHostTimeBase = YES; - } - // For now just send up to client - eventHandler->SendPlayerStateEvent(newState, 0.0); - previousPlayerState = newState; - } -} - -#if DUMP_TRACK_INFO -static void append_log(NSMutableString *s, NSString *fmt, ...) { - va_list args; - va_start(args, fmt); - NSString *appString = [[NSString alloc] initWithFormat:fmt arguments:args]; - [s appendFormat:@"%@\n", appString]; - va_end(args); - [appString release]; -} -#define TRACK_LOG(fmt, ...) append_log(trackLog, fmt, ##__VA_ARGS__) -#else -#define TRACK_LOG(...) {} -#endif - -- (void) parseMovieTracks -{ -#if DUMP_TRACK_INFO - NSMutableString *trackLog = [[NSMutableString alloc] initWithFormat:@"Parsing tracks for movie %@:\n", movie]; -#endif - /* - * Track properties we care about at the FX level: - * - * track: - * + trackEnabled (boolean) - * + trackID (jlong) - * + name (string) - * + locale (Locale) - language is derived from Locale or null - * + language (3 char iso code) - * video track: - * + width (int) - * + height (int) - * audio track: (no additional properties) - * - * - * Track properties at the com.sun level: - * - * track: - * X trackEnabled (boolean) == QTTrackEnabledAttribute - * X trackID (long) == QTTrackIDAttribute - * X name (string) == QTTrackDisplayNameAttribute - * X encoding (enum) == non-public selector: - (NSString*) codecName - * video track: (QTTrackMediaTypeAttribute == 'vide') - * X frame size (w,h) == QTTrackDimensionsAttribute (NSValue:NSSize) - * X frame rate - * X hasAlpha (boolean) == false (for now) - * audio track: (QTTrackMediaTypeAttribute == 'soun') - * X language == non-public selector: - (NSString*) isoLanguageCodeAsString - * X channels (int) == non-public selector: - (int) audioChannelCount - * X channel mask (int) == parsed from channel count (or ASBD) - * X sample rate (float) == non-public selector: - (float) audioSampleRate - * - * just create CVideoTrack or CAudioTrack and send them up with eventHandler and we're good - */ - NSArray *tracks = movie.tracks; - if (tracks) { - // get video tracks - NSArray *tracks = [movie tracksOfMediaType:QTMediaTypeVideo]; - for (QTTrack *track in tracks) { - long trackID = [[track attributeForKey:QTTrackIDAttribute] longValue]; - BOOL trackEnabled = [[track attributeForKey:QTTrackEnabledAttribute] boolValue]; - NSSize videoSize = [[track attributeForKey:QTTrackDimensionsAttribute] sizeValue]; - QTMedia *trackMedia; - float frameRate = 29.97; // default - NSString *codecName = nil; - - TRACK_LOG(@"Video QTTrack: %@", track); - TRACK_LOG(@" - id %ld (%sabled)", trackID, trackEnabled ? "en" : "dis"); - - CTrack::Encoding encoding = CTrack::CUSTOM; - if ([track respondsToSelector:@selector(codecName)]) { - codecName = [[track codecName] lowercaseString]; - if ([codecName hasPrefix:@"h.264"] || [codecName hasPrefix:@"avc"]) { - encoding = CTrack::H264; - } - } - TRACK_LOG(@" - encoding %d (name %@)", encoding, codecName); - - if ([track respondsToSelector:@selector(floatFrameRate)]) { - frameRate = [track floatFrameRate]; - TRACK_LOG(@" - provided frame rate %0.2f", frameRate); - } else if ((trackMedia = track.media) != nil) { - // estimate frame rate based on sample count and track duration - if ([trackMedia hasCharacteristic:QTMediaCharacteristicHasVideoFrameRate]) { - QTTime duration = [[trackMedia attributeForKey:QTMediaDurationAttribute] QTTimeValue]; - float samples = (float)[[trackMedia attributeForKey:QTMediaSampleCountAttribute] longValue]; - frameRate = samples * ((float)duration.timeScale / (float)duration.timeValue); - TRACK_LOG(@" - estimated frame rate %0.2f", frameRate); - } else { - TRACK_LOG(@" - Unable to determine frame rate!"); - } - } - - // If we will support more media formats in OS X Platform, then select apropriate name. - // Now only "video/x-h264" is supported - CVideoTrack *cvt = new CVideoTrack((int64_t)trackID, "video/x-h264", encoding, trackEnabled, - (int)videoSize.width, (int)videoSize.height, frameRate, false); - eventHandler->SendVideoTrackEvent(cvt); - delete cvt; - } - - // get audio tracks - tracks = [movie tracksOfMediaType:QTMediaTypeSound]; - for (QTTrack *track in tracks) { - long trackID = [[track attributeForKey:QTTrackIDAttribute] longValue]; - BOOL trackEnabled = [[track attributeForKey:QTTrackEnabledAttribute] boolValue]; - NSString *codecName = nil; - - TRACK_LOG(@"Audio QTTrack: %@", track); - TRACK_LOG(@" - id %ld (%sabled)", trackID, trackEnabled ? "en" : "dis"); - - CTrack::Encoding encoding = CTrack::CUSTOM; - if ([track respondsToSelector:@selector(codecName)]) { - codecName = [[track codecName] lowercaseString]; - - if ([codecName hasPrefix:@"aac"]) { - encoding = CTrack::AAC; - } else if ([codecName hasPrefix:@"mp3"]) { // FIXME: verify these values, if we ever officially support them - encoding = CTrack::MPEG1LAYER3; - } else if ([codecName hasPrefix:@"mpeg"] || [codecName hasPrefix:@"mp2"]) { - encoding = CTrack::MPEG1AUDIO; - } - } - TRACK_LOG(@" - encoding %d (name %@)", encoding, codecName); - - float rate = 44100.0; // sane default - if ([track respondsToSelector:@selector(audioSampleRate)]) { - rate = floor([track audioSampleRate] * 1000.0); // audioSampleRate returns KHz - } - TRACK_LOG(@" - sample rate %0.0f", rate); - - int channelCount = 2; - if ([track respondsToSelector:@selector(audioChannelCount)]) { - channelCount = [track audioChannelCount]; - if (channelCount == 0) { - // we may not know (happens with some HLS streams) so just report stereo and hope for the best - channelCount = 2; - } - } - TRACK_LOG(@" - channels %d", channelCount); - - int channelMask; - switch (channelCount) { - default: - channelMask = CAudioTrack::FRONT_LEFT | CAudioTrack::FRONT_RIGHT; - break; - case 5: - case 6: - // FIXME: Umm.. why don't we have a SUBWOOFER channel, which is what 5.1 (aka 6) channel audio is??? - channelMask = CAudioTrack::FRONT_LEFT | CAudioTrack::FRONT_RIGHT - | CAudioTrack::REAR_LEFT | CAudioTrack::REAR_RIGHT - | CAudioTrack::FRONT_CENTER; - break; - } - TRACK_LOG(@" - channel mask %02x", channelMask); - - NSString *lang = @"und"; - if ([track respondsToSelector:@selector(isoLanguageCodeAsString)]) { - NSString *newLang = [track isoLanguageCodeAsString]; - // it could return nil, in which case it's undetermined - if (newLang) { - lang = newLang; - } - } - TRACK_LOG(@" - language %@", lang); - - // If we will support more media formats in OS X Platform, then select apropriate name. - // Now only "audio/mpeg" is supported - CAudioTrack *cat = new CAudioTrack((int64_t)trackID, "audio/mpeg", encoding, (bool)trackEnabled, - [lang UTF8String], channelCount, channelMask, rate); - eventHandler->SendAudioTrackEvent(cat); - delete cat; - } - - // get subtitle tracks - // FIXME: also QTMediaType{ClosedCaption,Text,etc...} - tracks = [movie tracksOfMediaType:QTMediaTypeSubtitle]; - for (QTTrack *track in tracks) { - long trackID = [[track attributeForKey:QTTrackIDAttribute] longValue]; - BOOL trackEnabled = [[track attributeForKey:QTTrackEnabledAttribute] boolValue]; - NSString *name = [track attributeForKey:QTTrackDisplayNameAttribute]; - NSString *codecName = nil; - - TRACK_LOG(@"Subtitle QTTrack: %@", track); - TRACK_LOG(@" - id %ld (%sabled)", trackID, trackEnabled ? "en" : "dis"); - - CTrack::Encoding encoding = CTrack::CUSTOM; - if ([track respondsToSelector:@selector(codecName)]) { - codecName = [[track codecName] lowercaseString]; - - if ([codecName hasPrefix:@"aac"]) { - encoding = CTrack::AAC; - } else if ([codecName hasPrefix:@"mp3"]) { // FIXME: verify these values, if we ever officially support them - encoding = CTrack::MPEG1LAYER3; - } else if ([codecName hasPrefix:@"mpeg"] || [codecName hasPrefix:@"mp2"]) { - encoding = CTrack::MPEG1AUDIO; - } - } - TRACK_LOG(@" - encoding %d (name %@)", encoding, codecName); - - NSString *lang = nil; - if ([track respondsToSelector:@selector(isoLanguageCodeAsString)]) { - NSString *newLang = [track isoLanguageCodeAsString]; - // it could return nil, in which case it's undetermined - if (newLang) { - lang = newLang; - } - } - TRACK_LOG(@" - language %@", lang); - - CSubtitleTrack *cat = new CSubtitleTrack((int64_t)trackID, [name UTF8String], encoding, (bool)trackEnabled, - [lang UTF8String]); - eventHandler->SendSubtitleTrackEvent(cat); - delete cat; - } - } - -#if DUMP_TRACK_INFO - LOGGER_INFOMSG([trackLog UTF8String]); - [trackLog release]; -#endif -} - -- (void) setMovieReady -{ - if (movieReady) { - return; - } - - movieReady = YES; - - // send player ready - [self setPlayerState:kPlayerState_READY]; - - // get duration - NSNumber *hasDuration = [movie attributeForKey:QTMovieHasDurationAttribute]; - if (!suppressDurationEvents && hasDuration.boolValue) { - QTTime movieDur = movie.duration; - // send INFINITY if it's indefinite - if (QTTimeIsIndefinite(movieDur)) { - eventHandler->SendDurationUpdateEvent(INFINITY); - // and suppress all other duration events - suppressDurationEvents = YES; - } else { - // otherwise send duration - NSTimeInterval duration; - if (QTGetTimeInterval(movieDur, &duration)) { - eventHandler->SendDurationUpdateEvent(self.duration); - } - } - } - - // Get movie tracks (deferred) - [self parseMovieTracks]; - - // Assert settings - if (currentTime != 0.0) { - movie.currentTime = QTMakeTimeWithTimeInterval(self.currentTime); - } - - if (mute) { - movie.muted = YES; - } - - if (volume != 1.0) { - movie.volume = volume; - } - - if (requestedState == kPlaybackState_Play) { - [movie play]; - [movie setRate:requestedRate]; - } -} - -- (void) sendVideoFrame:(CVPixelBufferRef)buf hostTime:(uint64_t)hostTime -{ - // http://javafx-jira.kenai.com/browse/RT-27041 - // TODO: send off to a work queue for processing on a separate thread to avoid deadlock issues during shutdown - - if (movie && movieReady && eventHandler) { - if (updateHostTimeBase) { - double now = currentTime; - uint64_t hostTime = CVGetCurrentHostTime(); - hostTimeFreq = CVGetHostClockFrequency(); - uint64_t nowDelta = (uint64_t)(now * hostTimeFreq); // current time in host frequency units - hostTimeBase = hostTime - nowDelta; // Host time at movie time zero - updateHostTimeBase = NO; - } - double frameTime = (double)(hostTime - hostTimeBase) / hostTimeFreq; - - CVVideoFrame *frame = NULL; - try { - frame = new CVVideoFrame(buf, frameTime, hostTime); - } catch (const char *message) { - LOGGER_DEBUGMSG(message); - return; - } - - if (previousWidth < 0 || previousHeight < 0 - || previousWidth != frame->GetWidth() || previousHeight != frame->GetHeight()) - { - // Send/Queue frame size changed event - previousWidth = frame->GetWidth(); - previousHeight = frame->GetHeight(); - eventHandler->SendFrameSizeChangedEvent(previousWidth, previousHeight); - } - eventHandler->SendNewFrameEvent(frame); - } -} - -@end