tdf#154285 Check upper bound of arguments in SbRtl_Minute function
[LibreOffice.git] / avmedia / source / macavf / player.mm
blob190840f6b11f319bf9e2c07f44892ce09c6387bf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
20 #include "player.hxx"
21 #include "framegrabber.hxx"
22 #include "window.hxx"
23 #include <rtl/ref.hxx>
25 #include <cmath> // for log10()
27 using namespace ::com::sun::star;
29 @implementation MacAVObserverObject
31 - (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
33     (void) pObject;
34     (void) pChangeDict;
35     avmedia::macavf::MacAVObserverHandler* pHandler = static_cast<avmedia::macavf::MacAVObserverHandler*>(pContext);
36     pHandler->handleObservation( pKeyPath );
39 - (void)onNotification:(NSNotification*)pNotification
41     NSString* pNoteName = [pNotification name];
42     HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
43     if( it != maHandlersForObject.end() )
44         (*it).second->handleObservation( pNoteName );
47 - (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
49     maHandlersForObject[ pObject] = pHandler;
52 - (void)removeHandlerForObject:(NSObject*)pObject
54     maHandlersForObject.erase( pObject);
57 @end
60 namespace avmedia::macavf {
62 MacAVObserverObject* MacAVObserverHandler::mpMacAVObserverObject = nullptr;
64 MacAVObserverObject* MacAVObserverHandler::getObserver()
66     if( !mpMacAVObserverObject)
67     {
68         mpMacAVObserverObject = [MacAVObserverObject alloc];
69         [mpMacAVObserverObject retain];
70     }
71     return mpMacAVObserverObject;
75 Player::Player()
76 :   mpPlayer( nullptr )
77 ,   mfUnmutedVolume( 0 )
78 ,   mfStopTime( DBL_MAX )
79 ,   mbMuted( false )
80 ,   mbLooping( false )
84 Player::~Player()
86     if( !mpPlayer )
87         return;
88     // remove the observers
89     [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
90     AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
91     [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
92         name:AVPlayerItemDidPlayToEndTimeNotification
93         object:pOldPlayerItem];
94     [getObserver() removeHandlerForObject:pOldPlayerItem];
95     // release the AVPlayer
96     CFRelease( mpPlayer );
100 bool Player::handleObservation( NSString* pKeyPath )
102     if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
103     {
104         if( mbLooping )
105             setMediaTime( 0.0);
106     }
107     return true;
111 bool Player::create( const OUString& rURL )
113     // get the media asset
114     NSString* aNSStr = [NSString stringWithCharacters:reinterpret_cast<unichar const *>(rURL.getStr()) length:rURL.getLength()];
115     SAL_WNODEPRECATED_DECLARATIONS_PUSH
116         //TODO: 10.11 stringByAddingPercentEscapesUsingEncoding
117     NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
118     SAL_WNODEPRECATED_DECLARATIONS_POP
119     // get the matching AVPlayerItem
120     AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
122     // create or update the AVPlayer with the new AVPlayerItem
123     if( !mpPlayer )
124     {
125         mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
126         CFRetain( mpPlayer );
127         [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
128     }
129     else
130     {
131         // remove the obsoleted observers
132         AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
133         [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
134         [getObserver() removeHandlerForObject:pOldPlayerItem];
135         [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
136             name:AVPlayerItemDidPlayToEndTimeNotification
137             object:pOldPlayerItem];
138         // replace the playeritem
139         [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
140     }
142     // observe the status of the current player item
143     [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
145     // observe playback-end needed for playback looping
146     [[NSNotificationCenter defaultCenter] addObserver:getObserver()
147         selector:@selector(onNotification:)
148         name:AVPlayerItemDidPlayToEndTimeNotification
149         object:pPlayerItem];
150     [getObserver() setHandlerForObject:pPlayerItem handler:this];
152     return true;
156 void SAL_CALL Player::start()
158     if( !mpPlayer )
159         return;
161     [mpPlayer play];
162     // else // TODO: delay until it becomes ready
166 void SAL_CALL Player::stop()
168     if( !mpPlayer )
169         return;
170     const bool bPlaying = isPlaying();
171     if( bPlaying )
172         [mpPlayer pause];
176 sal_Bool SAL_CALL Player::isPlaying()
178     if( !mpPlayer )
179         return false;
180     const float fRate = [mpPlayer rate];
181     return (fRate != 0.0);
185 double SAL_CALL Player::getDuration()
187     // slideshow checks for non-zero duration, so cheat here
188     double duration = 0.01;
190     if( mpPlayer )
191     {
192         AVPlayerItem* pItem = [mpPlayer currentItem];
193         if( [pItem status] == AVPlayerItemStatusReadyToPlay )
194             duration = CMTimeGetSeconds( [pItem duration] );
195         else // fall back to AVAsset's best guess
196             duration = CMTimeGetSeconds( [[pItem asset] duration] );
197     }
199     return duration;
203 void SAL_CALL Player::setMediaTime( double fTime )
205     if( mpPlayer )
206         [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
210 double SAL_CALL Player::getMediaTime()
212     if( !mpPlayer )
213         return 0.0;
215     const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
216     if( position >= mfStopTime )
217         if( isPlaying() )
218             stop();
220     return position;
224 void Player::setStopTime( double fTime )
226     mfStopTime = fTime;
230 double Player::getStopTime()
232     return mfStopTime;
236 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
238     mbLooping = bSet;
242 sal_Bool SAL_CALL Player::isPlaybackLoop()
244     return mbLooping;
248 void SAL_CALL Player::setMute( sal_Bool bSet )
250     if( !mpPlayer )
251         return;
253     mbMuted = bSet;
254     [mpPlayer setMuted:mbMuted];
258 sal_Bool SAL_CALL Player::isMute()
260     return mbMuted;
264 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
266     // -40dB <-> AVPlayer volume 0.0
267     //   0dB <-> AVPlayer volume 1.0
268     mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
270     // change volume
271     if( !mbMuted && mpPlayer )
272         [mpPlayer setVolume:mfUnmutedVolume];
276 sal_Int16 SAL_CALL Player::getVolumeDB()
278     if( !mpPlayer )
279         return 0;
281     // get the actual volume
282     const float fVolume = [mpPlayer volume];
284     // convert into Decibel value
285     // -40dB <-> AVPlayer volume 0.0
286     //   0dB <-> AVPlayer volume 1.0
287     const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
289     return static_cast<sal_Int16>(nVolumeDB);
293 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
295     awt::Size aSize( 0, 0 ); // default size
297     AVAsset* pMovie = [[mpPlayer currentItem] asset];
298 SAL_WNODEPRECATED_DECLARATIONS_PUSH
299         // 'tracksWithMediaType:' is deprecated: first deprecated in macOS 15.0 - Use
300         // loadTracksWithMediaType:completionHandler: instead
301     NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
302 SAL_WNODEPRECATED_DECLARATIONS_POP
303     if ([pVideoTracks count] > 0)
304     {
305         AVAssetTrack* pFirstVideoTrack = static_cast<AVAssetTrack*>([pVideoTracks objectAtIndex:0]);
306         const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
307         aSize = awt::Size( aPrefSize.width, aPrefSize.height );
308     }
310     return aSize;
314 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
316     // get the preferred window size
317     const awt::Size aSize( getPreferredPlayerWindowSize() );
319     // get the parent view
320     sal_IntPtr nNSViewPtr = 0;
321     aArguments[0] >>= nNSViewPtr;
322     NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
324     // check the window parameters
325     if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == nullptr) )
326          return {};
328     // create the window
329     return new ::avmedia::macavf::Window( *this, pParentView );
333 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
335     rtl::Reference<FrameGrabber> pGrabber = new FrameGrabber();
336     AVAsset* pMovie = [[mpPlayer currentItem] asset];
337     if( !pGrabber->create( pMovie ) )
338         return {};
340     return pGrabber;
344 OUString SAL_CALL Player::getImplementationName(  )
346     return AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME;
350 sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
352     return ServiceName == AVMEDIA_MACAVF_PLAYER_SERVICENAME;
356 uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames(  )
358     return { AVMEDIA_MACAVF_PLAYER_SERVICENAME };
361 } // namespace avmedia::macavf
363 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */