LibreOffice Module avmedia (master) 1
player.mm
Go to the documentation of this file.
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 */
19
20#include "player.hxx"
21#include "framegrabber.hxx"
22#include "window.hxx"
23#include <rtl/ref.hxx>
24
25#include <cmath> // for log10()
26
27using namespace ::com::sun::star;
28
29@implementation MacAVObserverObject
30
31- (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
32{
33 (void) pObject;
34 (void) pChangeDict;
36 pHandler->handleObservation( pKeyPath );
37}
38
39- (void)onNotification:(NSNotification*)pNotification
40{
41 NSString* pNoteName = [pNotification name];
42 HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
43 if( it != maHandlersForObject.end() )
44 (*it).second->handleObservation( pNoteName );
45}
46
47- (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
48{
49 maHandlersForObject[ pObject] = pHandler;
50}
51
52- (void)removeHandlerForObject:(NSObject*)pObject
53{
54 maHandlersForObject.erase( pObject);
55}
56
57@end
58
59
60namespace avmedia::macavf {
61
63
65{
67 {
69 [mpMacAVObserverObject retain];
70 }
72}
73
74
76: mpPlayer( nullptr )
77, mfUnmutedVolume( 0 )
78, mfStopTime( DBL_MAX )
79, mbMuted( false )
80, mbLooping( false )
81{}
82
83
85{
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 );
97}
98
99
100bool Player::handleObservation( NSString* pKeyPath )
101{
102 if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
103 {
104 if( mbLooping )
105 setMediaTime( 0.0);
106 }
107 return true;
108}
109
110
111bool Player::create( const OUString& rURL )
112{
113 // get the media asset
114 NSString* aNSStr = [NSString stringWithCharacters:reinterpret_cast<unichar const *>(rURL.getStr()) length:rURL.getLength()];
116 //TODO: 10.11 stringByAddingPercentEscapesUsingEncoding
117 NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
119 // get the matching AVPlayerItem
120 AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
121
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 }
141
142 // observe the status of the current player item
143 [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
144
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];
151
152 return true;
153}
154
155
156void SAL_CALL Player::start()
157{
158 if( !mpPlayer )
159 return;
160
161 [mpPlayer play];
162 // else // TODO: delay until it becomes ready
163}
164
165
166void SAL_CALL Player::stop()
167{
168 if( !mpPlayer )
169 return;
170 const bool bPlaying = isPlaying();
171 if( bPlaying )
172 [mpPlayer pause];
173}
174
175
177{
178 if( !mpPlayer )
179 return false;
180 const float fRate = [mpPlayer rate];
181 return (fRate != 0.0);
182}
183
184
185double SAL_CALL Player::getDuration()
186{
187 // slideshow checks for non-zero duration, so cheat here
188 double duration = 0.01;
189
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 }
198
199 return duration;
200}
201
202
203void SAL_CALL Player::setMediaTime( double fTime )
204{
205 if( mpPlayer )
206 [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
207}
208
209
210double SAL_CALL Player::getMediaTime()
211{
212 if( !mpPlayer )
213 return 0.0;
214
215 const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
216 if( position >= mfStopTime )
217 if( isPlaying() )
218 stop();
219
220 return position;
221}
222
223
224void Player::setStopTime( double fTime )
225{
226 mfStopTime = fTime;
227}
228
229
231{
232 return mfStopTime;
233}
234
235
237{
238 mbLooping = bSet;
239}
240
241
243{
244 return mbLooping;
245}
246
247
248void SAL_CALL Player::setMute( sal_Bool bSet )
249{
250 if( !mpPlayer )
251 return;
252
253 mbMuted = bSet;
254 [mpPlayer setMuted:mbMuted];
255}
256
257
259{
260 return mbMuted;
261}
262
263
264void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
265{
266 // -40dB <-> AVPlayer volume 0.0
267 // 0dB <-> AVPlayer volume 1.0
268 mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
269
270 // change volume
271 if( !mbMuted && mpPlayer )
272 [mpPlayer setVolume:mfUnmutedVolume];
273}
274
275
276sal_Int16 SAL_CALL Player::getVolumeDB()
277{
278 if( !mpPlayer )
279 return 0;
280
281 // get the actual volume
282 const float fVolume = [mpPlayer volume];
283
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));
288
289 return static_cast<sal_Int16>(nVolumeDB);
290}
291
292
294{
295 awt::Size aSize( 0, 0 ); // default size
296
297 AVAsset* pMovie = [[mpPlayer currentItem] asset];
298 NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
299 if ([pVideoTracks count] > 0)
300 {
301 AVAssetTrack* pFirstVideoTrack = static_cast<AVAssetTrack*>([pVideoTracks objectAtIndex:0]);
302 const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
303 aSize = awt::Size( aPrefSize.width, aPrefSize.height );
304 }
305
306 return aSize;
307}
308
309
310uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
311{
312 // get the preferred window size
313 const awt::Size aSize( getPreferredPlayerWindowSize() );
314
315 // get the parent view
316 sal_IntPtr nNSViewPtr = 0;
317 aArguments[0] >>= nNSViewPtr;
318 NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
319
320 // check the window parameters
321 if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == nullptr) )
322 return {};
323
324 // create the window
325 return new ::avmedia::macavf::Window( *this, pParentView );
326}
327
328
329uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
330{
332 AVAsset* pMovie = [[mpPlayer currentItem] asset];
333 if( !pGrabber->create( pMovie ) )
334 return {};
335
336 return pGrabber;
337}
338
339
341{
343}
344
345
346sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
347{
349}
350
351
352uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames( )
353{
355}
356
357} // namespace avmedia::macavf
358
359/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static MacAVObserverObject * getObserver()
Definition: player.mm:64
virtual bool handleObservation(NSString *pKeyPath)=0
static MacAVObserverObject * mpMacAVObserverObject
virtual void SAL_CALL setMediaTime(double fTime) override
Definition: player.mm:203
virtual void SAL_CALL setMute(sal_Bool bSet) override
Definition: player.mm:248
virtual double SAL_CALL getMediaTime() override
Definition: player.mm:210
virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize() override
Definition: player.mm:293
virtual void SAL_CALL setVolumeDB(sal_Int16 nVolumeDB) override
Definition: player.mm:264
virtual sal_Bool SAL_CALL isPlaybackLoop() override
Definition: player.mm:242
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
Definition: player.mm:346
virtual double getStopTime()
Definition: player.mm:230
bool create(const OUString &rURL)
Definition: player.mm:111
virtual sal_Int16 SAL_CALL getVolumeDB() override
Definition: player.mm:276
virtual sal_Bool SAL_CALL isPlaying() override
Definition: player.mm:176
virtual css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow(const css::uno::Sequence< css::uno::Any > &aArguments) override
virtual void SAL_CALL setPlaybackLoop(sal_Bool bSet) override
Definition: player.mm:236
virtual ~Player() override
Definition: player.mm:84
virtual bool handleObservation(NSString *pKeyPath) override
Definition: player.mm:100
virtual void SAL_CALL start() override
Definition: player.mm:156
virtual void setStopTime(double fTime)
Definition: player.mm:224
virtual double SAL_CALL getDuration() override
Definition: player.mm:185
virtual void SAL_CALL stop() override
Definition: player.mm:166
virtual OUString SAL_CALL getImplementationName() override
Definition: player.mm:340
virtual sal_Bool SAL_CALL isMute() override
Definition: player.mm:258
virtual css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber() override
Definition: player.mm:329
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
Definition: player.mm:352
EmbeddedObjectRef * pObject
Sequence< PropertyValue > aArguments
HandlersForObject maHandlersForObject
#define AVMEDIA_MACAVF_PLAYER_SERVICENAME
#define AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME
def position(n=-1)
#define SAL_WNODEPRECATED_DECLARATIONS_POP
unsigned char sal_Bool
#define SAL_WNODEPRECATED_DECLARATIONS_PUSH