47 MMF::VideoPlayer::VideoPlayer() |
47 MMF::VideoPlayer::VideoPlayer() |
48 : m_wsSession(CCoeEnv::Static()->WsSession()) |
48 : m_wsSession(CCoeEnv::Static()->WsSession()) |
49 , m_screenDevice(*CCoeEnv::Static()->ScreenDevice()) |
49 , m_screenDevice(*CCoeEnv::Static()->ScreenDevice()) |
50 , m_window(0) |
50 , m_window(0) |
51 , m_totalTime(0) |
51 , m_totalTime(0) |
52 , m_mmfOutputChangePending(false) |
52 , m_pendingChanges(false) |
|
53 , m_dsaActive(false) |
|
54 , m_dsaWasActive(false) |
53 { |
55 { |
54 construct(); |
56 construct(); |
55 } |
57 } |
56 |
58 |
57 MMF::VideoPlayer::VideoPlayer(const AbstractPlayer& player) |
59 MMF::VideoPlayer::VideoPlayer(const AbstractPlayer& player) |
58 : AbstractMediaPlayer(player) |
60 : AbstractMediaPlayer(player) |
59 , m_wsSession(CCoeEnv::Static()->WsSession()) |
61 , m_wsSession(CCoeEnv::Static()->WsSession()) |
60 , m_screenDevice(*CCoeEnv::Static()->ScreenDevice()) |
62 , m_screenDevice(*CCoeEnv::Static()->ScreenDevice()) |
61 , m_window(0) |
63 , m_window(0) |
62 , m_totalTime(0) |
64 , m_totalTime(0) |
63 , m_mmfOutputChangePending(false) |
65 , m_pendingChanges(false) |
|
66 , m_dsaActive(false) |
64 { |
67 { |
65 construct(); |
68 construct(); |
66 } |
69 } |
67 |
70 |
68 void MMF::VideoPlayer::construct() |
71 void MMF::VideoPlayer::construct() |
69 { |
72 { |
70 TRACE_CONTEXT(VideoPlayer::VideoPlayer, EVideoApi); |
73 TRACE_CONTEXT(VideoPlayer::VideoPlayer, EVideoApi); |
71 TRACE_ENTRY_0(); |
74 TRACE_ENTRY_0(); |
72 |
75 |
73 if (m_videoOutput) |
76 getVideoWindow(); |
74 m_videoOutput->setObserver(this); |
|
75 |
77 |
76 const TInt priority = 0; |
78 const TInt priority = 0; |
77 const TMdaPriorityPreference preference = EMdaPriorityPreferenceNone; |
79 const TMdaPriorityPreference preference = EMdaPriorityPreferenceNone; |
78 |
|
79 // Ignore return value - first call must always return true |
|
80 getNativeWindowSystemHandles(); |
|
81 |
|
82 // TODO: is this the correct way to handle errors which occur when |
|
83 // creating a Symbian object in the constructor of a Qt object? |
|
84 |
|
85 // TODO: check whether videoOutput is visible? If not, then the |
|
86 // corresponding window will not be active, meaning that the |
|
87 // clipping region will be set to empty and the video will not be |
|
88 // visible. If this is the case, we should set m_mmfOutputChangePending |
|
89 // and respond to future showEvents from the videoOutput widget. |
|
90 |
80 |
91 TRAPD(err, |
81 TRAPD(err, |
92 m_player.reset(CVideoPlayerUtility::NewL |
82 m_player.reset(CVideoPlayerUtility::NewL |
93 ( |
83 ( |
94 *this, |
84 *this, |
95 priority, preference, |
85 priority, preference, |
96 m_wsSession, m_screenDevice, |
86 m_wsSession, m_screenDevice, |
97 *m_window, |
87 *m_window, |
98 m_rect, m_rect |
88 m_videoRect, m_videoRect |
99 )) |
89 )) |
100 ); |
90 ); |
101 |
91 |
|
92 // CVideoPlayerUtility::NewL starts DSA |
|
93 m_dsaActive = true; |
|
94 |
102 if (KErrNone != err) |
95 if (KErrNone != err) |
103 changeState(ErrorState); |
96 changeState(ErrorState); |
104 |
97 |
105 TRACE_EXIT_0(); |
98 TRACE_EXIT_0(); |
106 } |
99 } |
317 TRACE_EXIT_0(); |
306 TRACE_EXIT_0(); |
318 } |
307 } |
319 |
308 |
320 |
309 |
321 //----------------------------------------------------------------------------- |
310 //----------------------------------------------------------------------------- |
322 // VideoOutputObserver |
311 // Video window updates |
323 //----------------------------------------------------------------------------- |
312 //----------------------------------------------------------------------------- |
324 |
313 |
325 void MMF::VideoPlayer::videoOutputRegionChanged() |
314 void MMF::VideoPlayer::getVideoWindow() |
|
315 { |
|
316 TRACE_CONTEXT(VideoPlayer::getVideoWindow, EVideoInternal); |
|
317 TRACE_ENTRY_0(); |
|
318 |
|
319 if(m_videoOutput) { |
|
320 // Dump information to log, only in debug builds |
|
321 m_videoOutput->dump(); |
|
322 |
|
323 initVideoOutput(); |
|
324 videoWindowChanged(); |
|
325 } else |
|
326 // Top-level window |
|
327 m_window = QApplication::activeWindow()->effectiveWinId()->DrawableWindow(); |
|
328 |
|
329 TRACE_EXIT_0(); |
|
330 } |
|
331 |
|
332 void MMF::VideoPlayer::videoOutputChanged() |
|
333 { |
|
334 TRACE_CONTEXT(VideoPlayer::videoOutputChanged, EVideoInternal); |
|
335 TRACE_ENTRY_0(); |
|
336 |
|
337 if (m_videoOutput) { |
|
338 initVideoOutput(); |
|
339 videoWindowChanged(); |
|
340 } |
|
341 |
|
342 TRACE_EXIT_0(); |
|
343 } |
|
344 |
|
345 void MMF::VideoPlayer::initVideoOutput() |
|
346 { |
|
347 m_videoOutput->winId(); |
|
348 m_videoOutput->setVideoSize(m_videoFrameSize); |
|
349 |
|
350 bool connected = connect( |
|
351 m_videoOutput, SIGNAL(videoWindowChanged()), |
|
352 this, SLOT(videoWindowChanged()) |
|
353 ); |
|
354 Q_ASSERT(connected); |
|
355 |
|
356 connected = connect( |
|
357 m_videoOutput, SIGNAL(beginVideoWindowNativePaint()), |
|
358 this, SLOT(suspendDirectScreenAccess()) |
|
359 ); |
|
360 Q_ASSERT(connected); |
|
361 |
|
362 connected = connect( |
|
363 m_videoOutput, SIGNAL(endVideoWindowNativePaint()), |
|
364 this, SLOT(resumeDirectScreenAccess()) |
|
365 ); |
|
366 Q_ASSERT(connected); |
|
367 |
|
368 connected = connect( |
|
369 m_videoOutput, SIGNAL(aspectRatioChanged()), |
|
370 this, SLOT(aspectRatioChanged()) |
|
371 ); |
|
372 Q_ASSERT(connected); |
|
373 |
|
374 connected = connect( |
|
375 m_videoOutput, SIGNAL(scaleModeChanged()), |
|
376 this, SLOT(scaleModeChanged()) |
|
377 ); |
|
378 Q_ASSERT(connected); |
|
379 |
|
380 // Suppress warnings in release builds |
|
381 Q_UNUSED(connected); |
|
382 } |
|
383 |
|
384 void MMF::VideoPlayer::videoWindowChanged() |
326 { |
385 { |
327 TRACE_CONTEXT(VideoPlayer::videoOutputRegionChanged, EVideoInternal); |
386 TRACE_CONTEXT(VideoPlayer::videoOutputRegionChanged, EVideoInternal); |
328 TRACE_ENTRY("state %d", state()); |
387 TRACE_ENTRY("state %d", state()); |
329 |
388 |
330 const bool changed = getNativeWindowSystemHandles(); |
389 m_window = m_videoOutput->videoWindow(); |
331 |
390 updateVideoRect(); |
332 // See comment in updateMmfOutput |
391 |
333 if (changed) { |
392 TRACE_EXIT_0(); |
334 if (state() == LoadingState) |
393 } |
335 m_mmfOutputChangePending = true; |
394 |
|
395 void MMF::VideoPlayer::suspendDirectScreenAccess() |
|
396 { |
|
397 m_dsaWasActive = stopDirectScreenAccess(); |
|
398 } |
|
399 |
|
400 void MMF::VideoPlayer::resumeDirectScreenAccess() |
|
401 { |
|
402 if(m_dsaWasActive) { |
|
403 startDirectScreenAccess(); |
|
404 m_dsaWasActive = false; |
|
405 } |
|
406 } |
|
407 |
|
408 void MMF::VideoPlayer::startDirectScreenAccess() |
|
409 { |
|
410 if(!m_dsaActive) { |
|
411 TRAPD(err, m_player->StartDirectScreenAccessL()); |
|
412 if(KErrNone == err) |
|
413 m_dsaActive = true; |
336 else |
414 else |
337 updateMmfOutput(); |
415 setError(NormalError); |
338 } |
416 } |
339 |
417 } |
340 TRACE_EXIT_0(); |
418 |
341 } |
419 bool MMF::VideoPlayer::stopDirectScreenAccess() |
342 |
420 { |
|
421 const bool dsaWasActive = m_dsaActive; |
|
422 if(m_dsaActive) { |
|
423 TRAPD(err, m_player->StopDirectScreenAccessL()); |
|
424 if(KErrNone == err) |
|
425 m_dsaActive = false; |
|
426 else |
|
427 setError(NormalError); |
|
428 } |
|
429 return dsaWasActive; |
|
430 } |
|
431 |
|
432 // Helper function for aspect ratio / scale mode handling |
|
433 QSize scaleToAspect(const QSize& srcRect, int aspectWidth, int aspectHeight) |
|
434 { |
|
435 const qreal aspectRatio = qreal(aspectWidth) / aspectHeight; |
|
436 |
|
437 int width = srcRect.width(); |
|
438 int height = srcRect.width() / aspectRatio; |
|
439 if (height > srcRect.height()){ |
|
440 height = srcRect.height(); |
|
441 width = srcRect.height() * aspectRatio; |
|
442 } |
|
443 return QSize(width, height); |
|
444 } |
|
445 |
|
446 void MMF::VideoPlayer::updateVideoRect() |
|
447 { |
|
448 QRect videoRect; |
|
449 QRect windowRect = m_videoOutput->videoWindowRect(); |
|
450 |
|
451 // Clip to physical window size |
|
452 // This is due to a defect in the layout when running on S60 3.2, which |
|
453 // results in the rectangle of the video widget extending outside the |
|
454 // screen in certain circumstances. These include the initial startup |
|
455 // of the mediaplayer demo in portrait mode. When this rectangle is |
|
456 // passed to the CVideoPlayerUtility, no video is rendered. |
|
457 const TSize screenSize = m_screenDevice.SizeInPixels(); |
|
458 const QRect screenRect(0, 0, screenSize.iWidth, screenSize.iHeight); |
|
459 windowRect = windowRect.intersected(screenRect); |
|
460 |
|
461 const QSize windowSize = windowRect.size(); |
|
462 |
|
463 // Calculate size of smallest rect which contains video frame size |
|
464 // and conforms to aspect ratio |
|
465 switch (m_videoOutput->aspectRatio()) { |
|
466 case Phonon::VideoWidget::AspectRatioAuto: |
|
467 videoRect.setSize(m_videoFrameSize); |
|
468 break; |
|
469 |
|
470 case Phonon::VideoWidget::AspectRatioWidget: |
|
471 videoRect.setSize(windowSize); |
|
472 break; |
|
473 |
|
474 case Phonon::VideoWidget::AspectRatio4_3: |
|
475 videoRect.setSize(scaleToAspect(m_videoFrameSize, 4, 3)); |
|
476 break; |
|
477 |
|
478 case Phonon::VideoWidget::AspectRatio16_9: |
|
479 videoRect.setSize(scaleToAspect(m_videoFrameSize, 16, 9)); |
|
480 break; |
|
481 } |
|
482 |
|
483 // Scale to fill the window width |
|
484 const int windowWidth = windowSize.width(); |
|
485 const int windowHeight = windowSize.height(); |
|
486 const qreal windowScaleFactor = qreal(windowWidth) / videoRect.width(); |
|
487 int videoWidth = windowWidth; |
|
488 int videoHeight = videoRect.height() * windowScaleFactor; |
|
489 |
|
490 const qreal windowToVideoHeightRatio = qreal(windowHeight) / videoHeight; |
|
491 |
|
492 switch(m_videoOutput->scaleMode()) { |
|
493 case Phonon::VideoWidget::ScaleAndCrop: |
|
494 if(videoHeight < windowHeight) { |
|
495 videoWidth *= windowToVideoHeightRatio; |
|
496 videoHeight = windowHeight; |
|
497 } |
|
498 break; |
|
499 case Phonon::VideoWidget::FitInView: |
|
500 default: |
|
501 if(videoHeight > windowHeight) { |
|
502 videoWidth *= windowToVideoHeightRatio; |
|
503 videoHeight = windowHeight; |
|
504 } |
|
505 break; |
|
506 } |
|
507 |
|
508 // Calculate scale factors |
|
509 m_scaleWidth = 100.0f * videoWidth / m_videoFrameSize.width(); |
|
510 m_scaleHeight = 100.0f * videoHeight / m_videoFrameSize.height(); |
|
511 |
|
512 m_videoRect = qt_QRect2TRect(windowRect); |
|
513 |
|
514 if (state() == LoadingState) |
|
515 m_pendingChanges = true; |
|
516 else { |
|
517 applyVideoWindowChange(); |
|
518 m_pendingChanges = false; |
|
519 } |
|
520 } |
|
521 |
|
522 void MMF::VideoPlayer::aspectRatioChanged() |
|
523 { |
|
524 TRACE_CONTEXT(VideoPlayer::aspectRatioChanged, EVideoInternal); |
|
525 TRACE_ENTRY("state %d aspectRatio %d", state()); |
|
526 |
|
527 updateVideoRect(); |
|
528 |
|
529 TRACE_EXIT_0(); |
|
530 } |
|
531 |
|
532 void MMF::VideoPlayer::scaleModeChanged() |
|
533 { |
|
534 TRACE_CONTEXT(VideoPlayer::scaleModeChanged, EVideoInternal); |
|
535 TRACE_ENTRY("state %d", state()); |
|
536 |
|
537 updateVideoRect(); |
|
538 |
|
539 TRACE_EXIT_0(); |
|
540 } |
343 |
541 |
344 #ifndef QT_NO_DEBUG |
542 #ifndef QT_NO_DEBUG |
345 |
543 |
346 // The following code is for debugging problems related to video visibility. It allows |
544 // The following code is for debugging problems related to video visibility. It allows |
347 // the VideoPlayer instance to query the window server in order to determine the |
545 // the VideoPlayer instance to query the window server in order to determine the |
378 } |
576 } |
379 } |
577 } |
380 |
578 |
381 #endif // _DEBUG |
579 #endif // _DEBUG |
382 |
580 |
383 void MMF::VideoPlayer::updateMmfOutput() |
581 void MMF::VideoPlayer::applyPendingChanges() |
384 { |
582 { |
385 TRACE_CONTEXT(VideoPlayer::updateMmfOutput, EVideoInternal); |
583 if(m_pendingChanges) |
|
584 applyVideoWindowChange(); |
|
585 |
|
586 m_pendingChanges = false; |
|
587 } |
|
588 |
|
589 void MMF::VideoPlayer::applyVideoWindowChange() |
|
590 { |
|
591 TRACE_CONTEXT(VideoPlayer::applyVideoWindowChange, EVideoInternal); |
386 TRACE_ENTRY_0(); |
592 TRACE_ENTRY_0(); |
387 |
|
388 // Calling SetDisplayWindowL is a no-op unless the MMF controller has |
|
389 // been loaded, so we shouldn't do it. Instead, the |
|
390 // m_mmfOutputChangePending flag is used to record the fact that we |
|
391 // need to call SetDisplayWindowL, and this is checked in |
|
392 // MvpuoPrepareComplete, at which point the MMF controller has been |
|
393 // loaded. |
|
394 |
593 |
395 #ifndef QT_NO_DEBUG |
594 #ifndef QT_NO_DEBUG |
396 getDsaRegion(m_wsSession, *m_window); |
595 getDsaRegion(m_wsSession, *m_window); |
397 #endif |
596 #endif |
398 |
597 |
399 TRAPD(err, |
598 static const TBool antialias = ETrue; |
400 m_player->SetDisplayWindowL |
599 |
401 ( |
600 TRAPD(err, m_player->SetScaleFactorL(m_scaleWidth, m_scaleHeight, antialias)); |
402 m_wsSession, m_screenDevice, |
601 if(KErrNone != err) { |
403 *m_window, |
602 TRACE("SetScaleFactorL (1) err %d", err); |
404 m_rect, m_rect |
|
405 ) |
|
406 ); |
|
407 |
|
408 if (KErrNone != err) { |
|
409 TRACE("SetDisplayWindowL error %d", err); |
|
410 setError(NormalError); |
603 setError(NormalError); |
411 } |
604 } |
412 |
605 |
413 m_mmfOutputChangePending = false; |
606 if(KErrNone == err) { |
414 |
607 TRAP(err, |
415 TRACE_EXIT_0(); |
608 m_player->SetDisplayWindowL |
416 } |
609 ( |
417 |
610 m_wsSession, m_screenDevice, |
418 |
611 *m_window, |
419 //----------------------------------------------------------------------------- |
612 m_videoRect, m_videoRect |
420 // Private functions |
613 ) |
421 //----------------------------------------------------------------------------- |
614 ); |
422 |
615 |
423 void MMF::VideoPlayer::videoOutputChanged() |
616 if (KErrNone != err) { |
424 { |
617 TRACE("SetDisplayWindowL err %d", err); |
425 TRACE_CONTEXT(VideoPlayer::videoOutputChanged, EVideoInternal); |
618 setError(NormalError); |
426 TRACE_ENTRY_0(); |
619 } else { |
427 |
620 m_dsaActive = true; |
428 if (m_videoOutput) { |
621 TRAP(err, m_player->SetScaleFactorL(m_scaleWidth, m_scaleHeight, antialias)); |
429 m_videoOutput->setObserver(this); |
622 if(KErrNone != err) { |
430 m_videoOutput->setFrameSize(m_frameSize); |
623 TRACE("SetScaleFactorL (2) err %d", err); |
431 } |
624 setError(NormalError); |
432 |
625 } |
433 videoOutputRegionChanged(); |
626 } |
434 |
627 } |
435 TRACE_EXIT_0(); |
628 |
436 } |
629 TRACE_EXIT_0(); |
437 |
630 } |
438 bool MMF::VideoPlayer::getNativeWindowSystemHandles() |
631 |
439 { |
632 |
440 TRACE_CONTEXT(VideoPlayer::getNativeWindowSystemHandles, EVideoInternal); |
633 //----------------------------------------------------------------------------- |
441 TRACE_ENTRY_0(); |
634 // Metadata |
442 |
635 //----------------------------------------------------------------------------- |
443 CCoeControl *control = 0; |
636 |
444 |
637 int MMF::VideoPlayer::numberOfMetaDataEntries() const |
445 if (m_videoOutput) |
638 { |
446 // Create native window |
639 int numberOfEntries = 0; |
447 control = m_videoOutput->winId(); |
640 TRAP_IGNORE(numberOfEntries = m_player->NumberOfMetaDataEntriesL()); |
448 else |
641 return numberOfEntries; |
449 // Get top-level window |
642 } |
450 control = QApplication::activeWindow()->effectiveWinId(); |
643 |
451 |
644 QPair<QString, QString> MMF::VideoPlayer::metaDataEntry(int index) const |
452 #ifndef QT_NO_DEBUG |
645 { |
453 if (m_videoOutput) { |
646 CMMFMetaDataEntry *entry = 0; |
454 QScopedPointer<ObjectDump::QDumper> dumper(new ObjectDump::QDumper); |
647 QT_TRAP_THROWING(entry = m_player->MetaDataEntryL(index)); |
455 dumper->setPrefix("Phonon::MMF"); // to aid searchability of logs |
648 return QPair<QString, QString>(qt_TDesC2QString(entry->Name()), qt_TDesC2QString(entry->Value())); |
456 ObjectDump::addDefaultAnnotators(*dumper); |
649 } |
457 TRACE_0("Dumping VideoOutput:"); |
|
458 dumper->dumpObject(*m_videoOutput); |
|
459 } |
|
460 else { |
|
461 TRACE_0("m_videoOutput is null - dumping top-level control info:"); |
|
462 TRACE("control %08x", control); |
|
463 TRACE("control.parent %08x", control->Parent()); |
|
464 TRACE("control.isVisible %d", control->IsVisible()); |
|
465 TRACE("control.rect %d,%d %dx%d", |
|
466 control->Position().iX, control->Position().iY, |
|
467 control->Size().iWidth, control->Size().iHeight); |
|
468 TRACE("control.ownsWindow %d", control->OwnsWindow()); |
|
469 } |
|
470 #endif |
|
471 |
|
472 RWindowBase *const window = control->DrawableWindow(); |
|
473 const TRect rect(window->AbsPosition(), window->Size()); |
|
474 |
|
475 TRACE("rect %d %d - %d %d", |
|
476 rect.iTl.iX, rect.iTl.iY, |
|
477 rect.iBr.iX, rect.iBr.iY); |
|
478 |
|
479 bool changed = false; |
|
480 |
|
481 if (window != m_window || rect != m_rect) { |
|
482 m_window = window; |
|
483 m_rect = rect; |
|
484 changed = true; |
|
485 } |
|
486 |
|
487 TRACE_RETURN("changed %d", changed); |
|
488 } |
|
489 |
|
490 |
650 |
491 QT_END_NAMESPACE |
651 QT_END_NAMESPACE |
492 |
652 |