avmedia/Library_avmediawin.mk | 8 avmedia/source/win/framegrabber.cxx | 211 ++++--- avmedia/source/win/framegrabber.hxx | 8 avmedia/source/win/player.cxx | 634 +++++++++++++++------- avmedia/source/win/player.hxx | 89 ++- avmedia/source/win/wincommon.hxx | 2 avmedia/source/win/window.cxx | 96 +-- avmedia/source/win/window.hxx | 5 slideshow/source/engine/shapes/viewmediashape.cxx | 8 svx/uiconfig/ui/medialine.ui | 11 svx/uiconfig/ui/mediawindow.ui | 10 11 files changed, 732 insertions(+), 350 deletions(-)
New commits: commit 65d72f816afc92a062cf6ad14c1bb4fb0a5829df Author: Balazs Varga <[email protected]> AuthorDate: Fri Jul 4 12:54:04 2025 +0200 Commit: Balazs Varga <[email protected]> CommitDate: Wed Jul 23 20:34:23 2025 +0200 tdf#62408 tdf#159292 Impress: Introduce Microsoft Media Foundation APIs on Windows to support playback of common codecs. Managing video playback with MFPlay: https://learn.microsoft.com/en-us/windows/win32/medfound/getting-started-with-mfplay Using MFCreateSourceReaderFromURL for getting media informations. Supported video/audio file formats (without any additional codec) should be the same as in PowerPoint: https://support.microsoft.com/en-us/office/video-and-audio-file-formats-supported-in-powerpoint-d8b12450-26db-4c7b-a5c1-593d3418fb59 Change-Id: I6a32f3619b6985753122a17b863e09b3a12ecc0e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187384 Tested-by: Jenkins Reviewed-by: Balazs Varga <[email protected]> diff --git a/avmedia/Library_avmediawin.mk b/avmedia/Library_avmediawin.mk index 318e28265530..b34c0eda7f51 100644 --- a/avmedia/Library_avmediawin.mk +++ b/avmedia/Library_avmediawin.mk @@ -28,6 +28,7 @@ $(eval $(call gb_Library_use_libraries,avmediawin,\ tl \ utl \ vcl \ + avmedia \ )) $(eval $(call gb_Library_use_system_win32_libs,avmediawin,\ @@ -35,6 +36,13 @@ $(eval $(call gb_Library_use_system_win32_libs,avmediawin,\ ole32 \ oleaut32 \ strmiids \ + propsys \ + shlwapi \ + mf \ + mfuuid \ + mfplat \ + mfplay \ + mfreadwrite \ )) $(eval $(call gb_Library_add_exception_objects,avmediawin,\ diff --git a/avmedia/source/win/framegrabber.cxx b/avmedia/source/win/framegrabber.cxx index 84e9d1b187e8..62b771c076e4 100644 --- a/avmedia/source/win/framegrabber.cxx +++ b/avmedia/source/win/framegrabber.cxx @@ -21,14 +21,16 @@ #include <memory> -#include <prewin.h> -#include <postwin.h> #include <objbase.h> #include <strmif.h> -#include <Amvideo.h> #include "interface.hxx" #include <uuids.h> +// Media Foundation headers +#include <mfapi.h> +#include <mfidl.h> +#include <mfreadwrite.h> + #include "framegrabber.hxx" #include "player.hxx" @@ -37,147 +39,166 @@ #include <tools/stream.hxx> #include <vcl/graph.hxx> #include <vcl/dibtools.hxx> +#include <vcl/BitmapTools.hxx> #include <o3tl/char16_t2wchar_t.hxx> #include <systools/win32/oleauto.hxx> constexpr OUStringLiteral AVMEDIA_WIN_FRAMEGRABBER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.FrameGrabber_DirectX"; constexpr OUString AVMEDIA_WIN_FRAMEGRABBER_SERVICENAME = u"com.sun.star.media.FrameGrabber_DirectX"_ustr; +constexpr LONGLONG SEEK_TOLERANCE = 10000000; +constexpr LONGLONG MAX_FRAMES_TO_SKIP = 10; using namespace ::com::sun::star; namespace avmedia::win { -FrameGrabber::FrameGrabber() +FrameGrabber::FrameGrabber( const OUString& rURL, UINT32 nFrameWidth, UINT32 nFrameHeight ) : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false, sal::systools::CoInitializeGuard::WhenFailed::NoThrow) { + maURL = rURL; + mnFrameWidth = nFrameWidth; + mnFrameHeight = nFrameHeight; } - FrameGrabber::~FrameGrabber() = default; -namespace { - -sal::systools::COMReference<IMediaDet> implCreateMediaDet( const OUString& rURL ) +uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime ) { - sal::systools::COMReference<IMediaDet> pDet; + uno::Reference< graphic::XGraphic > xRet; - if( SUCCEEDED(pDet.CoCreateInstance(CLSID_MediaDet, nullptr, CLSCTX_INPROC_SERVER)) ) + if (mnFrameWidth && mnFrameHeight && + SUCCEEDED(MFStartup(MF_VERSION))) { - OUString aLocalStr; - - if( osl::FileBase::getSystemPathFromFileURL( rURL, aLocalStr ) - == osl::FileBase::E_None ) + HRESULT hr = S_OK; + IMFAttributes* pAttributes = nullptr; + + // Configure the source reader to perform video processing. + // + // This includes: + // - YUV to RGB-32 + // - Software deinterlace + if (SUCCEEDED(MFCreateAttributes(&pAttributes, 1))) { - if( !SUCCEEDED( pDet->put_Filename(sal::systools::BStr(aLocalStr)) ) ) - pDet.clear(); + hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE); } - } - - return pDet; -} - -} -bool FrameGrabber::create( const OUString& rURL ) -{ - // just check if a MediaDet interface can be created with the given URL - if (implCreateMediaDet(rURL)) - maURL = rURL; - else - maURL.clear(); + // Create the source reader from the URL. + IMFSourceReader* pReader = nullptr; // Create the source reader. + if (SUCCEEDED(hr) && SUCCEEDED(MFCreateSourceReaderFromURL(o3tl::toW(maURL.getStr()), pAttributes, &pReader))) + { + IMFMediaType* pType = nullptr; - return !maURL.isEmpty(); -} + // Configure the source reader to give us progressive RGB32 frames. + // The source reader will load the decoder if needed. + if (SUCCEEDED(MFCreateMediaType(&pType))) + hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); + if (SUCCEEDED(hr)) + hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32); -uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime ) -{ - uno::Reference< graphic::XGraphic > xRet; - if (sal::systools::COMReference<IMediaDet> pDet = implCreateMediaDet(maURL)) - { - double fLength; - long nStreamCount; - bool bFound = false; - - if( SUCCEEDED( pDet->get_OutputStreams( &nStreamCount ) ) ) - { - for( long n = 0; ( n < nStreamCount ) && !bFound; ++n ) + if (SUCCEEDED(hr)) { - GUID aMajorType; - - if( SUCCEEDED( pDet->put_CurrentStream( n ) ) && - SUCCEEDED( pDet->get_StreamType( &aMajorType ) ) && - ( aMajorType == MEDIATYPE_Video ) ) - { - bFound = true; - } + hr = pReader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + nullptr, + pType + ); } - } - - if( bFound && - ( S_OK == pDet->get_StreamLength( &fLength ) ) && - ( fLength > 0.0 ) && ( fMediaTime >= 0.0 ) && ( fMediaTime <= fLength ) ) - { - AM_MEDIA_TYPE aMediaType; - LONG nWidth = 0, nHeight = 0; - long nSize = 0; - if( SUCCEEDED( pDet->get_StreamMediaType( &aMediaType ) ) ) + // Ensure the stream is selected. + if (SUCCEEDED(hr)) { - if( ( aMediaType.formattype == FORMAT_VideoInfo ) && - ( aMediaType.cbFormat >= sizeof( VIDEOINFOHEADER ) ) ) - { - VIDEOINFOHEADER* pVih = reinterpret_cast< VIDEOINFOHEADER* >( aMediaType.pbFormat ); + hr = pReader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + TRUE + ); + } - nWidth = pVih->bmiHeader.biWidth; - nHeight = pVih->bmiHeader.biHeight; + SafeRelease(&pType); - if( nHeight < 0 ) - nHeight *= -1; - } + if (SUCCEEDED(hr) && fMediaTime > 0.0) + { + PROPVARIANT var; + PropVariantInit(&var); - if( aMediaType.cbFormat != 0 ) - { - ::CoTaskMemFree( aMediaType.pbFormat ); - aMediaType.cbFormat = 0; - aMediaType.pbFormat = nullptr; - } + var.vt = VT_I8; + var.hVal.QuadPart = fMediaTime; - if( aMediaType.pUnk != nullptr ) - { - aMediaType.pUnk->Release(); - aMediaType.pUnk = nullptr; - } + hr = pReader->SetCurrentPosition(GUID_NULL, var); } - if( ( nWidth > 0 ) && ( nHeight > 0 ) && - SUCCEEDED( pDet->GetBitmapBits( 0, &nSize, nullptr, nWidth, nHeight ) ) && - ( nSize > 0 ) ) - { - auto pBuffer = std::make_unique<char[]>(nSize); + IMFSample* pSampleTmp = nullptr; - try + if (SUCCEEDED(hr)) + { + DWORD dwFlags = 0; + DWORD cSkipped = 0; // Number of skipped frames + while (true) { - if( SUCCEEDED( pDet->GetBitmapBits( fMediaTime, nullptr, pBuffer.get(), nWidth, nHeight ) ) ) + hr = pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + 0, + nullptr, + &dwFlags, + nullptr, + &pSampleTmp + ); + + if (FAILED(hr) || (dwFlags & MF_SOURCE_READERF_ENDOFSTREAM)) + break; + + if (pSampleTmp == nullptr) + continue; + + LONGLONG hnsTimeStamp = 0; + if (SUCCEEDED(pSampleTmp->GetSampleTime(&hnsTimeStamp))) { - SvMemoryStream aMemStm( pBuffer.get(), nSize, StreamMode::READ | StreamMode::WRITE ); - Bitmap aBmp; + // Keep going until we get a frame that is within tolerance of the + // desired seek position, or until we skip MAX_FRAMES_TO_SKIP frames. - if( ReadDIB(aBmp, aMemStm, false ) && !aBmp.IsEmpty() ) + // During this process, we might reach the end of the file, so we + // always cache the last sample that we got (pSample). + + if ((cSkipped < MAX_FRAMES_TO_SKIP) && + (hnsTimeStamp + SEEK_TOLERANCE < fMediaTime)) { - BitmapEx aBitmapEx(aBmp); - Graphic aGraphic(aBitmapEx); - xRet = aGraphic.GetXGraphic(); + SafeRelease(&pSampleTmp); + + ++cSkipped; + continue; } } + + fMediaTime = hnsTimeStamp; + break; } - catch( ... ) + } + + // We got a sample. Hold onto it. + IMFMediaBuffer* pBuffer = nullptr; + BYTE* pBitmapData = nullptr; // Bitmap data + DWORD cbBitmapData = 0; // Size of data, in bytes + if (pSampleTmp && SUCCEEDED(pSampleTmp->ConvertToContiguousBuffer(&pBuffer))) + { + if (SUCCEEDED(pBuffer->Lock(&pBitmapData, nullptr, &cbBitmapData)) && cbBitmapData) { + BitmapEx aBitmapEx = vcl::bitmap::CreateFromData(pBitmapData, mnFrameWidth, mnFrameHeight, mnFrameWidth * 4, /*nBitsPerPixel*/32, true); + Graphic aGraphic(aBitmapEx); + xRet = aGraphic.GetXGraphic(); } + pBuffer->Unlock(); } + + SafeRelease(&pBuffer); + SafeRelease(&pSampleTmp); } + + SafeRelease(&pAttributes); + SafeRelease(&pReader); + // Shut down Media Foundation. + MFShutdown(); } return xRet; diff --git a/avmedia/source/win/framegrabber.hxx b/avmedia/source/win/framegrabber.hxx index d1ca48e84230..3971d3f98e78 100644 --- a/avmedia/source/win/framegrabber.hxx +++ b/avmedia/source/win/framegrabber.hxx @@ -34,11 +34,9 @@ class FrameGrabber : public ::cppu::WeakImplHelper< css::media::XFrameGrabber, public sal::systools::CoInitializeGuard { public: - explicit FrameGrabber(); + explicit FrameGrabber( const OUString& rURL, UINT32 nFrameWidth, UINT32 nFrameHeight ); ~FrameGrabber() override; - bool create( const OUString& rURL ); - // XFrameGrabber virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override; @@ -48,7 +46,9 @@ public: virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; private: - OUString maURL; + OUString maURL; + UINT32 mnFrameWidth; + UINT32 mnFrameHeight; }; } // namespace avmedia::win diff --git a/avmedia/source/win/player.cxx b/avmedia/source/win/player.cxx index b52ac8171dfc..39330ab70829 100644 --- a/avmedia/source/win/player.cxx +++ b/avmedia/source/win/player.cxx @@ -18,10 +18,17 @@ */ #include <objbase.h> -#include <strmif.h> -#include <control.h> #include <uuids.h> #include <evcode.h> +#include <propvarutil.h> +#include <propkey.h> +// Media Foundation headers +#include <mfapi.h> +#include <mfidl.h> +#include <mfreadwrite.h> + +#include <avmedia/mediaitem.hxx> +#include <avmedia/mediawindow.hxx> #include "player.hxx" #include "framegrabber.hxx" @@ -33,255 +40,510 @@ constexpr OUStringLiteral AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Player_DirectX"; constexpr OUString AVMEDIA_WIN_PLAYER_SERVICENAME = u"com.sun.star.media.Player_DirectX"_ustr; +constexpr float AVMEDIA_DB_RANGE = 40.0; using namespace ::com::sun::star; namespace avmedia::win { -static LRESULT CALLBACK MediaPlayerWndProc_2( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 ) +static LRESULT CALLBACK MediaPlayerWndProc_2( HWND /*hWnd*/, UINT /*nMsg*/, WPARAM /*nPar1*/, LPARAM /*nPar2*/ ) { - Player* pPlayer = reinterpret_cast<Player*>(::GetWindowLongPtrW( hWnd, 0 )); - bool bProcessed = true; - - if( pPlayer ) - { - switch( nMsg ) - { - case WM_GRAPHNOTIFY: - pPlayer->processEvent(); - break; - default: - bProcessed = false; - break; - } - } - else - bProcessed = false; - - return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ) ); + return 0; } - Player::Player() : Player_BASE(m_aMutex), sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false, sal::systools::CoInitializeGuard::WhenFailed::NoThrow), - mnUnmutedVolume( 0 ), + m_cRef( 1 ), + mnUnmutedVolume( 1 ), mnFrameWnd( nullptr ), mbMuted( false ), mbLooping( false ), - mbAddWindow( true ) + mbAutoPlayBack( false ), + mnFrameWidth( 0 ), + mnFrameHeight( 0 ), + g_pPlayer( nullptr ), + g_bHasVideo( false ), + g_bHasAudio( false ), + m_state( Closed ) { } - Player::~Player() { if( mnFrameWnd ) ::DestroyWindow( mnFrameWnd ); + + if (g_pPlayer) + g_pPlayer->Shutdown(); + SafeRelease(&g_pPlayer); } +//***************************** IUnknown methods *****************************// -void SAL_CALL Player::disposing() + +//------------------------------------------------------------------------------ +// AddRef +//------------------------------------------------------------------------------ + +ULONG Player::AddRef() { - ::osl::MutexGuard aGuard(m_aMutex); - stop(); - if( mpME ) - mpME->SetNotifyWindow( 0, WM_GRAPHNOTIFY, 0); + return InterlockedIncrement(&m_cRef); } +//------------------------------------------------------------------------------ +// Release +//------------------------------------------------------------------------------ -bool Player::create( const OUString& rURL ) +ULONG Player::Release() { - bool bRet = false; + ULONG uCount = InterlockedDecrement(&m_cRef); + if (uCount == 0) + { + delete this; + } + return uCount; +} - if( SUCCEEDED(mpGB.CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER)) ) +//------------------------------------------------------------------------------ +// QueryInterface +//------------------------------------------------------------------------------ + +STDMETHODIMP Player::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = { - // Don't use the overlay mixer on Windows Vista - // It disables the desktop composition as soon as RenderFile is called - // also causes some other problems: video rendering is not reliable - - // tdf#128057: IGraphBuilder::RenderFile seems to fail to handle file URIs properly when - // they contain encoded characters like "%23"; so pass system path in that case instead. - OUString aFile(rURL); - if (aFile.startsWithIgnoreAsciiCase("file:")) - osl::FileBase::getSystemPathFromFileURL(rURL, aFile); - - if( SUCCEEDED( mpGB->RenderFile( o3tl::toW(aFile.getStr()), nullptr ) ) && - mpMC.set(mpGB, sal::systools::COM_QUERY) && - mpME.set(mpGB, sal::systools::COM_QUERY) && - mpMP.set(mpGB, sal::systools::COM_QUERY) ) - { - // Video interfaces - mpVW.set(mpGB, sal::systools::COM_QUERY); - mpBV.set(mpGB, sal::systools::COM_QUERY); + QITABENT(Player, IMFPMediaPlayerCallback), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} - // Audio interface - mpBA.set(mpGB, sal::systools::COM_QUERY); +//********************* IMFPMediaPlayerCallback methods **********************// - if( mpBA ) - mpBA->put_Volume( mnUnmutedVolume ); +//----------------------------------------------------------------------------- +// OnMediaPlayerEvent +// +// Notifies the object of an MFPlay event. +//----------------------------------------------------------------------------- - bRet = true; - } +void Player::OnMediaPlayerEvent(MFP_EVENT_HEADER* pEventHeader) +{ + if (FAILED(pEventHeader->hrEvent)) + { + SAL_WARN("avmedia.win", + "Player::OnMediaPlayerEvent failed with error code: " << pEventHeader->hrEvent); + + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + + return; + } + + switch (pEventHeader->eEventType) + { + case MFP_EVENT_TYPE_MEDIAITEM_CREATED: + OnMediaItemCreated(MFP_GET_MEDIAITEM_CREATED_EVENT(pEventHeader)); + break; + + case MFP_EVENT_TYPE_MEDIAITEM_SET: + OnMediaItemSet(MFP_GET_MEDIAITEM_SET_EVENT(pEventHeader)); + break; + + case MFP_EVENT_TYPE_POSITION_SET: + OnMediaPosSet(MFP_GET_POSITION_SET_EVENT(pEventHeader)); + break; + + case MFP_EVENT_TYPE_PLAYBACK_ENDED: + OnMediaItemEnded(MFP_GET_PLAYBACK_ENDED_EVENT(pEventHeader)); + break; } +} + +void SAL_CALL Player::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + OnClose(mnFrameWnd); +} - if( bRet ) +bool Player::create( const OUString& rURL ) +{ + bool bRet = !rURL.isEmpty(); + if (bRet) + { maURL = rURL; + // Initialize the Media Foundation platform. + HRESULT hr = MFStartup(MF_VERSION); + if (SUCCEEDED(hr)) + { + // Create the source reader. + IMFSourceReader* pReader; + hr = MFCreateSourceReaderFromURL(o3tl::toW(maURL.getStr()), nullptr, &pReader); + if (SUCCEEDED(hr)) + { + IMFMediaType* pType = nullptr; + DWORD dwMediaTypeIndex = 0; + if (SUCCEEDED(pReader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, dwMediaTypeIndex, &pType))) + { + MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &mnFrameWidth, &mnFrameHeight); + SafeRelease(&pType); + } + SafeRelease(&pReader); + } + // Shut down Media Foundation. + MFShutdown(); + } + bRet = SUCCEEDED(hr); + } else maURL.clear(); return bRet; } +void Player::setNotifyWnd( HWND nNotifyWnd ) +{ + mnFrameWnd = nNotifyWnd; +} -const IVideoWindow* Player::getVideoWindow() const +//------------------------------------------------------------------- +// OnClose +// +// Handles the WM_CLOSE message. +//------------------------------------------------------------------- +void Player::OnClose(HWND /*hwnd*/) { - return mpVW; + if (g_pPlayer) + g_pPlayer->Shutdown(); + SafeRelease(&g_pPlayer); + + PostQuitMessage(0); } +//------------------------------------------------------------------- +// OnPaint +// +// Handles the WM_PAINT message. +//------------------------------------------------------------------- +void Player::OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + HDC hdc = 0; -void Player::setNotifyWnd( HWND nNotifyWnd ) + hdc = BeginPaint(hwnd, &ps); + + if (g_pPlayer && g_bHasVideo) + { + // Playback has started and there is video. + + // Do not draw the window background, because the video + // frame fills the entire client area. + + g_pPlayer->UpdateVideo(); + } + else + { + // There is no video stream, or playback has not started. + // Paint the entire client area. + + FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1)); + } + + EndPaint(hwnd, &ps); +} + +//------------------------------------------------------------------- +// OnSize +// +// Handles the WM_SIZE message. +//------------------------------------------------------------------- +void Player::OnSize(HWND /*hwnd*/, UINT state, int /*cx*/, int /*cy*/) +{ + if (state == SIZE_RESTORED) + { + if (g_pPlayer) + { + // Resize the video. + g_pPlayer->UpdateVideo(); + } + } +} + +//------------------------------------------------------------------- +// OnMediaItemCreated +// +// Called when the IMFPMediaPlayer::CreateMediaItemFromURL method +// completes. +//------------------------------------------------------------------- +void Player::OnMediaItemCreated(MFP_MEDIAITEM_CREATED_EVENT* pEvent) +{ + if (g_pPlayer) + { + BOOL bHasVideoOrAudio = FALSE, bIsSelected = FALSE; + + // Check if the media item contains video or audio. + HRESULT hr = pEvent->pMediaItem->HasVideo(&bHasVideoOrAudio, &bIsSelected); + if (SUCCEEDED(hr)) + g_bHasVideo = bHasVideoOrAudio && bIsSelected; + + hr = pEvent->pMediaItem->HasAudio(&bHasVideoOrAudio, &bIsSelected); + if (SUCCEEDED(hr)) + g_bHasAudio = bHasVideoOrAudio && bIsSelected; + + if (SUCCEEDED(hr) && (g_bHasVideo || g_bHasAudio)) + { + // Set the media item on the player. This method completes asynchronously. + hr = g_pPlayer->SetMediaItem(pEvent->pMediaItem); + } + + if (FAILED(hr) || (!g_bHasVideo && !g_bHasAudio)) + { + SAL_WARN("avmedia.win", + "Player::OnMediaItemCreated failed with error code: " << hr); + + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + m_state = Closed; + } + } +} + +//------------------------------------------------------------------- +// OnMediaItemSet +// +// Called when the IMFPMediaPlayer::SetMediaItem method completes. +//------------------------------------------------------------------- +void Player::OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT* /*pEvent*/) +{ + HRESULT hr = S_OK; + + if (mbAutoPlayBack) + { + hr = g_pPlayer->Play(); + if (SUCCEEDED(hr)) + m_state = Started; + } + + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::OnMediaItemSet failed with error code: " << hr); + + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + } +} + +void Player::OnMediaPosSet(MFP_POSITION_SET_EVENT* /*pEvent*/) { - mbAddWindow = false; - if( mpME ) - mpME->SetNotifyWindow( reinterpret_cast<OAHWND>(nNotifyWnd), WM_GRAPHNOTIFY, reinterpret_cast< LONG_PTR>( this ) ); + // TODO: Handle position set event if needed with update of MediaPlayer UI. + // void MediaWindowControl::update() should be called } +void Player::OnMediaItemEnded(MFP_PLAYBACK_ENDED_EVENT* /*pEvent*/) +{ + if( mbLooping ) + start(); + else + m_state = Stopped; +} -void Player::processEvent() +HRESULT Player::InitializeWindow(bool bAddSoundWindow) { - long nCode; - LONG_PTR nParam1, nParam2; + HRESULT hr = S_OK; + SafeRelease(&g_pPlayer); - while( mpME && SUCCEEDED( mpME->GetEvent( &nCode, &nParam1, &nParam2, 0 ) ) ) + if (bAddSoundWindow) { - if( EC_COMPLETE == nCode ) + static WNDCLASSW* mpWndClass = nullptr; + if ( !mpWndClass ) { - if( mbLooping ) - { - setMediaTime( 0.0 ); - start(); - } - else + mpWndClass = new WNDCLASSW; + + memset( mpWndClass, 0, sizeof( *mpWndClass ) ); + mpWndClass->hInstance = GetModuleHandleW( nullptr ); + mpWndClass->cbWndExtra = sizeof( DWORD ); + mpWndClass->lpfnWndProc = MediaPlayerWndProc_2; + mpWndClass->lpszClassName = L"com_sun_star_media_Sound_Player"; + mpWndClass->hbrBackground = static_cast<HBRUSH>(::GetStockObject( BLACK_BRUSH )); + mpWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW ); + + RegisterClassW( mpWndClass ); + } + if ( !mnFrameWnd ) + { + mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr, + 0, + 0, 0, 0, 0, + nullptr, nullptr, mpWndClass->hInstance, nullptr); + if ( mnFrameWnd ) { - setMediaTime( getDuration() ); - stop(); + ::ShowWindow(mnFrameWnd, SW_HIDE); + SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) ); } } - - mpME->FreeEventParams( nCode, nParam1, nParam2 ); } -} + // Create the MFPlayer object. + hr = MFPCreateMediaPlayer( + nullptr, + FALSE, // Start playback automatically? + 0, // Flags + this, // Callback pointer + mnFrameWnd, // Video window + &g_pPlayer + ); + + // Create a new media item for this URL. + // The CreateMediaItemFromURL method completes asynchronously. When it does, + // MFPlay sends an MFP_EVENT_TYPE_MEDIAITEM_CREATED event. + if (SUCCEEDED(hr)) + hr = g_pPlayer->CreateMediaItemFromURL(o3tl::toW(maURL.getStr()), FALSE, 0, nullptr); + + if (SUCCEEDED(hr)) + m_state = Stopped; + + return hr; +} void SAL_CALL Player::start( ) { ::osl::MutexGuard aGuard(m_aMutex); - if( mpMC ) + if( g_pPlayer ) { - if ( mbAddWindow ) + HRESULT hr = S_OK; + if (g_bHasVideo || g_bHasAudio) { - static WNDCLASSW* mpWndClass = nullptr; - if ( !mpWndClass ) + MFP_MEDIAPLAYER_STATE state = MFP_MEDIAPLAYER_STATE_EMPTY; + hr = g_pPlayer->GetState(&state); + if (SUCCEEDED(hr)) { - mpWndClass = new WNDCLASSW; - - memset( mpWndClass, 0, sizeof( *mpWndClass ) ); - mpWndClass->hInstance = GetModuleHandleW( nullptr ); - mpWndClass->cbWndExtra = sizeof( DWORD ); - mpWndClass->lpfnWndProc = MediaPlayerWndProc_2; - mpWndClass->lpszClassName = L"com_sun_star_media_Sound_Player"; - mpWndClass->hbrBackground = static_cast<HBRUSH>(::GetStockObject( BLACK_BRUSH )); - mpWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW ); - - RegisterClassW( mpWndClass ); - } - if ( !mnFrameWnd ) - { - mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr, - 0, - 0, 0, 0, 0, - nullptr, nullptr, mpWndClass->hInstance, nullptr ); - if ( mnFrameWnd ) + if (state == MFP_MEDIAPLAYER_STATE_PAUSED || state == MFP_MEDIAPLAYER_STATE_STOPPED) { - ::ShowWindow(mnFrameWnd, SW_HIDE); - SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) ); - // mpVW->put_Owner( (OAHWND) mnFrameWnd ); - setNotifyWnd( mnFrameWnd ); + hr = g_pPlayer->Play(); + if (SUCCEEDED(hr)) + m_state = Started; } } } - mpMC->Run(); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::start failed with error code: " << hr); + + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + } } } - void SAL_CALL Player::stop( ) { ::osl::MutexGuard aGuard(m_aMutex); - if( mpMC ) - mpMC->Stop(); + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + HRESULT hr = S_OK; + if (getMediaTime()) + { + hr = g_pPlayer->Pause(); + if (SUCCEEDED(hr)) + m_state = Paused; + } + else + { + hr = g_pPlayer->Stop(); + if (SUCCEEDED(hr)) + m_state = Stopped; + } + } } - sal_Bool SAL_CALL Player::isPlaying() { ::osl::MutexGuard aGuard(m_aMutex); - OAFilterState eFilterState; - bool bRet = false; - - if( mpMC && SUCCEEDED( mpMC->GetState( 10, &eFilterState ) ) ) - bRet = ( State_Running == eFilterState ); + bool bRet = false; + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + MFP_MEDIAPLAYER_STATE state = MFP_MEDIAPLAYER_STATE_EMPTY; + HRESULT hr = g_pPlayer->GetState(&state); + if (SUCCEEDED(hr)) + { + bRet = (state == MFP_MEDIAPLAYER_STATE_PLAYING && m_state == Started); + } + else + { + SAL_WARN("avmedia.win", + "Player::isPlaying failed with error code: " << hr); + } + } return bRet; } - double SAL_CALL Player::getDuration( ) { ::osl::MutexGuard aGuard(m_aMutex); REFTIME aRefTime( 0.0 ); - - if( mpMP ) - mpMP->get_Duration( &aRefTime ); - + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + PROPVARIANT var; + HRESULT hr = g_pPlayer->GetDuration(MFP_POSITIONTYPE_100NS, &var); + if (SUCCEEDED(hr)) + { + hr = PropVariantToDouble(var, &aRefTime); + aRefTime = aRefTime / (1000.0 * 10.0) / 1000.0; // Convert from 100-nanosecond units to seconds + PropVariantClear(&var); + } + } return aRefTime; } - void SAL_CALL Player::setMediaTime( double fTime ) { ::osl::MutexGuard aGuard(m_aMutex); - if( mpMP ) + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) { - const bool bPlaying = isPlaying(); - - mpMP->put_CurrentPosition( fTime ); - - if( !bPlaying && mpMC ) - mpMC->StopWhenReady(); + PROPVARIANT var; + PropVariantInit(&var); + var.vt = VT_I8; // PROPVARIANT type has to be VT_I8. + var.hVal.QuadPart = static_cast<MFTIME>(fTime * 1000.0 * (1000.0 * 10.0)); // Convert from seconds to 100-nanosecond units + HRESULT hr = g_pPlayer->SetPosition(MFP_POSITIONTYPE_100NS, &var); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::setMediaTime: setMediaTime failed with error code: " << hr); + } + PropVariantClear(&var); + // on resetting back to zero the reported timestamp doesn't seem to get + // updated in a reasonable time, so on zero just force an update of timestamp to 0. + // Same as in /core/avmedia/source/gtk/gtkplayer.cxx:GtkPlayer::setMediaTime(double fTime) + // With UpdateVideo() it can help on video streams, but doesn't help on audio streams. TODO! + if (fTime == 0.0) + g_pPlayer->UpdateVideo(); } } - double SAL_CALL Player::getMediaTime( ) { ::osl::MutexGuard aGuard(m_aMutex); REFTIME aRefTime( 0.0 ); - - if( mpMP ) - mpMP->get_CurrentPosition( &aRefTime ); + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + PROPVARIANT var; + HRESULT hr = g_pPlayer->GetPosition(MFP_POSITIONTYPE_100NS, &var); + if (SUCCEEDED(hr)) + { + hr = PropVariantToDouble(var, &aRefTime); + aRefTime = aRefTime / (1000.0 * 10.0) / 1000.0; // Convert from 100-nanosecond units to seconds + PropVariantClear(&var); + } + } return aRefTime; } - void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) { ::osl::MutexGuard aGuard(m_aMutex); @@ -289,7 +551,6 @@ void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) mbLooping = bSet; } - sal_Bool SAL_CALL Player::isPlaybackLoop( ) { ::osl::MutexGuard aGuard(m_aMutex); @@ -297,116 +558,143 @@ sal_Bool SAL_CALL Player::isPlaybackLoop( ) return mbLooping; } - void SAL_CALL Player::setMute( sal_Bool bSet ) { ::osl::MutexGuard aGuard(m_aMutex); - if (mpBA && (mbMuted != static_cast<bool>(bSet))) + if (g_pPlayer && (g_bHasVideo || g_bHasAudio) && + (mbMuted != static_cast<BOOL>(bSet))) { mbMuted = bSet; - mpBA->put_Volume( mbMuted ? -10000 : mnUnmutedVolume ); + HRESULT hr = g_pPlayer->SetMute(bSet); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::setMute: setMute failed with error code: " << hr); + } } } - sal_Bool SAL_CALL Player::isMute( ) { ::osl::MutexGuard aGuard(m_aMutex); + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + HRESULT hr = g_pPlayer->GetMute(&mbMuted); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::isMute: getMute failed with error code: " << hr); + mbMuted = false; // Reset to default if error occurs + } + } return mbMuted; } - void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) { ::osl::MutexGuard aGuard(m_aMutex); - mnUnmutedVolume = static_cast< long >( nVolumeDB ) * 100; + mnUnmutedVolume = static_cast< float >( (nVolumeDB / AVMEDIA_DB_RANGE) + 1.0 ); - if( !mbMuted && mpBA ) - mpBA->put_Volume( mnUnmutedVolume ); + if ( g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + HRESULT hr = g_pPlayer->SetVolume(mnUnmutedVolume); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::setVolumeDB: setVolumeDB failed with error code: " << hr); + } + } } - sal_Int16 SAL_CALL Player::getVolumeDB( ) { ::osl::MutexGuard aGuard(m_aMutex); - return static_cast< sal_Int16 >( mnUnmutedVolume / 100 ); + if (g_pPlayer && (g_bHasVideo || g_bHasAudio)) + { + HRESULT hr = g_pPlayer->GetVolume(&mnUnmutedVolume); + if (FAILED(hr)) + { + SAL_WARN("avmedia.win", + "Player::getVolumeDB: getVolumeDB failed with error code: " << hr); + mnUnmutedVolume = 1; // Reset to default if error occurs + } + } + return static_cast< sal_Int16 >( (mnUnmutedVolume - 1.0) * AVMEDIA_DB_RANGE); } - awt::Size SAL_CALL Player::getPreferredPlayerWindowSize( ) { ::osl::MutexGuard aGuard(m_aMutex); - awt::Size aSize( 0, 0 ); - - if( mpBV ) - { - long nWidth = 0, nHeight = 0; - - mpBV->GetVideoSize( &nWidth, &nHeight ); - aSize.Width = nWidth; - aSize.Height = nHeight; - } - - return aSize; + return awt::Size(mnFrameWidth, mnFrameHeight); } - uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments ) { ::osl::MutexGuard aGuard(m_aMutex); uno::Reference< ::media::XPlayerWindow > xRet; - awt::Size aSize( getPreferredPlayerWindowSize() ); + awt::Size aSize(getPreferredPlayerWindowSize()); - if( mpVW && aSize.Width > 0 && aSize.Height > 0 ) + if( aSize.Width > 0 && aSize.Height > 0 ) { rtl::Reference<::avmedia::win::Window> pWindow = new ::avmedia::win::Window( *this ); xRet = pWindow; - if( !pWindow->create( aArguments ) ) + if (!pWindow->create( aArguments ) ) xRet.clear(); } + else + { + if (SUCCEEDED(InitializeWindow(true))) + { + sal_IntPtr pIntPtr = 0; + if ((aArguments.getLength() >= 4) && (aArguments[3] >>= pIntPtr) && pIntPtr) + { + auto pItem = reinterpret_cast<const avmedia::MediaItem*>(pIntPtr); + if (pItem->getState() == avmedia::MediaState::Play) + { + setAutoPlayBack(true); + } + } + } + else + { + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + } + } return xRet; } - uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber( ) { uno::Reference< media::XFrameGrabber > xRet; if( !maURL.isEmpty() ) { - rtl::Reference<FrameGrabber> pGrabber = new FrameGrabber(); - + rtl::Reference<FrameGrabber> pGrabber = new FrameGrabber(maURL, mnFrameWidth, mnFrameHeight); xRet = pGrabber; - - if( !pGrabber->create( maURL ) ) - xRet.clear(); } return xRet; } - OUString SAL_CALL Player::getImplementationName( ) { return AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME; } - sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName ) { return cppu::supportsService(this, ServiceName); } - uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames( ) { return { AVMEDIA_WIN_PLAYER_SERVICENAME }; diff --git a/avmedia/source/win/player.hxx b/avmedia/source/win/player.hxx index 1563d549a85e..04675217881f 100644 --- a/avmedia/source/win/player.hxx +++ b/avmedia/source/win/player.hxx @@ -23,6 +23,10 @@ #include <WinDef.h> +// Media Foundation headers +#include <mfplay.h> +#include <mferror.h> + #include "wincommon.hxx" #include <com/sun/star/media/XPlayer.hpp> @@ -31,26 +35,31 @@ #include <cppuhelper/basemutex.hxx> #include <systools/win32/comtools.hxx> -struct IGraphBuilder; -struct IBaseFilter; -struct IMediaControl; -struct IMediaEventEx; -struct IMediaSeeking; -struct IMediaPosition; -struct IBasicAudio; -struct IBasicVideo; -struct IVideoWindow; -struct IDDrawExclModeVideo; -struct IDirectDraw; -struct IDirectDrawSurface; namespace avmedia::win { +enum PlayerState +{ + Closed = 0, // No session. + Started, // Session is playing a file. + Paused, // Session is paused. + Stopped // Session is stopped (ready to play). +}; + +template <class T> void SafeRelease(T **ppT) +{ + if (*ppT) + { + (*ppT)->Release(); + *ppT = nullptr; + } +} + typedef ::cppu::WeakComponentImplHelper< css::media::XPlayer, css::lang::XServiceInfo > Player_BASE; - -class Player : public cppu::BaseMutex, +class Player : public IMFPMediaPlayerCallback, + public cppu::BaseMutex, public Player_BASE, public sal::systools::CoInitializeGuard { @@ -59,12 +68,32 @@ public: explicit Player(); ~Player() override; - bool create( const OUString& rURL ); - - void setNotifyWnd( HWND nNotifyWnd ); - void processEvent(); - - const IVideoWindow* getVideoWindow() const; + bool create( const OUString& rURL ); + HRESULT InitializeWindow( bool bAddSoundWindow ); + void setNotifyWnd( HWND nNotifyWnd ); + HWND* getNotifyWnd() { return &mnFrameWnd; } + void setAutoPlayBack(bool bVal) { mbAutoPlayBack = bVal; } + const UINT32 GetVideoWidth() const { return mnFrameWidth; } + const UINT32 GetVideoHeight() const { return mnFrameHeight; } + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMFPMediaPlayerCallback methods + void STDMETHODCALLTYPE OnMediaPlayerEvent(MFP_EVENT_HEADER* pEventHeader); + + // Window message handlers + void OnClose(HWND hwnd); + void OnPaint(HWND hwnd); + void OnSize(HWND hwnd, UINT state, int cx, int cy); + + // MFPlay event handler functions. + void OnMediaItemCreated(MFP_MEDIAITEM_CREATED_EVENT* pEvent); + void OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT* pEvent); + void OnMediaPosSet(MFP_POSITION_SET_EVENT* pEvent); + void OnMediaItemEnded(MFP_PLAYBACK_ENDED_EVENT* pEvent); // XPlayer virtual void SAL_CALL start( ) override; @@ -93,19 +122,19 @@ public: private: + long m_cRef; // Reference count. OUString maURL; - sal::systools::COMReference<IGraphBuilder> mpGB; - sal::systools::COMReference<IMediaControl> mpMC; - sal::systools::COMReference<IMediaEventEx> mpME; - sal::systools::COMReference<IMediaPosition> mpMP; - sal::systools::COMReference<IBasicAudio> mpBA; - sal::systools::COMReference<IBasicVideo> mpBV; - sal::systools::COMReference<IVideoWindow> mpVW; - long mnUnmutedVolume; + float mnUnmutedVolume; HWND mnFrameWnd; - bool mbMuted; + BOOL mbMuted; bool mbLooping; - bool mbAddWindow; + bool mbAutoPlayBack; + UINT32 mnFrameWidth; + UINT32 mnFrameHeight; + IMFPMediaPlayer* g_pPlayer; // The MFPlay player object. + BOOL g_bHasVideo; + BOOL g_bHasAudio; + PlayerState m_state; }; } // namespace avmedia::win diff --git a/avmedia/source/win/wincommon.hxx b/avmedia/source/win/wincommon.hxx index 5572f6c3e63d..ec0d0004b41d 100644 --- a/avmedia/source/win/wincommon.hxx +++ b/avmedia/source/win/wincommon.hxx @@ -36,6 +36,4 @@ #include <com/sun/star/awt/MouseButton.hpp> #include <com/sun/star/media/XManager.hpp> -#define WM_GRAPHNOTIFY (WM_USER + 567) - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/win/window.cxx b/avmedia/source/win/window.cxx index 8cf3fee74d3d..4c3100bea864 100644 --- a/avmedia/source/win/window.cxx +++ b/avmedia/source/win/window.cxx @@ -18,12 +18,12 @@ */ #include <objbase.h> -#include <strmif.h> -#include <control.h> #include <dshow.h> #include <com/sun/star/awt/SystemPointer.hpp> #include <cppuhelper/supportsservice.hxx> +#include <avmedia/mediaitem.hxx> +#include <avmedia/mediawindow.hxx> #include "window.hxx" #include "player.hxx" @@ -35,7 +35,7 @@ using namespace ::com::sun::star; namespace avmedia::win { -static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 ) +static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { Window* pWindow = reinterpret_cast<Window*>(GetWindowLongPtrW( hWnd, 0 )); bool bProcessed = true; @@ -44,14 +44,14 @@ static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, L { switch( nMsg ) { + HANDLE_MSG(hWnd, WM_CLOSE, pWindow->getPlayer().OnClose); + HANDLE_MSG(hWnd, WM_PAINT, pWindow->getPlayer().OnPaint); + HANDLE_MSG(hWnd, WM_SIZE, pWindow->getPlayer().OnSize); + case WM_SETCURSOR: pWindow->updatePointer(); break; - case WM_GRAPHNOTIFY: - pWindow->processGraphEvent(); - break; - case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: @@ -59,7 +59,7 @@ static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, L case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: - PostMessage(pWindow->getParentWnd(), nMsg, nPar1, nPar2); + PostMessage(pWindow->getParentWnd(), nMsg, wParam, lParam); break; case WM_SETFOCUS: @@ -77,7 +77,7 @@ static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, L else bProcessed = false; - return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ) ); + return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, wParam, lParam ) ); } static WNDCLASSW* lcl_getWndClass() @@ -112,12 +112,32 @@ Window::~Window() ::DestroyWindow( mnFrameWnd ); } +const css::awt::Rectangle Window::getParentPosSize() const +{ + awt::Rectangle aRet; + + if (mnParentWnd) + { + ::RECT aWndRect; + + if (::GetClientRect(mnParentWnd, &aWndRect)) + { + aRet.X = aWndRect.left; + aRet.Y = aWndRect.top; + aRet.Width = aWndRect.right - aWndRect.left + 1; + aRet.Height = aWndRect.bottom - aWndRect.top + 1; + } + } + + return aRet; +} + void Window::ImplLayoutVideoWindow() { if( media::ZoomLevel_NOT_AVAILABLE != meZoomLevel ) { awt::Size aPrefSize( mrPlayer.getPreferredPlayerWindowSize() ); - awt::Rectangle aRect = getPosSize(); + awt::Rectangle aRect = getParentPosSize(); int nW = aRect.Width, nH = aRect.Height; int nVideoW = nW, nVideoH = nH; int nX = 0, nY = 0, nWidth = 0, nHeight = 0; @@ -190,19 +210,16 @@ void Window::ImplLayoutVideoWindow() nX = nY = nWidth = nHeight = 0; } - IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() ); - - if( pVideoWindow ) - pVideoWindow->SetWindowPosition( nX, nY, nWidth, nHeight ); + if (mnFrameWnd && mrPlayer.GetVideoWidth() && mrPlayer.GetVideoHeight() ) + SetWindowPos( mnFrameWnd, HWND_TOP, nX, nY, nWidth, nHeight, 0 ); } } bool Window::create( const uno::Sequence< uno::Any >& rArguments ) { - IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() ); static WNDCLASSW* mpWndClass = lcl_getWndClass(); - if( !mnFrameWnd && pVideoWindow && mpWndClass ) + if( !mnFrameWnd && mpWndClass ) { awt::Rectangle aRect; sal_IntPtr nWnd; @@ -213,32 +230,38 @@ bool Window::create( const uno::Sequence< uno::Any >& rArguments ) mnParentWnd = reinterpret_cast<HWND>(nWnd); mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr, - WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, - aRect.X, aRect.Y, aRect.Width, aRect.Height, - mnParentWnd, nullptr, mpWndClass->hInstance, nullptr ); + WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + aRect.X, aRect.Y, aRect.Width, aRect.Height, + mnParentWnd, nullptr, mpWndClass->hInstance, nullptr ); if( mnFrameWnd ) { SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) ); - - pVideoWindow->put_Owner( reinterpret_cast<OAHWND>(mnFrameWnd) ); - pVideoWindow->put_MessageDrain( reinterpret_cast<OAHWND>(mnFrameWnd) ); - pVideoWindow->put_WindowStyle( WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); - - mrPlayer.setNotifyWnd( mnFrameWnd ); - - meZoomLevel = media::ZoomLevel_FIT_TO_WINDOW; - ImplLayoutVideoWindow(); + mrPlayer.setNotifyWnd(mnFrameWnd); + if (SUCCEEDED(mrPlayer.InitializeWindow(false))) + { + sal_IntPtr pIntPtr = 0; + if ((rArguments.getLength() >= 4) && (rArguments[3] >>= pIntPtr) && pIntPtr) + { + auto pItem = reinterpret_cast<const avmedia::MediaItem*>(pIntPtr); + if (pItem->getState() == avmedia::MediaState::Play) + { + mrPlayer.setAutoPlayBack(true); + } + } + meZoomLevel = media::ZoomLevel_FIT_TO_WINDOW; + ImplLayoutVideoWindow(); + } + else + { + ::avmedia::MediaWindow::executeFormatErrorBox(nullptr); + } } } return( mnFrameWnd != nullptr ); } -void Window::processGraphEvent() -{ - mrPlayer.processEvent(); -} void Window::updatePointer() { @@ -323,15 +346,8 @@ awt::Rectangle SAL_CALL Window::getPosSize() void SAL_CALL Window::setVisible( sal_Bool bVisible ) { - if( mnFrameWnd ) - { - IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() ); - - if( pVideoWindow ) - pVideoWindow->put_Visible( bVisible ? OATRUE : OAFALSE ); - + if( mnFrameWnd && mrPlayer.GetVideoWidth() && mrPlayer.GetVideoHeight() ) ::ShowWindow( mnFrameWnd, bVisible ? SW_SHOW : SW_HIDE ); - } } void SAL_CALL Window::setEnable( sal_Bool bEnable ) diff --git a/avmedia/source/win/window.hxx b/avmedia/source/win/window.hxx index 0ab691ffbb82..7c6316ef473e 100644 --- a/avmedia/source/win/window.hxx +++ b/avmedia/source/win/window.hxx @@ -30,8 +30,6 @@ #include <com/sun/star/media/XPlayerWindow.hpp> -struct IVideoWindow; - namespace avmedia::win { class Player; @@ -46,7 +44,6 @@ public: ~Window() override; bool create( const css::uno::Sequence< css::uno::Any >& aArguments ); - void processGraphEvent(); void updatePointer(); // XPlayerWindow @@ -93,6 +90,7 @@ public: void fireKeyReleasedEvent( const css::awt::KeyEvent& rEvt ); void fireSetFocusEvent( const css::awt::FocusEvent& rEvt ); HWND getParentWnd() const { return mnParentWnd; } + Player& getPlayer() const { return mrPlayer; } private: @@ -110,6 +108,7 @@ private: HWND mnParentWnd; int mnPointerType; + const css::awt::Rectangle getParentPosSize() const; void ImplLayoutVideoWindow(); }; diff --git a/slideshow/source/engine/shapes/viewmediashape.cxx b/slideshow/source/engine/shapes/viewmediashape.cxx index ec19f11b66f8..dbdb172fc40f 100644 --- a/slideshow/source/engine/shapes/viewmediashape.cxx +++ b/slideshow/source/engine/shapes/viewmediashape.cxx @@ -464,12 +464,16 @@ namespace slideshow::internal SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape); auto pMediaObj = dynamic_cast<SdrMediaObj*>(pObj); - const avmedia::MediaItem* pMediaItem = nullptr; + avmedia::MediaItem* pMediaItem = nullptr; if (pMediaObj) { - pMediaItem = &pMediaObj->getMediaProperties(); + pMediaItem = pMediaObj->getMediaProperties().Clone(); } + // TODO In slideshow we cannot play/pause/stop the video + if (pMediaItem) + pMediaItem->setState(avmedia::MediaState::Play); + uno::Sequence< uno::Any > aArgs{ uno::Any(nParentWindowHandle), uno::Any(aAWTRect), diff --git a/svx/uiconfig/ui/medialine.ui b/svx/uiconfig/ui/medialine.ui index 4c90cae1d213..c83b1834069b 100644 --- a/svx/uiconfig/ui/medialine.ui +++ b/svx/uiconfig/ui/medialine.ui @@ -31,6 +31,7 @@ <property name="show-arrow">False</property> <child> <object class="GtkToolButton" id="open"> + <property name="can-focus">False</property> <property name="no-show-all">True</property> <property name="label" translatable="yes" context="medialine|toolbutton_open">Open</property> <property name="use-underline">True</property> @@ -43,6 +44,7 @@ </child> <child> <object class="GtkToolButton" id="apply"> + <property name="can-focus">False</property> <property name="no-show-all">True</property> <property name="label" translatable="yes" context="medialine|toolbutton_apply">Apply</property> <property name="use-underline">True</property> @@ -66,6 +68,7 @@ <child> <object class="GtkToggleToolButton" id="play"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="medialine|toolbutton_play">Play</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02049.png</property> @@ -78,6 +81,7 @@ <child> <object class="GtkToggleToolButton" id="pause"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="medialine|toolbutton_pause">Pause</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02050.png</property> @@ -90,6 +94,7 @@ <child> <object class="GtkToggleToolButton" id="stop"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="medialine|toolbutton_stop">Stop</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02051.png</property> @@ -112,6 +117,7 @@ <child> <object class="GtkToggleToolButton" id="loop"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="medialine|toolbutton_loop">Repeat</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02052.png</property> @@ -196,6 +202,7 @@ <child> <object class="GtkToggleToolButton" id="mute"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="medialine|toolbutton_mute">Mute</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02054.png</property> @@ -211,11 +218,13 @@ <property name="can-focus">True</property> <child> <object class="GtkScale" id="volumeslider"> - <property name="width-request">50</property> + <property name="width-request">150</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="opacity">0.9882352941176471</property> + <property name="valign">center</property> <property name="hexpand">True</property> + <property name="vexpand">True</property> <property name="adjustment">adjustment2</property> <property name="draw-value">False</property> </object> diff --git a/svx/uiconfig/ui/mediawindow.ui b/svx/uiconfig/ui/mediawindow.ui index 327c52356d21..8f9757fa96b5 100644 --- a/svx/uiconfig/ui/mediawindow.ui +++ b/svx/uiconfig/ui/mediawindow.ui @@ -97,6 +97,7 @@ <child> <object class="GtkToolButton" id="open"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_open">Open</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02048.png</property> @@ -109,6 +110,7 @@ <child> <object class="GtkToolButton" id="apply"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_apply">Apply</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02053.png</property> @@ -131,6 +133,7 @@ <child> <object class="GtkToggleToolButton" id="play"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_play">Play</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02049.png</property> @@ -143,6 +146,7 @@ <child> <object class="GtkToggleToolButton" id="pause"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_pause">Pause</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02050.png</property> @@ -155,6 +159,7 @@ <child> <object class="GtkToggleToolButton" id="stop"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_stop">Stop</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02051.png</property> @@ -177,6 +182,7 @@ <child> <object class="GtkToggleToolButton" id="loop"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_loop">Repeat</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02052.png</property> @@ -206,6 +212,7 @@ <child> <object class="GtkToggleToolButton" id="mute"> <property name="visible">True</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="mediawindow|toolbutton_mute">Mute</property> <property name="use-underline">True</property> <property name="icon-name">avmedia/res/av02054.png</property> @@ -224,10 +231,13 @@ </child> <child> <object class="GtkScale" id="volumeslider"> + <property name="width-request">150</property> <property name="visible">True</property> <property name="can-focus">True</property> <property name="opacity">0.9882352941176471</property> + <property name="valign">center</property> <property name="hexpand">True</property> + <property name="vexpand">True</property> <property name="adjustment">adjustment2</property> <property name="draw-value">False</property> </object>
