1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
23 #include "mediawindow_impl.hxx"
24 #include "mediaevent_impl.hxx"
25 #include "mediamisc.hxx"
26 #include "mediawindow.hrc"
27 #include "helpids.hrc"
32 #include <comphelper/processfactory.hxx>
33 #include <osl/mutex.hxx>
34 #include <tools/urlobj.hxx>
35 #include <unotools/securityoptions.hxx>
36 #include <vcl/svapp.hxx>
38 #include <com/sun/star/awt/SystemPointer.hpp>
39 #include <com/sun/star/lang/XComponent.hpp>
40 #include <com/sun/star/media/XManager.hpp>
41 #include <vcl/sysdata.hxx>
42 #include <vcl/opengl/OpenGLContext.hxx>
44 using namespace ::com::sun::star
;
46 namespace avmedia
{ namespace priv
{
48 MediaWindowControl::MediaWindowControl(vcl::Window
* pParent
)
49 : MediaControl(pParent
, MEDIACONTROLSTYLE_MULTILINE
)
53 void MediaWindowControl::update()
57 static_cast< MediaWindowImpl
* >( GetParent() )->updateMediaItem( aItem
);
61 void MediaWindowControl::execute(const MediaItem
& rItem
)
63 static_cast<MediaWindowImpl
*>(GetParent())->executeMediaItem(rItem
);
66 MediaChildWindow::MediaChildWindow(vcl::Window
* pParent
)
67 : SystemChildWindow(pParent
, WB_CLIPCHILDREN
)
72 MediaChildWindow::MediaChildWindow(vcl::Window
* pParent
, SystemWindowData
* pData
)
73 : SystemChildWindow(pParent
, WB_CLIPCHILDREN
, pData
)
78 void MediaChildWindow::MouseMove( const MouseEvent
& rMEvt
)
80 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
81 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
83 SystemChildWindow::MouseMove( rMEvt
);
84 GetParent()->MouseMove( aTransformedEvent
);
87 void MediaChildWindow::MouseButtonDown( const MouseEvent
& rMEvt
)
89 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
90 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
92 SystemChildWindow::MouseButtonDown( rMEvt
);
93 GetParent()->MouseButtonDown( aTransformedEvent
);
96 void MediaChildWindow::MouseButtonUp( const MouseEvent
& rMEvt
)
98 const MouseEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt
.GetPosPixel() ) ),
99 rMEvt
.GetClicks(), rMEvt
.GetMode(), rMEvt
.GetButtons(), rMEvt
.GetModifier() );
101 SystemChildWindow::MouseButtonUp( rMEvt
);
102 GetParent()->MouseButtonUp( aTransformedEvent
);
105 void MediaChildWindow::KeyInput( const KeyEvent
& rKEvt
)
107 SystemChildWindow::KeyInput( rKEvt
);
108 GetParent()->KeyInput( rKEvt
);
111 void MediaChildWindow::KeyUp( const KeyEvent
& rKEvt
)
113 SystemChildWindow::KeyUp( rKEvt
);
114 GetParent()->KeyUp( rKEvt
);
117 void MediaChildWindow::Command( const CommandEvent
& rCEvt
)
119 const CommandEvent
aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rCEvt
.GetMousePosPixel() ) ),
120 rCEvt
.GetCommand(), rCEvt
.IsMouseEvent(), rCEvt
.GetEventData() );
122 SystemChildWindow::Command( rCEvt
);
123 GetParent()->Command( aTransformedEvent
);
126 MediaWindowImpl::MediaWindowImpl(vcl::Window
* pParent
, MediaWindow
* pMediaWindow
, bool bInternalMediaControl
)
128 , DropTargetHelper(this)
129 , DragSourceHelper(this)
130 , mpMediaWindow(pMediaWindow
)
132 , mbEventTransparent(true)
133 , mpMediaWindowControl(bInternalMediaControl
? VclPtr
<MediaWindowControl
>::Create(this) : nullptr)
134 , mpEmptyBmpEx(nullptr)
135 , mpAudioBmpEx(nullptr)
137 if (mpMediaWindowControl
)
139 mpMediaWindowControl
->SetSizePixel(mpMediaWindowControl
->getMinSizePixel());
140 mpMediaWindowControl
->Show();
144 MediaWindowImpl::~MediaWindowImpl()
149 void MediaWindowImpl::dispose()
154 if (mxPlayerWindow
.is())
156 mxPlayerWindow
->removeKeyListener( uno::Reference
< awt::XKeyListener
>( mxEventsIf
, uno::UNO_QUERY
) );
157 mxPlayerWindow
->removeMouseListener( uno::Reference
< awt::XMouseListener
>( mxEventsIf
, uno::UNO_QUERY
) );
158 mxPlayerWindow
->removeMouseMotionListener( uno::Reference
< awt::XMouseMotionListener
>( mxEventsIf
, uno::UNO_QUERY
) );
160 uno::Reference
< lang::XComponent
> xComponent( mxPlayerWindow
, uno::UNO_QUERY
);
162 xComponent
->dispose();
164 mxPlayerWindow
.clear();
167 uno::Reference
< lang::XComponent
> xComponent( mxPlayer
, uno::UNO_QUERY
);
168 if (xComponent
.is()) // this stops the player
169 xComponent
->dispose();
173 mpMediaWindow
= NULL
;
179 mpMediaWindowControl
.disposeAndClear();
180 mpChildWindow
.disposeAndClear();
185 uno::Reference
<media::XPlayer
> MediaWindowImpl::createPlayer(const OUString
& rURL
, const OUString
& rReferer
, const OUString
* pMimeType
)
187 uno::Reference
<media::XPlayer
> xPlayer
;
192 if (SvtSecurityOptions().isUntrustedReferer(rReferer
))
196 uno::Reference
<uno::XComponentContext
> xContext(::comphelper::getProcessComponentContext());
198 if (!pMimeType
|| *pMimeType
== AVMEDIA_MIMETYPE_COMMON
)
201 static const char * aServiceManagers
[] =
203 AVMEDIA_MANAGER_SERVICE_PREFERRED
,
204 AVMEDIA_MANAGER_SERVICE_NAME
,
205 // a fallback path just for gstreamer which has
206 // two significant versions deployed at once ...
207 #ifdef AVMEDIA_MANAGER_SERVICE_NAME_OLD
208 AVMEDIA_MANAGER_SERVICE_NAME_OLD
210 // fallback to AVMedia framework on OS X
211 #ifdef AVMEDIA_MANAGER_SERVICE_NAME_FALLBACK1
212 AVMEDIA_MANAGER_SERVICE_NAME_FALLBACK1
216 for (sal_uInt32 i
= 0; !xPlayer
.is() && i
< SAL_N_ELEMENTS( aServiceManagers
); ++i
)
218 const OUString
aServiceName(aServiceManagers
[i
],
219 strlen( aServiceManagers
[i
]),
220 RTL_TEXTENCODING_ASCII_US
);
222 xPlayer
= createPlayer(rURL
, aServiceName
, xContext
);
225 #if HAVE_FEATURE_GLTF
226 else if ( *pMimeType
== AVMEDIA_MIMETYPE_JSON
)
228 xPlayer
= createPlayer(rURL
, AVMEDIA_OPENGL_MANAGER_SERVICE_NAME
, xContext
);
235 uno::Reference
< media::XPlayer
> MediaWindowImpl::createPlayer(
236 const OUString
& rURL
, const OUString
& rManagerServName
,
237 uno::Reference
< uno::XComponentContext
> xContext
)
239 uno::Reference
< media::XPlayer
> xPlayer
;
242 uno::Reference
< media::XManager
> xManager (
243 xContext
->getServiceManager()->createInstanceWithContext(rManagerServName
, xContext
),
246 xPlayer
= uno::Reference
< media::XPlayer
>( xManager
->createPlayer( rURL
), uno::UNO_QUERY
);
248 SAL_WARN( "avmedia", "failed to create media player service " << rManagerServName
);
249 } catch ( const uno::Exception
&e
)
251 SAL_WARN( "avmedia", "couldn't create media player " << rManagerServName
252 << ", exception '" << e
.Message
<< '\'');
257 void MediaWindowImpl::setURL( const OUString
& rURL
,
258 OUString
const& rTempURL
, OUString
const& rReferer
)
260 maReferer
= rReferer
;
261 if( rURL
!= getURL() )
266 if( mxPlayerWindow
.is() )
268 mxPlayerWindow
->setVisible( false );
269 mxPlayerWindow
.clear();
273 mTempFileURL
.clear();
275 if (!rTempURL
.isEmpty())
278 mTempFileURL
= rTempURL
;
282 INetURLObject
aURL( rURL
);
284 if (aURL
.GetProtocol() != INetProtocol::NotValid
)
285 maFileURL
= aURL
.GetMainURL(INetURLObject::DECODE_UNAMBIGUOUS
);
290 mxPlayer
= createPlayer((!mTempFileURL
.isEmpty()) ? mTempFileURL
: maFileURL
, rReferer
, &m_sMimeType
);
295 const OUString
& MediaWindowImpl::getURL() const
300 bool MediaWindowImpl::isValid() const
302 return( mxPlayer
.is() );
305 Size
MediaWindowImpl::getPreferredSize() const
311 awt::Size
aPrefSize( mxPlayer
->getPreferredPlayerWindowSize() );
313 aRet
.Width() = aPrefSize
.Width
;
314 aRet
.Height() = aPrefSize
.Height
;
320 bool MediaWindowImpl::start()
322 return mxPlayer
.is() && ( mxPlayer
->start(), true );
325 void MediaWindowImpl::updateMediaItem( MediaItem
& rItem
) const
328 rItem
.setState( MEDIASTATE_PLAY
);
330 rItem
.setState( ( 0.0 == getMediaTime() ) ? MEDIASTATE_STOP
: MEDIASTATE_PAUSE
);
332 rItem
.setDuration( getDuration() );
333 rItem
.setTime( getMediaTime() );
334 rItem
.setLoop( isPlaybackLoop() );
335 rItem
.setMute( isMute() );
336 rItem
.setVolumeDB( getVolumeDB() );
337 rItem
.setZoom( getZoom() );
338 rItem
.setURL( getURL(), mTempFileURL
, maReferer
);
341 void MediaWindowImpl::executeMediaItem( const MediaItem
& rItem
)
343 const AVMediaSetMask nMaskSet
= rItem
.getMaskSet();
346 if (nMaskSet
& AVMediaSetMask::URL
)
348 m_sMimeType
= rItem
.getMimeType();
349 setURL(rItem
.getURL(), rItem
.getTempURL(), rItem
.getReferer());
352 // set different states next
353 if (nMaskSet
& AVMediaSetMask::TIME
)
354 setMediaTime(std::min(rItem
.getTime(), getDuration()));
356 if (nMaskSet
& AVMediaSetMask::LOOP
)
357 setPlaybackLoop(rItem
.isLoop());
359 if (nMaskSet
& AVMediaSetMask::MUTE
)
360 setMute(rItem
.isMute());
362 if (nMaskSet
& AVMediaSetMask::VOLUMEDB
)
363 setVolumeDB(rItem
.getVolumeDB());
365 if (nMaskSet
& AVMediaSetMask::ZOOM
)
366 setZoom(rItem
.getZoom());
368 // set play state at last
369 if (nMaskSet
& AVMediaSetMask::STATE
)
371 switch (rItem
.getState())
373 case MEDIASTATE_PLAY
:
380 case MEDIASTATE_PAUSE
:
387 case MEDIASTATE_STOP
:
401 bool MediaWindowImpl::setZoom(css::media::ZoomLevel eLevel
)
403 return mxPlayerWindow
.is() && mxPlayerWindow
->setZoomLevel( eLevel
);
406 css::media::ZoomLevel
MediaWindowImpl::getZoom() const
408 return( mxPlayerWindow
.is() ? mxPlayerWindow
->getZoomLevel() : media::ZoomLevel_NOT_AVAILABLE
);
411 void MediaWindowImpl::stop()
417 bool MediaWindowImpl::isPlaying() const
419 return( mxPlayer
.is() && mxPlayer
->isPlaying() );
422 double MediaWindowImpl::getDuration() const
424 return( mxPlayer
.is() ? mxPlayer
->getDuration() : 0.0 );
427 void MediaWindowImpl::setMediaTime( double fTime
)
430 mxPlayer
->setMediaTime( fTime
);
433 double MediaWindowImpl::getMediaTime() const
435 return( mxPlayer
.is() ? mxPlayer
->getMediaTime() : 0.0 );
438 void MediaWindowImpl::setPlaybackLoop( bool bSet
)
441 mxPlayer
->setPlaybackLoop( bSet
);
444 bool MediaWindowImpl::isPlaybackLoop() const
446 return mxPlayer
.is() && mxPlayer
->isPlaybackLoop();
449 void MediaWindowImpl::setMute( bool bSet
)
452 mxPlayer
->setMute( bSet
);
455 bool MediaWindowImpl::isMute() const
457 return mxPlayer
.is() && mxPlayer
->isMute();
460 void MediaWindowImpl::setVolumeDB( sal_Int16 nVolumeDB
)
463 mxPlayer
->setVolumeDB( nVolumeDB
);
466 sal_Int16
MediaWindowImpl::getVolumeDB() const
468 return (mxPlayer
.is() ? mxPlayer
->getVolumeDB() : 0);
471 void MediaWindowImpl::stopPlayingInternal(bool bStop
)
475 bStop
? mxPlayer
->stop() : mxPlayer
->start();
479 void MediaWindowImpl::onURLChanged()
481 if (m_sMimeType
== AVMEDIA_MIMETYPE_COMMON
)
483 mpChildWindow
.disposeAndClear();
484 mpChildWindow
.reset(VclPtr
<MediaChildWindow
>::Create(this));
486 #if HAVE_FEATURE_GLTF
487 else if (m_sMimeType
== AVMEDIA_MIMETYPE_JSON
)
489 SystemWindowData aWinData
= OpenGLContext::generateWinData(this, false);
490 mpChildWindow
.disposeAndClear();
491 mpChildWindow
.reset(VclPtr
<MediaChildWindow
>::Create(this,&aWinData
));
492 mbEventTransparent
= false;
497 mpChildWindow
->SetHelpId(HID_AVMEDIA_PLAYERWINDOW
);
498 mxEventsIf
.set(static_cast<cppu::OWeakObject
*>(mpEvents
= new MediaEventListenersImpl(*mpChildWindow
.get())));
503 uno::Sequence
<uno::Any
> aArgs( 3 );
504 uno::Reference
<media::XPlayerWindow
> xPlayerWindow
;
506 const Size
aSize(mpChildWindow
->GetSizePixel());
508 aArgs
[0] = uno::makeAny(mpChildWindow
->GetParentWindowHandle());
509 aArgs
[1] = uno::makeAny(awt::Rectangle(aPoint
.X(), aPoint
.Y(), aSize
.Width(), aSize
.Height()));
510 aArgs
[2] = uno::makeAny(reinterpret_cast<sal_IntPtr
>(mpChildWindow
.get()));
514 xPlayerWindow
= mxPlayer
->createPlayerWindow( aArgs
);
516 catch( const uno::RuntimeException
& )
518 // happens eg, on MacOSX where Java frames cannot be created from X11 window handles
521 mxPlayerWindow
= xPlayerWindow
;
523 if( xPlayerWindow
.is() )
525 xPlayerWindow
->addKeyListener( uno::Reference
< awt::XKeyListener
>( mxEventsIf
, uno::UNO_QUERY
) );
526 xPlayerWindow
->addMouseListener( uno::Reference
< awt::XMouseListener
>( mxEventsIf
, uno::UNO_QUERY
) );
527 xPlayerWindow
->addMouseMotionListener( uno::Reference
< awt::XMouseMotionListener
>( mxEventsIf
, uno::UNO_QUERY
) );
528 xPlayerWindow
->addFocusListener( uno::Reference
< awt::XFocusListener
>( mxEventsIf
, uno::UNO_QUERY
) );
532 mxPlayerWindow
.clear();
534 if( mxPlayerWindow
.is() )
535 mpChildWindow
->Show();
537 mpChildWindow
->Hide();
539 if( mpMediaWindowControl
)
543 updateMediaItem( aItem
);
544 mpMediaWindowControl
->setState( aItem
);
548 void MediaWindowImpl::setPosSize(const Rectangle
& rRect
)
550 SetPosSizePixel(rRect
.TopLeft(), rRect
.GetSize());
553 void MediaWindowImpl::setPointer(const Pointer
& rPointer
)
555 SetPointer(rPointer
);
558 mpChildWindow
->SetPointer(rPointer
);
560 if (mxPlayerWindow
.is())
564 switch (rPointer
.GetStyle())
566 case PointerStyle::Cross
:
567 nPointer
= awt::SystemPointer::CROSS
;
569 case PointerStyle::Hand
:
570 nPointer
= awt::SystemPointer::HAND
;
572 case PointerStyle::Move
:
573 nPointer
= awt::SystemPointer::MOVE
;
575 case PointerStyle::Wait
:
576 nPointer
= awt::SystemPointer::WAIT
;
579 nPointer
= awt::SystemPointer::ARROW
;
583 mxPlayerWindow
->setPointerType(nPointer
);
587 void MediaWindowImpl::Resize()
589 const Size
aCurSize(GetOutputSizePixel());
590 const sal_Int32
nOffset(mpMediaWindowControl
? AVMEDIA_CONTROLOFFSET
: 0);
592 Size
aPlayerWindowSize(aCurSize
.Width() - (nOffset
<< 1),
593 aCurSize
.Height() - (nOffset
<< 1));
595 if (mpMediaWindowControl
)
597 const sal_Int32 nControlHeight
= mpMediaWindowControl
->GetSizePixel().Height();
598 const sal_Int32 nControlY
= ::std::max(aCurSize
.Height() - nControlHeight
- nOffset
, 0L);
600 aPlayerWindowSize
.Height() = (nControlY
- (nOffset
<< 1));
601 mpMediaWindowControl
->SetPosSizePixel(Point(nOffset
, nControlY
), Size(aCurSize
.Width() - (nOffset
<< 1), nControlHeight
));
604 mpChildWindow
->SetPosSizePixel(Point(0, 0), aPlayerWindowSize
);
606 if (mxPlayerWindow
.is())
607 mxPlayerWindow
->setPosSize(0, 0, aPlayerWindowSize
.Width(), aPlayerWindowSize
.Height(), 0);
610 void MediaWindowImpl::StateChanged(StateChangedType eType
)
612 if (mxPlayerWindow
.is())
614 // stop playing when going disabled or hidden
617 case StateChangedType::Visible
:
619 stopPlayingInternal(!IsVisible());
620 mxPlayerWindow
->setVisible(IsVisible());
624 case StateChangedType::Enable
:
626 stopPlayingInternal(!IsEnabled());
627 mxPlayerWindow
->setEnable(IsEnabled());
637 void MediaWindowImpl::Paint(vcl::RenderContext
& rRenderContext
, const Rectangle
&)
639 if (mxPlayerWindow
.is())
640 mxPlayerWindow
->update();
642 BitmapEx
* pLogo
= NULL
;
647 mpEmptyBmpEx
= new BitmapEx(AVMEDIA_RESID(AVMEDIA_BMP_EMPTYLOGO
));
649 pLogo
= mpEmptyBmpEx
;
651 else if (!mxPlayerWindow
.is())
654 mpAudioBmpEx
= new BitmapEx(AVMEDIA_RESID(AVMEDIA_BMP_AUDIOLOGO
));
656 pLogo
= mpAudioBmpEx
;
662 const Point
aBasePos(mpChildWindow
->GetPosPixel());
663 const Rectangle
aVideoRect(aBasePos
, mpChildWindow
->GetSizePixel());
665 if (pLogo
&& !pLogo
->IsEmpty() && (aVideoRect
.GetWidth() > 0) && (aVideoRect
.GetHeight() > 0))
667 Size
aLogoSize(pLogo
->GetSizePixel());
668 const Color
aBackgroundColor(67, 67, 67);
670 rRenderContext
.SetLineColor(aBackgroundColor
);
671 rRenderContext
.SetFillColor(aBackgroundColor
);
672 rRenderContext
.DrawRect(aVideoRect
);
674 if ((aLogoSize
.Width() > aVideoRect
.GetWidth() || aLogoSize
.Height() > aVideoRect
.GetHeight() ) &&
675 (aLogoSize
.Height() > 0))
677 const double fLogoWH
= double(aLogoSize
.Width()) / aLogoSize
.Height();
679 if (fLogoWH
< (double(aVideoRect
.GetWidth()) / aVideoRect
.GetHeight()))
681 aLogoSize
.Width() = long(aVideoRect
.GetHeight() * fLogoWH
);
682 aLogoSize
.Height() = aVideoRect
.GetHeight();
686 aLogoSize
.Width() = aVideoRect
.GetWidth();
687 aLogoSize
.Height()= long(aVideoRect
.GetWidth() / fLogoWH
);
691 Point
aPoint(aBasePos
.X() + ((aVideoRect
.GetWidth() - aLogoSize
.Width()) >> 1),
692 aBasePos
.Y() + ((aVideoRect
.GetHeight() - aLogoSize
.Height()) >> 1));
694 rRenderContext
.DrawBitmapEx(aPoint
, aLogoSize
, *pLogo
);
698 void MediaWindowImpl::GetFocus()
702 void MediaWindowImpl::MouseMove(const MouseEvent
& rMEvt
)
704 if (mpMediaWindow
&& mbEventTransparent
)
705 mpMediaWindow
->MouseMove(rMEvt
);
708 void MediaWindowImpl::MouseButtonDown(const MouseEvent
& rMEvt
)
710 if (mpMediaWindow
&& mbEventTransparent
)
711 mpMediaWindow
->MouseButtonDown(rMEvt
);
714 void MediaWindowImpl::MouseButtonUp(const MouseEvent
& rMEvt
)
716 if (mpMediaWindow
&& mbEventTransparent
)
717 mpMediaWindow
->MouseButtonUp(rMEvt
);
720 void MediaWindowImpl::KeyInput(const KeyEvent
& rKEvt
)
722 if (mpMediaWindow
&& mbEventTransparent
)
723 mpMediaWindow
->KeyInput(rKEvt
);
726 void MediaWindowImpl::KeyUp(const KeyEvent
& rKEvt
)
728 if (mpMediaWindow
&& mbEventTransparent
)
729 mpMediaWindow
->KeyUp(rKEvt
);
732 void MediaWindowImpl::Command(const CommandEvent
& rCEvt
)
734 if (mpMediaWindow
&& mbEventTransparent
)
735 mpMediaWindow
->Command(rCEvt
);
738 sal_Int8
MediaWindowImpl::AcceptDrop(const AcceptDropEvent
& rEvt
)
740 return (mpMediaWindow
&& mbEventTransparent
? mpMediaWindow
->AcceptDrop(rEvt
) : 0);
743 sal_Int8
MediaWindowImpl::ExecuteDrop(const ExecuteDropEvent
& rEvt
)
745 return (mpMediaWindow
&& mbEventTransparent
? mpMediaWindow
->ExecuteDrop(rEvt
) : 0);
748 void MediaWindowImpl::StartDrag(sal_Int8 nAction
, const Point
& rPosPixel
)
750 if (mpMediaWindow
&& mbEventTransparent
)
751 mpMediaWindow
->StartDrag(nAction
, rPosPixel
);
755 } // namespace avmedia
757 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */