From 32464a364f83305987e276720726e4ecb2cbfbbd Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Tue, 15 Jul 2014 19:12:35 +0200 Subject: [PATCH] updating header info, adding AVKit support, iOS support (cherry picked from commit a485823cb7d47d88c021dc37c749e8c33dd7baa3 and 2e2cfd9d55708aca49174461a8448ecd9ff2d737) --- src/osx/cocoa/mediactrl.mm | 603 +++++++++++++++++++++++++++++++++++-- 1 file changed, 584 insertions(+), 19 deletions(-) diff --git a/src/osx/cocoa/mediactrl.mm b/src/osx/cocoa/mediactrl.mm index 5a26b5c360..9df6eb4501 100644 --- a/src/osx/cocoa/mediactrl.mm +++ b/src/osx/cocoa/mediactrl.mm @@ -1,10 +1,10 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: src/cocoa/mediactrl.cpp +// Name: src/osx/cocoa/mediactrl.mm // Purpose: Built-in Media Backends for Cocoa -// Author: Ryan Norton +// Author: Ryan Norton , Stefan Csomor // Modified by: // Created: 02/03/05 -// Copyright: (c) 2004-2005 Ryan Norton, (c) 2005 David Elliot +// Copyright: (c) 2004-2005 Ryan Norton, (c) 2005 David Elliot, (c) Stefan Csomor // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -36,10 +36,34 @@ #include "wx/osx/private.h" +#ifndef wxOSX_USE_QTKIT + #if wxOSX_USE_IPHONE + #define wxOSX_USE_QTKIT 0 + #define wxOSX_USE_AVFOUNDATION 1 + #else + #define wxOSX_USE_QTKIT 1 + #define wxOSX_USE_AVFOUNDATION 0 + #endif +#else + #if wxOSX_USE_QTKIT + #define wxOSX_USE_AVFOUNDATION 0 + #else + #define wxOSX_USE_AVFOUNDATION 1 + #endif +#endif + +#if wxOSX_USE_AVFOUNDATION && wxOSX_USE_COCOA && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 + #define wxOSX_USE_AVKIT 1 +#else + #define wxOSX_USE_AVKIT 0 +#endif + //=========================================================================== // BACKEND DECLARATIONS //=========================================================================== +#if wxOSX_USE_QTKIT + //--------------------------------------------------------------------------- // // wxQTMediaBackend @@ -147,9 +171,9 @@ private: -(void)dealloc { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:self]; + [nc removeObserver:self]; - [super dealloc]; + [super dealloc]; } -(wxQTMediaBackend*) backend @@ -173,17 +197,17 @@ private: - (void)movieRateChanged:(NSNotification *)notification { - NSDictionary *userInfo = [notification userInfo]; - - NSNumber *newRate = [userInfo objectForKey:QTMovieRateDidChangeNotificationParameter]; + NSDictionary *userInfo = [notification userInfo]; - if ([newRate intValue] == 0) - { - m_backend->QueuePauseEvent(); - } + NSNumber *newRate = [userInfo objectForKey:QTMovieRateDidChangeNotificationParameter]; + + if ([newRate intValue] == 0) + { + m_backend->QueuePauseEvent(); + } else if ( [self isPlaying] == NO ) { - m_backend->QueuePlayEvent(); + m_backend->QueuePlayEvent(); } } @@ -207,12 +231,12 @@ private: -(BOOL)isPlaying { - if ([self rate] == 0) - { - return NO; - } - - return YES; + if ([self rate] == 0) + { + return NO; + } + + return YES; } @end @@ -433,6 +457,547 @@ void wxQTMediaBackend::DoShowPlayerControls(wxMediaCtrlPlayerControls flags) } } +#endif + +#if wxOSX_USE_AVFOUNDATION + +//--------------------------------------------------------------------------- +// +// wxAVMediaBackend +// +//--------------------------------------------------------------------------- + +#import + +#if wxOSX_USE_AVKIT +#import +#endif + +class WXDLLIMPEXP_FWD_MEDIA wxAVMediaBackend; + +static void *AVSPPlayerItemStatusContext = &AVSPPlayerItemStatusContext; +static void *AVSPPlayerRateContext = &AVSPPlayerRateContext; + +@interface wxAVPlayer : AVPlayer { + + AVPlayerLayer *playerLayer; + + wxAVMediaBackend* m_backend; +} + +-(BOOL)isPlaying; + +@property (retain) AVPlayerLayer *playerLayer; + +@end + +class WXDLLIMPEXP_MEDIA wxAVMediaBackend : public wxMediaBackendCommonBase +{ +public: + + wxAVMediaBackend(); + ~wxAVMediaBackend(); + + virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name); + + virtual bool Play(); + virtual bool Pause(); + virtual bool Stop(); + + virtual bool Load(const wxString& fileName); + virtual bool Load(const wxURI& location); + + virtual wxMediaState GetState(); + + virtual bool SetPosition(wxLongLong where); + virtual wxLongLong GetPosition(); + virtual wxLongLong GetDuration(); + + virtual void Move(int x, int y, int w, int h); + wxSize GetVideoSize() const; + + virtual double GetPlaybackRate(); + virtual bool SetPlaybackRate(double dRate); + + virtual double GetVolume(); + virtual bool SetVolume(double dVolume); + + void Cleanup(); + void FinishLoad(); + + virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags); +private: + void DoShowPlayerControls(wxMediaCtrlPlayerControls flags); + + wxSize m_bestSize; //Original movie size + wxAVPlayer* m_player; //AVPlayer handle/instance + + wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags + + wxDECLARE_DYNAMIC_CLASS(wxAVMediaBackend); +}; + +// -------------------------------------------------------------------------- +// wxAVMediaBackend +// -------------------------------------------------------------------------- + +@implementation wxAVPlayer + +@synthesize playerLayer; + +- (id) init +{ + self = [super init]; + + [self addObserver:self forKeyPath:@"currentItem.status" + options:NSKeyValueObservingOptionNew context:AVSPPlayerItemStatusContext]; + [self addObserver:self forKeyPath:@"rate" + options:NSKeyValueObservingOptionNew context:AVSPPlayerRateContext]; + + return self; +} + +- (void)dealloc +{ + [playerLayer release]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [self removeObserver:self forKeyPath:@"rate" context:AVSPPlayerRateContext]; + [self removeObserver:self forKeyPath:@"currentItem.status" context:AVSPPlayerItemStatusContext]; + + [super dealloc]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == AVSPPlayerItemStatusContext) + { + id val = [change objectForKey:NSKeyValueChangeNewKey]; + if ( val != [NSNull null ] ) + { + AVPlayerStatus status = [ val integerValue]; + + switch (status) + { + case AVPlayerItemStatusUnknown: + break; + case AVPlayerItemStatusReadyToPlay: + [[NSNotificationCenter defaultCenter] + addObserver:self selector:@selector(playerItemDidReachEnd:) + name:AVPlayerItemDidPlayToEndTimeNotification object:self.currentItem]; + m_backend->FinishLoad(); + break; + case AVPlayerItemStatusFailed: + break; + default: + break; + } + } + } + else if (context == AVSPPlayerRateContext) + { + NSNumber* newRate = [change objectForKey:NSKeyValueChangeNewKey]; + if ([newRate intValue] == 0) + { + m_backend->QueuePauseEvent(); + } + else if ( [self isPlaying] == NO ) + { + m_backend->QueuePlayEvent(); + } + } + else + { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +-(wxAVMediaBackend*) backend +{ + return m_backend; +} + +-(void) setBackend:(wxAVMediaBackend*) backend +{ + m_backend = backend; +} + +- (void)playerItemDidReachEnd:(NSNotification *)notification +{ + if ( m_backend ) + { + if ( m_backend->SendStopEvent() ) + m_backend->QueueFinishEvent(); + } +} + +-(BOOL)isPlaying +{ + if ([self rate] == 0) + { + return NO; + } + + return YES; +} + +@end + +#if wxOSX_USE_IPHONE + +@interface wxAVView : UIView +{ +} + +@end + +@implementation wxAVView + ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + wxOSXIPhoneClassAddWXMethods( self ); + } +} + ++ (Class)layerClass +{ + return [AVPlayerLayer class]; +} + +- (id) initWithFrame:(CGRect)rect player:(wxAVPlayer*) player +{ + if ( !(self=[super initWithFrame:rect]) ) + return nil; + + AVPlayerLayer* playerLayer = (AVPlayerLayer*) [self layer]; + [playerLayer setPlayer: player]; + [player setPlayerLayer:playerLayer]; + + return self; +} + +- (AVPlayerLayer*) playerLayer +{ + return (AVPlayerLayer*) [self layer]; +} +@end + +#else + +#if wxOSX_USE_AVKIT + +@interface wxAVPlayerView : AVPlayerView +{ +} + +@end + +@implementation wxAVPlayerView + ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + wxOSXCocoaClassAddWXMethods( self ); + } +} + +- (id) initWithFrame:(NSRect)rect player:(wxAVPlayer*) player +{ + if ( !(self=[super initWithFrame:rect]) ) + return nil; + + self.player = player; + + return self; +} + +- (AVPlayerLayer*) playerLayer +{ + return (AVPlayerLayer*) [[[self layer] sublayers] firstObject]; +} + +@end + +#endif // wxOSX_USE_AVKIT + +@interface wxAVView : NSView +{ +} + +@end + +@implementation wxAVView + ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + wxOSXCocoaClassAddWXMethods( self ); + } +} + +- (id) initWithFrame:(NSRect)rect player:(AVPlayer*) player +{ + if ( !(self=[super initWithFrame:rect]) ) + return nil; + + [self setWantsLayer:YES]; + AVPlayerLayer* playerlayer = [[AVPlayerLayer playerLayerWithPlayer: player] retain]; + [player setPlayerLayer:playerlayer]; + + [playerlayer setFrame:[[self layer] bounds]]; + [playerlayer setAutoresizingMask:kCALayerWidthSizable | kCALayerHeightSizable]; + [[self layer] addSublayer:playerlayer]; + + return self; +} + +- (AVPlayerLayer*) playerLayer +{ + return (AVPlayerLayer*) [[[self layer] sublayers] firstObject]; +} + +@end + +#endif + +IMPLEMENT_DYNAMIC_CLASS(wxAVMediaBackend, wxMediaBackend); + +wxAVMediaBackend::wxAVMediaBackend() : +m_player(nil), +m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE) +{ +} + +wxAVMediaBackend::~wxAVMediaBackend() +{ + Cleanup(); +} + +bool wxAVMediaBackend::CreateControl(wxControl* inctrl, wxWindow* parent, + wxWindowID wid, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator& validator, + const wxString& name) +{ + wxMediaCtrl* mediactrl = (wxMediaCtrl*) inctrl; + + mediactrl->DontCreatePeer(); + + if ( !mediactrl->wxControl::Create( + parent, wid, pos, size, + wxWindow::MacRemoveBordersFromStyle(style), + validator, name)) + { + return false; + } + + m_player = [[wxAVPlayer alloc] init]; + [m_player setBackend:this]; + + NSRect r = wxOSXGetFrameForControl( mediactrl, pos , size ) ; + + WXWidget view = NULL; +#if wxOSX_USE_AVKIT + if ( NSClassFromString(@"AVPlayerView") ) + { + view = [[wxAVPlayerView alloc] initWithFrame: r player:m_player]; + [(wxAVPlayerView*) view setControlsStyle:AVPlayerViewControlsStyleNone]; + } +#endif + + if ( view == NULL ) + { + view = [[wxAVView alloc] initWithFrame: r player:m_player]; + } + +#if wxOSX_USE_IPHONE + wxWidgetIPhoneImpl* impl = new wxWidgetIPhoneImpl(mediactrl,view); +#else + wxWidgetCocoaImpl* impl = new wxWidgetCocoaImpl(mediactrl,view); +#endif + mediactrl->SetPeer(impl); + + m_ctrl = mediactrl; + return true; +} + +bool wxAVMediaBackend::Load(const wxString& fileName) +{ + return Load( + wxURI( + wxString( wxT("file://") ) + fileName + ) + ); +} + +bool wxAVMediaBackend::Load(const wxURI& location) +{ + wxCFStringRef uri(location.BuildURI()); + NSURL *url = [NSURL URLWithString: uri.AsNSString()]; + + AVAsset* asset = [AVAsset assetWithURL:url]; + if (! asset ) + return false; + + if ( [asset isPlayable] ) + { + AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset]; + [m_player replaceCurrentItemWithPlayerItem:playerItem]; + + return playerItem != nil; + } + return false; +} + +void wxAVMediaBackend::FinishLoad() +{ + DoShowPlayerControls(m_interfaceflags); + + AVPlayerItem *playerItem = [m_player currentItem]; + + CGSize s = [playerItem presentationSize]; + m_bestSize = wxSize(s.width, s.height); + + NotifyMovieLoaded(); +} + +bool wxAVMediaBackend::Play() +{ + [m_player play]; + return true; +} + +bool wxAVMediaBackend::Pause() +{ + [m_player pause]; + return true; +} + +bool wxAVMediaBackend::Stop() +{ + [m_player pause]; + [m_player seekToTime:kCMTimeZero]; + return true; +} + +double wxAVMediaBackend::GetVolume() +{ + return [m_player volume]; +} + +bool wxAVMediaBackend::SetVolume(double dVolume) +{ + [m_player setVolume:dVolume]; + return true; +} +double wxAVMediaBackend::GetPlaybackRate() +{ + return [m_player rate]; +} + +bool wxAVMediaBackend::SetPlaybackRate(double dRate) +{ + [m_player setRate:dRate]; + return true; +} + +bool wxAVMediaBackend::SetPosition(wxLongLong where) +{ + [m_player seekToTime:CMTimeMakeWithSeconds(where.GetValue() / 1000.0, 1) + toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; + + return true; +} + +wxLongLong wxAVMediaBackend::GetPosition() +{ + return CMTimeGetSeconds([m_player currentTime])*1000.0; +} + +wxLongLong wxAVMediaBackend::GetDuration() +{ + AVPlayerItem *playerItem = [m_player currentItem]; + + if ([playerItem status] == AVPlayerItemStatusReadyToPlay) + return CMTimeGetSeconds([[playerItem asset] duration])*1000.0; + else + return 0.f; +} + +wxMediaState wxAVMediaBackend::GetState() +{ + if ( [m_player isPlaying] ) + return wxMEDIASTATE_PLAYING; + else + { + if ( GetPosition() == 0 ) + return wxMEDIASTATE_STOPPED; + else + return wxMEDIASTATE_PAUSED; + } +} + +void wxAVMediaBackend::Cleanup() +{ + [m_player pause]; + [m_player release]; + m_player = nil; +} + +wxSize wxAVMediaBackend::GetVideoSize() const +{ + return m_bestSize; +} + +void wxAVMediaBackend::Move(int x, int y, int w, int h) +{ + // as we have a native player, no need to move the video area +} + +bool wxAVMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags) +{ + if ( m_interfaceflags != flags ) + DoShowPlayerControls(flags); + + m_interfaceflags = flags; + return true; +} + +void wxAVMediaBackend::DoShowPlayerControls(wxMediaCtrlPlayerControls flags) +{ +#if wxOSX_USE_AVKIT + NSView* view = m_ctrl->GetHandle(); + if ( [view isKindOfClass:[wxAVPlayerView class]] ) + { + wxAVPlayerView* playerView = (wxAVPlayerView*) view; + if (flags == wxMEDIACTRLPLAYERCONTROLS_NONE ) + playerView.controlsStyle = AVPlayerViewControlsStyleNone; + else + playerView.controlsStyle = AVPlayerViewControlsStyleDefault; + } +#endif +} + +#endif + //in source file that contains stuff you don't directly use #include "wx/html/forcelnk.h" FORCE_LINK_ME(basewxmediabackends);