1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include "gstframegrabber.hxx" 29 #include "gstplayer.hxx" 30 31 #include <vcl/graph.hxx> 32 #include <vcl/bmpacc.hxx> 33 34 #include <string> 35 36 37 using namespace ::com::sun::star; 38 39 namespace avmedia { namespace gst { 40 41 const gulong GRAB_TIMEOUT = 10000000; 42 43 // ---------------- 44 // - FrameGrabber - 45 // ---------------- 46 47 FrameGrabber::FrameGrabber( GString* pURI ) : 48 FrameGrabber_BASE(pURI), 49 mpFrameMutex( g_mutex_new() ), 50 mpFrameCond( g_cond_new() ), 51 mpLastPixbuf( NULL ), 52 mbIsInGrabMode( false ) 53 { 54 } 55 56 // ------------------------------------------------------------------------------ 57 58 FrameGrabber::~FrameGrabber() 59 { 60 if( g_atomic_pointer_get( &mpPlayer ) ) 61 { 62 implQuitThread(); 63 } 64 65 // thread has ended, so that no more synchronization is necessary 66 if( mpLastPixbuf ) 67 { 68 g_object_unref( mpLastPixbuf ); 69 mpLastPixbuf = NULL; 70 } 71 72 g_cond_free( mpFrameCond ); 73 g_mutex_free( mpFrameMutex ); 74 } 75 76 // ------------------------------------------------------------------------------ 77 78 FrameGrabber* FrameGrabber::create( const GString* pURI ) 79 { 80 FrameGrabber* pFrameGrabber = NULL; 81 82 if( pURI && pURI->len ) 83 { 84 // safely initialize GLib threading framework 85 try 86 { 87 if( !g_thread_supported() ) 88 { 89 g_thread_init( NULL ); 90 } 91 } 92 catch( ... ) 93 {} 94 95 if( g_thread_supported() ) 96 { 97 pFrameGrabber = new FrameGrabber( g_string_new( pURI->str ) ); 98 99 // wait until thread signals that it has finished initialization 100 if( pFrameGrabber->mpThread ) 101 { 102 g_mutex_lock( pFrameGrabber->mpMutex ); 103 104 while( !pFrameGrabber->implIsInitialized() ) 105 { 106 g_cond_wait( pFrameGrabber->mpCond, pFrameGrabber->mpMutex ); 107 } 108 109 g_mutex_unlock( pFrameGrabber->mpMutex ); 110 } 111 112 GstElement* pPixbufSink = gst_element_factory_make( "gdkpixbufsink", NULL ); 113 114 // check if player pipeline and GdkPixbufSink could be initialized 115 if( !pFrameGrabber->mpPlayer || !pPixbufSink ) 116 { 117 delete pFrameGrabber; 118 pFrameGrabber = NULL; 119 } 120 else 121 { 122 g_object_set( pFrameGrabber->mpPlayer, "audio-sink", gst_element_factory_make( "fakesink", NULL ), NULL ); 123 g_object_set( pFrameGrabber->mpPlayer, "video-sink", pPixbufSink, NULL ); 124 } 125 } 126 } 127 128 return( pFrameGrabber ); 129 } 130 131 // ------------------------------------------------------------------------------ 132 133 gboolean FrameGrabber::busCallback( GstBus* pBus, GstMessage* pMsg ) 134 { 135 bool bDone = false; 136 137 if( pMsg && pMsg->structure ) 138 { 139 GstStructure* pStruct = pMsg->structure; 140 const gchar* pStructName = gst_structure_get_name( pStruct ); 141 142 if( ( ::std::string( pStructName ).find( "pixbuf" ) != ::std::string::npos ) && 143 gst_structure_has_field ( pStruct, "pixbuf") ) 144 { 145 bool bFrameGrabbed = false; 146 147 g_mutex_lock( mpFrameMutex ); 148 149 if( mbIsInGrabMode && ( getMediaTime() >= mfGrabTime ) ) 150 { 151 OSL_TRACE( "Grabbing frame at %fs", getMediaTime() ); 152 153 if( mpLastPixbuf ) 154 { 155 g_object_unref( mpLastPixbuf ); 156 mpLastPixbuf = NULL; 157 } 158 159 mpLastPixbuf = GDK_PIXBUF( g_value_dup_object( gst_structure_get_value( pStruct, "pixbuf" ) ) ); 160 bFrameGrabbed = true; 161 } 162 163 g_mutex_unlock( mpFrameMutex ); 164 165 if( bFrameGrabbed ) 166 { 167 g_cond_signal( mpFrameCond ); 168 } 169 170 bDone = true; 171 } 172 } 173 174 return( bDone || Player::busCallback( pBus, pMsg ) ); 175 } 176 177 // ------------------------------------------------------------------------------ 178 179 uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime ) 180 throw (uno::RuntimeException) 181 { 182 uno::Reference< graphic::XGraphic > xRet; 183 184 if( implInitPlayer() ) 185 { 186 OSL_TRACE( "Trying to grab frame at %fs", fMediaTime ); 187 188 GTimeVal aTimeoutTime; 189 190 g_get_current_time( &aTimeoutTime ); 191 g_time_val_add( &aTimeoutTime, GRAB_TIMEOUT ); 192 setMediaTime( fMediaTime ); 193 start(); 194 195 if( isPlaying() ) 196 { 197 g_mutex_lock( mpFrameMutex ); 198 199 mbIsInGrabMode = true; 200 mfGrabTime = fMediaTime; 201 g_cond_timed_wait( mpFrameCond, mpFrameMutex, &aTimeoutTime ); 202 mbIsInGrabMode = false; 203 204 g_mutex_unlock( mpFrameMutex ); 205 206 stop(); 207 } 208 209 OSL_ENSURE( g_atomic_pointer_get( &mpLastPixbuf ), "FrameGrabber timed out without receiving a Pixbuf" ); 210 211 if( g_atomic_pointer_get( &mpLastPixbuf ) ) 212 { 213 OSL_TRACE( "FrameGrabber received a GdkPixbuf"); 214 215 g_mutex_lock( mpFrameMutex ); 216 217 const int nWidth = gdk_pixbuf_get_width( mpLastPixbuf ); 218 const int nHeight = gdk_pixbuf_get_height( mpLastPixbuf ); 219 const int nChannels = gdk_pixbuf_get_n_channels( mpLastPixbuf ); 220 const guchar* pBuffer = gdk_pixbuf_get_pixels( mpLastPixbuf ); 221 222 if( pBuffer && ( nWidth > 0 ) && ( nHeight > 0 ) ) 223 { 224 Bitmap aFrame( Size( nWidth, nHeight), 24 ); 225 bool bInit = false; 226 227 if( ( gdk_pixbuf_get_colorspace( mpLastPixbuf ) == GDK_COLORSPACE_RGB ) && 228 ( nChannels >= 3 ) && ( nChannels <= 4 ) && 229 ( gdk_pixbuf_get_bits_per_sample( mpLastPixbuf ) == 8 ) ) 230 { 231 BitmapWriteAccess* pAcc = aFrame.AcquireWriteAccess(); 232 233 if( pAcc ) 234 { 235 BitmapColor aPixel( 0, 0, 0 ); 236 const int nRowStride = gdk_pixbuf_get_rowstride( mpLastPixbuf ); 237 const bool bAlpha = ( nChannels == 4 ); 238 239 for( int nRow = 0; nRow < nHeight; ++nRow ) 240 { 241 guchar* pCur = const_cast< guchar* >( pBuffer + nRow * nRowStride ); 242 243 for( int nCol = 0; nCol < nWidth; ++nCol ) 244 { 245 aPixel.SetRed( *pCur++ ); 246 aPixel.SetGreen( *pCur++ ); 247 aPixel.SetBlue( *pCur++ ); 248 249 // ignore alpha channel 250 if( bAlpha ) 251 { 252 ++pCur; 253 } 254 255 pAcc->SetPixel( nRow, nCol, aPixel ); 256 } 257 } 258 259 aFrame.ReleaseAccess( pAcc ); 260 bInit = true; 261 } 262 } 263 264 if( !bInit ) 265 { 266 aFrame.Erase( Color( COL_BLACK ) ); 267 } 268 269 xRet = Graphic( aFrame ).GetXGraphic(); 270 } 271 272 g_object_unref( mpLastPixbuf ); 273 mpLastPixbuf = NULL; 274 275 g_mutex_unlock( mpFrameMutex ); 276 } 277 } 278 279 return xRet; 280 } 281 282 // ------------------------------------------------------------------------------ 283 284 ::rtl::OUString SAL_CALL FrameGrabber::getImplementationName( ) 285 throw (uno::RuntimeException) 286 { 287 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_FRAMEGRABBER_IMPLEMENTATIONNAME ) ); 288 } 289 290 // ------------------------------------------------------------------------------ 291 292 sal_Bool SAL_CALL FrameGrabber::supportsService( const ::rtl::OUString& ServiceName ) 293 throw (uno::RuntimeException) 294 { 295 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 296 } 297 298 // ------------------------------------------------------------------------------ 299 300 uno::Sequence< ::rtl::OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( ) 301 throw (uno::RuntimeException) 302 { 303 uno::Sequence< ::rtl::OUString > aRet(1); 304 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 305 306 return aRet; 307 } 308 309 } // namespace win 310 } // namespace avmedia 311