1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23 #include "macavf_player.hxx"
24 #include "macavf_framegrabber.hxx"
25 #include "macavf_window.hxx"
26
27 #include <cmath> // for log10()
28
29 using namespace ::com::sun::star;
30
31 #include <hash_map>
32 typedef std::hash_map<NSObject*,avmedia::macavf::MacAVObserverHandler*> HandlersForObject;
33
34 @implementation MacAVObserverObject
35 {
36 HandlersForObject maHandlersForObject;
37 }
38 - (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
39 {
40 NSString* pDictStr = [NSString stringWithFormat:@"%@", pChangeDict];
41 OSL_TRACE( "MacAVObserver::onKeyChange k=\"%s\" c=%s", [pKeyPath UTF8String], [pDictStr UTF8String]);
42 avmedia::macavf::MacAVObserverHandler* pHandler = (avmedia::macavf::MacAVObserverHandler*)pContext;
43 pHandler->handleObservation( pKeyPath );
44 }
45
46 - (void)onNotification:(NSNotification*)pNotification
47 {
48 NSString* pNoteName = (NSString*)[pNotification name];
49 OSL_TRACE( "MacAVObserver::onNotification key=\"%s\"", [pNoteName UTF8String]);
50 HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
51 if( it != maHandlersForObject.end() )
52 (*it).second->handleObservation( pNoteName );
53 }
54
55 - (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
56 {
57 maHandlersForObject[ pObject] = pHandler;
58 }
59
60 - (void)removeHandlerForObject:(NSObject*)pObject
61 {
62 maHandlersForObject.erase( pObject);
63 }
64
65 @end
66
67
68 namespace avmedia { namespace macavf {
69
70 MacAVObserverObject* MacAVObserverHandler::mpMacAVObserverObject = NULL;
71
getObserver() const72 MacAVObserverObject* MacAVObserverHandler::getObserver() const
73 {
74 if( !mpMacAVObserverObject)
75 {
76 mpMacAVObserverObject = [MacAVObserverObject alloc];
77 [mpMacAVObserverObject retain];
78 }
79 return mpMacAVObserverObject;
80 }
81
82
83 // ----------------
84 // - Player -
85 // ----------------
86
Player(const uno::Reference<lang::XMultiServiceFactory> & rxMgr)87 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr )
88 : mxMgr( rxMgr )
89 , mpPlayer( NULL )
90 , mfUnmutedVolume( 0 )
91 , mfStopTime( DBL_MAX )
92 , mbMuted( false )
93 , mbLooping( false )
94 {}
95
96 // ------------------------------------------------------------------------------
97
~Player()98 Player::~Player()
99 {
100 if( !mpPlayer )
101 return;
102 // remove the observers
103 [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
104 AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
105 [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
106 name:AVPlayerItemDidPlayToEndTimeNotification
107 object:pOldPlayerItem];
108 [getObserver() removeHandlerForObject:pOldPlayerItem];
109 // release the AVPlayer
110 CFRelease( mpPlayer );
111 }
112
113 // ------------------------------------------------------------------------------
114
handleObservation(NSString * pKeyPath)115 bool Player::handleObservation( NSString* pKeyPath )
116 {
117 OSL_TRACE( "AVPlayer::handleObservation key=\"%s\"", [pKeyPath UTF8String]);
118 if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
119 {
120 OSL_TRACE( "AVPlayer replay=%d", mbLooping);
121 if( mbLooping )
122 setMediaTime( 0.0);
123 }
124 return true;
125 }
126
127 // ------------------------------------------------------------------------------
128
create(const::rtl::OUString & rURL)129 bool Player::create( const ::rtl::OUString& rURL )
130 {
131 // get the media asset
132 NSString* aNSStr = [NSString stringWithCharacters:rURL.getStr() length:rURL.getLength()];
133 NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
134 // get the matching AVPlayerItem
135 AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
136
137 // create or update the AVPlayer with the new AVPlayerItem
138 if( !mpPlayer )
139 {
140 mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
141 CFRetain( mpPlayer );
142 [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
143 }
144 else
145 {
146 // remove the obsoleted observers
147 AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
148 [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
149 [getObserver() removeHandlerForObject:pOldPlayerItem];
150 [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
151 name:AVPlayerItemDidPlayToEndTimeNotification
152 object:pOldPlayerItem];
153 // replace the playeritem
154 [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
155 }
156
157 // observe the status of the current player item
158 [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
159
160 // observe playback-end needed for playback looping
161 [[NSNotificationCenter defaultCenter] addObserver:getObserver()
162 selector:@selector(onNotification:)
163 name:AVPlayerItemDidPlayToEndTimeNotification
164 object:pPlayerItem];
165 [getObserver() setHandlerForObject:pPlayerItem handler:this];
166
167 return true;
168 }
169
170 // ------------------------------------------------------------------------------
171
start()172 void SAL_CALL Player::start()
173 throw (uno::RuntimeException)
174 {
175 if( !mpPlayer )
176 return;
177 #if 0
178 const AVPlayerStatus eStatus = [mpPlayer status];
179 OSL_TRACE ("Player::start status=%d", (int)eStatus);
180 if( eStatus == AVPlayerStatusReadyToPlay)
181 #endif
182 [mpPlayer play];
183 // else // TODO: delay until it becomes ready
184 }
185
186 // ------------------------------------------------------------------------------
187
stop()188 void SAL_CALL Player::stop()
189 throw (uno::RuntimeException)
190 {
191 if( !mpPlayer )
192 return;
193 const bool bPlaying = isPlaying();
194 OSL_TRACE ("Player::stop() playing=%d", bPlaying);
195 if( bPlaying )
196 [mpPlayer pause];
197 }
198
199 // ------------------------------------------------------------------------------
200
isPlaying()201 sal_Bool SAL_CALL Player::isPlaying()
202 throw (uno::RuntimeException)
203 {
204 if( !mpPlayer )
205 return false;
206 const float fRate = [mpPlayer rate];
207 return (fRate != 0.0);
208 }
209
210 // ------------------------------------------------------------------------------
211
getDuration()212 double SAL_CALL Player::getDuration()
213 throw (uno::RuntimeException)
214 {
215 // slideshow checks for non-zero duration, so cheat here
216 double duration = 0.01;
217
218 if( mpPlayer )
219 {
220 AVPlayerItem* pItem = [mpPlayer currentItem];
221 if( [pItem status] == AVPlayerItemStatusReadyToPlay )
222 duration = CMTimeGetSeconds( [pItem duration] );
223 else // fall back to AVAsset's best guess
224 duration = CMTimeGetSeconds( [[pItem asset] duration] );
225 }
226
227 return duration;
228 }
229
230 // ------------------------------------------------------------------------------
231
setMediaTime(double fTime)232 void SAL_CALL Player::setMediaTime( double fTime )
233 throw (uno::RuntimeException)
234 {
235 OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
236 if( mpPlayer )
237 [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
238 }
239
240 // ------------------------------------------------------------------------------
241
getMediaTime()242 double SAL_CALL Player::getMediaTime()
243 throw (uno::RuntimeException)
244 {
245 if( !mpPlayer )
246 return 0.0;
247
248 const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
249 OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
250 if( position >= mfStopTime )
251 if( isPlaying() )
252 stop();
253
254 return position;
255 }
256
257 // ------------------------------------------------------------------------------
258
setStopTime(double fTime)259 void SAL_CALL Player::setStopTime( double fTime )
260 throw (uno::RuntimeException)
261 {
262 OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
263 mfStopTime = fTime;
264 }
265
266 // ------------------------------------------------------------------------------
267
getStopTime()268 double SAL_CALL Player::getStopTime()
269 throw (uno::RuntimeException)
270 {
271 return mfStopTime;
272 }
273
274 // ------------------------------------------------------------------------------
275
setRate(double fRate)276 void SAL_CALL Player::setRate( double fRate )
277 throw (uno::RuntimeException)
278 {
279 OSL_TRACE ("Player::setRate( %.3f)", fRate);
280 if( !mpPlayer )
281 return;
282
283 // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
284 [mpPlayer setRate: fRate];
285 }
286
287 // ------------------------------------------------------------------------------
288
getRate()289 double SAL_CALL Player::getRate()
290 throw (uno::RuntimeException)
291 {
292 // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
293 const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
294 OSL_TRACE ("Player::getRate() = %.3f", fRate);
295 return fRate;
296 }
297
298 // ------------------------------------------------------------------------------
299
setPlaybackLoop(sal_Bool bSet)300 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
301 throw (uno::RuntimeException)
302 {
303 OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
304 mbLooping = bSet;
305 }
306
307 // ------------------------------------------------------------------------------
308
isPlaybackLoop()309 sal_Bool SAL_CALL Player::isPlaybackLoop()
310 throw (uno::RuntimeException)
311 {
312 const bool bRet = mbLooping;
313 OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
314 return bRet;
315 }
316
317 // ------------------------------------------------------------------------------
318
setMute(sal_Bool bSet)319 void SAL_CALL Player::setMute( sal_Bool bSet )
320 throw (uno::RuntimeException)
321 {
322 OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
323
324 if( !mpPlayer )
325 return;
326
327 mbMuted = (bSet == TRUE);
328 [mpPlayer setMuted:mbMuted];
329 }
330
331 // ------------------------------------------------------------------------------
332
isMute()333 sal_Bool SAL_CALL Player::isMute()
334 throw (uno::RuntimeException)
335 {
336 OSL_TRACE ("Player::isMuted() = %d", mbMuted);
337 return mbMuted;
338 }
339
340 // ------------------------------------------------------------------------------
341
setVolumeDB(sal_Int16 nVolumeDB)342 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
343 throw (uno::RuntimeException)
344 {
345 // -40dB <-> AVPlayer volume 0.0
346 // 0dB <-> AVPlayer volume 1.0
347 mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
348 OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
349
350 // change volume
351 if( !mbMuted && mpPlayer )
352 [mpPlayer setVolume:mfUnmutedVolume];
353 }
354
355 // ------------------------------------------------------------------------------
356
getVolumeDB()357 sal_Int16 SAL_CALL Player::getVolumeDB()
358 throw (uno::RuntimeException)
359 {
360 if( !mpPlayer )
361 return 0;
362
363 // get the actual volume
364 const float fVolume = [mpPlayer volume];
365
366 // convert into Dezibel value
367 // -40dB <-> AVPlayer volume 0.0
368 // 0dB <-> AVPlayer volume 1.0
369 const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
370
371 return (sal_Int16)nVolumeDB;
372 }
373
374 // ------------------------------------------------------------------------------
375
getPreferredPlayerWindowSize()376 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
377 throw (uno::RuntimeException)
378 {
379 awt::Size aSize( 0, 0 ); // default size
380
381 AVAsset* pMovie = [[mpPlayer currentItem] asset];
382 NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
383 AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
384 if( pFirstVideoTrack )
385 {
386 const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
387 aSize = awt::Size( aPrefSize.width, aPrefSize.height );
388 }
389
390 return aSize;
391 }
392
393 // ------------------------------------------------------------------------------
394
createPlayerWindow(const uno::Sequence<uno::Any> & aArguments)395 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
396 throw (uno::RuntimeException)
397 {
398 // get the preferred window size
399 const awt::Size aSize( getPreferredPlayerWindowSize() );
400 OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
401
402 // get the parent view
403 sal_IntPtr nNSViewPtr = NULL;
404 aArguments[0] >>= nNSViewPtr;
405 NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
406
407 // check the window parameters
408 uno::Reference< ::media::XPlayerWindow > xRet;
409 if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
410 return xRet;
411
412 // create the window
413 ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView );
414 xRet = pWindow;
415 return xRet;
416 }
417
418 // ------------------------------------------------------------------------------
419
createFrameGrabber()420 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
421 throw (uno::RuntimeException)
422 {
423 uno::Reference< media::XFrameGrabber > xRet;
424 OSL_TRACE ("Player::createFrameGrabber");
425
426 FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
427 AVAsset* pMovie = [[mpPlayer currentItem] asset];
428 if( pGrabber->create( pMovie ) )
429 xRet = pGrabber;
430
431 return xRet;
432 }
433
434 // ------------------------------------------------------------------------------
435
getImplementationName()436 ::rtl::OUString SAL_CALL Player::getImplementationName( )
437 throw (uno::RuntimeException)
438 {
439 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
440 }
441
442 // ------------------------------------------------------------------------------
443
supportsService(const::rtl::OUString & ServiceName)444 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
445 throw (uno::RuntimeException)
446 {
447 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
448 }
449
450 // ------------------------------------------------------------------------------
451
getSupportedServiceNames()452 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames( )
453 throw (uno::RuntimeException)
454 {
455 uno::Sequence< ::rtl::OUString > aRet(1);
456 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
457
458 return aRet;
459 }
460
461 } // namespace macavf
462 } // namespace avmedia
463
464