blob: 5338c72dbc2a511b5ce998cc14be654d4bcacb0d [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "gstframegrabber.hxx"
#include "gstplayer.hxx"
#include <vcl/graph.hxx>
#include <vcl/bmpacc.hxx>
#include <string>
using namespace ::com::sun::star;
namespace avmedia { namespace gst {
const gulong GRAB_TIMEOUT = 10000000;
// ----------------
// - FrameGrabber -
// ----------------
FrameGrabber::FrameGrabber( GString* pURI ) :
FrameGrabber_BASE(pURI),
mpFrameMutex( g_mutex_new() ),
mpFrameCond( g_cond_new() ),
mpLastPixbuf( NULL ),
mbIsInGrabMode( false )
{
}
// ------------------------------------------------------------------------------
FrameGrabber::~FrameGrabber()
{
if( g_atomic_pointer_get( &mpPlayer ) )
{
implQuitThread();
}
// thread has ended, so that no more synchronization is necessary
if( mpLastPixbuf )
{
g_object_unref( mpLastPixbuf );
mpLastPixbuf = NULL;
}
g_cond_free( mpFrameCond );
g_mutex_free( mpFrameMutex );
}
// ------------------------------------------------------------------------------
FrameGrabber* FrameGrabber::create( const GString* pURI )
{
FrameGrabber* pFrameGrabber = NULL;
if( pURI && pURI->len )
{
// safely initialize GLib threading framework
try
{
if( !g_thread_supported() )
{
g_thread_init( NULL );
}
}
catch( ... )
{}
if( g_thread_supported() )
{
pFrameGrabber = new FrameGrabber( g_string_new( pURI->str ) );
// wait until thread signals that it has finished initialization
if( pFrameGrabber->mpThread )
{
g_mutex_lock( pFrameGrabber->mpMutex );
while( !pFrameGrabber->implIsInitialized() )
{
g_cond_wait( pFrameGrabber->mpCond, pFrameGrabber->mpMutex );
}
g_mutex_unlock( pFrameGrabber->mpMutex );
}
GstElement* pPixbufSink = gst_element_factory_make( "gdkpixbufsink", NULL );
// check if player pipeline and GdkPixbufSink could be initialized
if( !pFrameGrabber->mpPlayer || !pPixbufSink )
{
delete pFrameGrabber;
pFrameGrabber = NULL;
}
else
{
g_object_set( pFrameGrabber->mpPlayer, "audio-sink", gst_element_factory_make( "fakesink", NULL ), NULL );
g_object_set( pFrameGrabber->mpPlayer, "video-sink", pPixbufSink, NULL );
}
}
}
return( pFrameGrabber );
}
// ------------------------------------------------------------------------------
gboolean FrameGrabber::busCallback( GstBus* pBus, GstMessage* pMsg )
{
bool bDone = false;
if( pMsg && pMsg->structure )
{
GstStructure* pStruct = pMsg->structure;
const gchar* pStructName = gst_structure_get_name( pStruct );
if( ( ::std::string( pStructName ).find( "pixbuf" ) != ::std::string::npos ) &&
gst_structure_has_field ( pStruct, "pixbuf") )
{
bool bFrameGrabbed = false;
g_mutex_lock( mpFrameMutex );
if( mbIsInGrabMode && ( getMediaTime() >= mfGrabTime ) )
{
OSL_TRACE( "Grabbing frame at %fs", getMediaTime() );
if( mpLastPixbuf )
{
g_object_unref( mpLastPixbuf );
mpLastPixbuf = NULL;
}
mpLastPixbuf = GDK_PIXBUF( g_value_dup_object( gst_structure_get_value( pStruct, "pixbuf" ) ) );
bFrameGrabbed = true;
}
g_mutex_unlock( mpFrameMutex );
if( bFrameGrabbed )
{
g_cond_signal( mpFrameCond );
}
bDone = true;
}
}
return( bDone || Player::busCallback( pBus, pMsg ) );
}
// ------------------------------------------------------------------------------
uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
throw (uno::RuntimeException)
{
uno::Reference< graphic::XGraphic > xRet;
if( implInitPlayer() )
{
OSL_TRACE( "Trying to grab frame at %fs", fMediaTime );
GTimeVal aTimeoutTime;
g_get_current_time( &aTimeoutTime );
g_time_val_add( &aTimeoutTime, GRAB_TIMEOUT );
setMediaTime( fMediaTime );
start();
if( isPlaying() )
{
g_mutex_lock( mpFrameMutex );
mbIsInGrabMode = true;
mfGrabTime = fMediaTime;
g_cond_timed_wait( mpFrameCond, mpFrameMutex, &aTimeoutTime );
mbIsInGrabMode = false;
g_mutex_unlock( mpFrameMutex );
stop();
}
OSL_ENSURE( g_atomic_pointer_get( &mpLastPixbuf ), "FrameGrabber timed out without receiving a Pixbuf" );
if( g_atomic_pointer_get( &mpLastPixbuf ) )
{
OSL_TRACE( "FrameGrabber received a GdkPixbuf");
g_mutex_lock( mpFrameMutex );
const int nWidth = gdk_pixbuf_get_width( mpLastPixbuf );
const int nHeight = gdk_pixbuf_get_height( mpLastPixbuf );
const int nChannels = gdk_pixbuf_get_n_channels( mpLastPixbuf );
const guchar* pBuffer = gdk_pixbuf_get_pixels( mpLastPixbuf );
if( pBuffer && ( nWidth > 0 ) && ( nHeight > 0 ) )
{
Bitmap aFrame( Size( nWidth, nHeight), 24 );
bool bInit = false;
if( ( gdk_pixbuf_get_colorspace( mpLastPixbuf ) == GDK_COLORSPACE_RGB ) &&
( nChannels >= 3 ) && ( nChannels <= 4 ) &&
( gdk_pixbuf_get_bits_per_sample( mpLastPixbuf ) == 8 ) )
{
BitmapWriteAccess* pAcc = aFrame.AcquireWriteAccess();
if( pAcc )
{
BitmapColor aPixel( 0, 0, 0 );
const int nRowStride = gdk_pixbuf_get_rowstride( mpLastPixbuf );
const bool bAlpha = ( nChannels == 4 );
for( int nRow = 0; nRow < nHeight; ++nRow )
{
guchar* pCur = const_cast< guchar* >( pBuffer + nRow * nRowStride );
for( int nCol = 0; nCol < nWidth; ++nCol )
{
aPixel.SetRed( *pCur++ );
aPixel.SetGreen( *pCur++ );
aPixel.SetBlue( *pCur++ );
// ignore alpha channel
if( bAlpha )
{
++pCur;
}
pAcc->SetPixel( nRow, nCol, aPixel );
}
}
aFrame.ReleaseAccess( pAcc );
bInit = true;
}
}
if( !bInit )
{
aFrame.Erase( Color( COL_BLACK ) );
}
xRet = Graphic( aFrame ).GetXGraphic();
}
g_object_unref( mpLastPixbuf );
mpLastPixbuf = NULL;
g_mutex_unlock( mpFrameMutex );
}
}
return xRet;
}
// ------------------------------------------------------------------------------
::rtl::OUString SAL_CALL FrameGrabber::getImplementationName( )
throw (uno::RuntimeException)
{
return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_FRAMEGRABBER_IMPLEMENTATIONNAME ) );
}
// ------------------------------------------------------------------------------
sal_Bool SAL_CALL FrameGrabber::supportsService( const ::rtl::OUString& ServiceName )
throw (uno::RuntimeException)
{
return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) );
}
// ------------------------------------------------------------------------------
uno::Sequence< ::rtl::OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( )
throw (uno::RuntimeException)
{
uno::Sequence< ::rtl::OUString > aRet(1);
aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) );
return aRet;
}
} // namespace win
} // namespace avmedia