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<uno::XComponentContext> & rxContext)87 Player::Player( const uno::Reference< uno::XComponentContext >& rxContext )
88 : mxContext( rxContext )
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 {
174 if( !mpPlayer )
175 return;
176 #if 0
177 const AVPlayerStatus eStatus = [mpPlayer status];
178 OSL_TRACE ("Player::start status=%d", (int)eStatus);
179 if( eStatus == AVPlayerStatusReadyToPlay)
180 #endif
181 [mpPlayer play];
182 // else // TODO: delay until it becomes ready
183 }
184
185 // ------------------------------------------------------------------------------
186
stop()187 void SAL_CALL Player::stop()
188 {
189 if( !mpPlayer )
190 return;
191 const bool bPlaying = isPlaying();
192 OSL_TRACE ("Player::stop() playing=%d", bPlaying);
193 if( bPlaying )
194 [mpPlayer pause];
195 }
196
197 // ------------------------------------------------------------------------------
198
isPlaying()199 sal_Bool SAL_CALL Player::isPlaying()
200 {
201 if( !mpPlayer )
202 return false;
203 const float fRate = [mpPlayer rate];
204 return (fRate != 0.0);
205 }
206
207 // ------------------------------------------------------------------------------
208
getDuration()209 double SAL_CALL Player::getDuration()
210 {
211 // slideshow checks for non-zero duration, so cheat here
212 double duration = 0.01;
213
214 if( mpPlayer )
215 {
216 AVPlayerItem* pItem = [mpPlayer currentItem];
217 if( [pItem status] == AVPlayerItemStatusReadyToPlay )
218 duration = CMTimeGetSeconds( [pItem duration] );
219 else // fall back to AVAsset's best guess
220 duration = CMTimeGetSeconds( [[pItem asset] duration] );
221 }
222
223 return duration;
224 }
225
226 // ------------------------------------------------------------------------------
227
setMediaTime(double fTime)228 void SAL_CALL Player::setMediaTime( double fTime )
229 {
230 OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
231 if( mpPlayer )
232 [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
233 }
234
235 // ------------------------------------------------------------------------------
236
getMediaTime()237 double SAL_CALL Player::getMediaTime()
238 {
239 if( !mpPlayer )
240 return 0.0;
241
242 const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
243 OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
244 if( position >= mfStopTime )
245 if( isPlaying() )
246 stop();
247
248 return position;
249 }
250
251 // ------------------------------------------------------------------------------
252
setStopTime(double fTime)253 void SAL_CALL Player::setStopTime( double fTime )
254 {
255 OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
256 mfStopTime = fTime;
257 }
258
259 // ------------------------------------------------------------------------------
260
getStopTime()261 double SAL_CALL Player::getStopTime()
262 {
263 return mfStopTime;
264 }
265
266 // ------------------------------------------------------------------------------
267
setRate(double fRate)268 void SAL_CALL Player::setRate( double fRate )
269 {
270 OSL_TRACE ("Player::setRate( %.3f)", fRate);
271 if( !mpPlayer )
272 return;
273
274 // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
275 [mpPlayer setRate: fRate];
276 }
277
278 // ------------------------------------------------------------------------------
279
getRate()280 double SAL_CALL Player::getRate()
281 {
282 // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
283 const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
284 OSL_TRACE ("Player::getRate() = %.3f", fRate);
285 return fRate;
286 }
287
288 // ------------------------------------------------------------------------------
289
setPlaybackLoop(sal_Bool bSet)290 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
291 {
292 OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
293 mbLooping = bSet;
294 }
295
296 // ------------------------------------------------------------------------------
297
isPlaybackLoop()298 sal_Bool SAL_CALL Player::isPlaybackLoop()
299 {
300 const bool bRet = mbLooping;
301 OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
302 return bRet;
303 }
304
305 // ------------------------------------------------------------------------------
306
setMute(sal_Bool bSet)307 void SAL_CALL Player::setMute( sal_Bool bSet )
308 {
309 OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
310
311 if( !mpPlayer )
312 return;
313
314 mbMuted = (bSet == TRUE);
315 [mpPlayer setMuted:mbMuted];
316 }
317
318 // ------------------------------------------------------------------------------
319
isMute()320 sal_Bool SAL_CALL Player::isMute()
321 {
322 OSL_TRACE ("Player::isMuted() = %d", mbMuted);
323 return mbMuted;
324 }
325
326 // ------------------------------------------------------------------------------
327
setVolumeDB(sal_Int16 nVolumeDB)328 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
329 {
330 // -40dB <-> AVPlayer volume 0.0
331 // 0dB <-> AVPlayer volume 1.0
332 mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
333 OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
334
335 // change volume
336 if( !mbMuted && mpPlayer )
337 [mpPlayer setVolume:mfUnmutedVolume];
338 }
339
340 // ------------------------------------------------------------------------------
341
getVolumeDB()342 sal_Int16 SAL_CALL Player::getVolumeDB()
343 {
344 if( !mpPlayer )
345 return 0;
346
347 // get the actual volume
348 const float fVolume = [mpPlayer volume];
349
350 // convert into Dezibel value
351 // -40dB <-> AVPlayer volume 0.0
352 // 0dB <-> AVPlayer volume 1.0
353 const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
354
355 return (sal_Int16)nVolumeDB;
356 }
357
358 // ------------------------------------------------------------------------------
359
getPreferredPlayerWindowSize()360 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
361 {
362 awt::Size aSize( 0, 0 ); // default size
363
364 AVAsset* pMovie = [[mpPlayer currentItem] asset];
365 NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
366 AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
367 if( pFirstVideoTrack )
368 {
369 const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
370 aSize = awt::Size( aPrefSize.width, aPrefSize.height );
371 }
372
373 return aSize;
374 }
375
376 // ------------------------------------------------------------------------------
377
createPlayerWindow(const uno::Sequence<uno::Any> & aArguments)378 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
379 {
380 // get the preferred window size
381 const awt::Size aSize( getPreferredPlayerWindowSize() );
382 OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
383
384 // get the parent view
385 sal_IntPtr nNSViewPtr = NULL;
386 aArguments[0] >>= nNSViewPtr;
387 NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
388
389 // check the window parameters
390 uno::Reference< ::media::XPlayerWindow > xRet;
391 if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
392 return xRet;
393
394 // create the window
395 ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxContext, *this, pParentView );
396 xRet = pWindow;
397 return xRet;
398 }
399
400 // ------------------------------------------------------------------------------
401
createFrameGrabber()402 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
403 {
404 uno::Reference< media::XFrameGrabber > xRet;
405 OSL_TRACE ("Player::createFrameGrabber");
406
407 FrameGrabber* pGrabber = new FrameGrabber( mxContext );
408 AVAsset* pMovie = [[mpPlayer currentItem] asset];
409 if( pGrabber->create( pMovie ) )
410 xRet = pGrabber;
411
412 return xRet;
413 }
414
415 // ------------------------------------------------------------------------------
416
getImplementationName()417 ::rtl::OUString SAL_CALL Player::getImplementationName( )
418 {
419 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
420 }
421
422 // ------------------------------------------------------------------------------
423
supportsService(const::rtl::OUString & ServiceName)424 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
425 {
426 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
427 }
428
429 // ------------------------------------------------------------------------------
430
getSupportedServiceNames()431 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames( )
432 {
433 uno::Sequence< ::rtl::OUString > aRet(1);
434 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
435
436 return aRet;
437 }
438
439 } // namespace macavf
440 } // namespace avmedia
441