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 mpFrameMutex( g_mutex_new() ), 49 mpFrameCond( g_cond_new() ), 50 mpLastPixbuf( NULL ), 51 mbIsInGrabMode( false ) 52 { 53 if( pURI ) 54 { 55 OSL_TRACE( ">>> --------------------------------" ); 56 OSL_TRACE( ">>> Creating Player object with URL: %s", pURI->str ); 57 58 mpThread = g_thread_create( Player::implThreadFunc, this, true, NULL ); 59 } 60 } 61 62 // ------------------------------------------------------------------------------ 63 64 FrameGrabber::~FrameGrabber() 65 { 66 if( g_atomic_pointer_get( &mpPlayer ) ) 67 { 68 implQuitThread(); 69 } 70 71 // thread has ended, so that no more synchronization is necessary 72 if( mpLastPixbuf ) 73 { 74 g_object_unref( mpLastPixbuf ); 75 mpLastPixbuf = NULL; 76 } 77 78 g_cond_free( mpFrameCond ); 79 g_mutex_free( mpFrameMutex ); 80 } 81 82 // ------------------------------------------------------------------------------ 83 84 FrameGrabber* FrameGrabber::create( const GString* pURI ) 85 { 86 FrameGrabber* pFrameGrabber = NULL; 87 88 if( pURI && pURI->len ) 89 { 90 // safely initialize GLib threading framework 91 try 92 { 93 if( !g_thread_supported() ) 94 { 95 g_thread_init( NULL ); 96 } 97 } 98 catch( ... ) 99 {} 100 101 if( g_thread_supported() ) 102 { 103 pFrameGrabber = new FrameGrabber( g_string_new( pURI->str ) ); 104 105 // wait until thread signals that it has finished initialization 106 if( pFrameGrabber->mpThread ) 107 { 108 g_mutex_lock( pFrameGrabber->mpMutex ); 109 110 while( !pFrameGrabber->implIsInitialized() ) 111 { 112 g_cond_wait( pFrameGrabber->mpCond, pFrameGrabber->mpMutex ); 113 } 114 115 g_mutex_unlock( pFrameGrabber->mpMutex ); 116 } 117 118 GstElement* pPixbufSink = gst_element_factory_make( "gdkpixbufsink", NULL ); 119 120 // check if player pipeline and GdkPixbufSink could be initialized 121 if( !pFrameGrabber->mpPlayer || !pPixbufSink ) 122 { 123 delete pFrameGrabber; 124 pFrameGrabber = NULL; 125 } 126 else 127 { 128 g_object_set( pFrameGrabber->mpPlayer, "audio-sink", gst_element_factory_make( "fakesink", NULL ), NULL ); 129 g_object_set( pFrameGrabber->mpPlayer, "video-sink", pPixbufSink, NULL ); 130 } 131 } 132 } 133 134 return( pFrameGrabber ); 135 } 136 137 // ------------------------------------------------------------------------------ 138 139 gboolean FrameGrabber::busCallback( GstBus* pBus, GstMessage* pMsg ) 140 { 141 bool bDone = false; 142 143 if( pMsg && pMsg->structure ) 144 { 145 GstStructure* pStruct = pMsg->structure; 146 const gchar* pStructName = gst_structure_get_name( pStruct ); 147 148 if( ( ::std::string( pStructName ).find( "pixbuf" ) != ::std::string::npos ) && 149 gst_structure_has_field ( pStruct, "pixbuf") ) 150 { 151 bool bFrameGrabbed = false; 152 153 g_mutex_lock( mpFrameMutex ); 154 155 if( mbIsInGrabMode && ( getMediaTime() >= mfGrabTime ) ) 156 { 157 OSL_TRACE( "Grabbing frame at %fs", getMediaTime() ); 158 159 if( mpLastPixbuf ) 160 { 161 g_object_unref( mpLastPixbuf ); 162 mpLastPixbuf = NULL; 163 } 164 165 mpLastPixbuf = GDK_PIXBUF( g_value_dup_object( gst_structure_get_value( pStruct, "pixbuf" ) ) ); 166 bFrameGrabbed = true; 167 } 168 169 g_mutex_unlock( mpFrameMutex ); 170 171 if( bFrameGrabbed ) 172 { 173 g_cond_signal( mpFrameCond ); 174 } 175 176 bDone = true; 177 } 178 } 179 180 return( bDone || Player::busCallback( pBus, pMsg ) ); 181 } 182 183 // ------------------------------------------------------------------------------ 184 185 uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime ) 186 throw (uno::RuntimeException) 187 { 188 uno::Reference< graphic::XGraphic > xRet; 189 190 if( implInitPlayer() ) 191 { 192 OSL_TRACE( "Trying to grab frame at %fs", fMediaTime ); 193 194 GTimeVal aTimeoutTime; 195 196 g_get_current_time( &aTimeoutTime ); 197 g_time_val_add( &aTimeoutTime, GRAB_TIMEOUT ); 198 setMediaTime( fMediaTime ); 199 start(); 200 201 if( isPlaying() ) 202 { 203 g_mutex_lock( mpFrameMutex ); 204 205 mbIsInGrabMode = true; 206 mfGrabTime = fMediaTime; 207 g_cond_timed_wait( mpFrameCond, mpFrameMutex, &aTimeoutTime ); 208 mbIsInGrabMode = false; 209 210 g_mutex_unlock( mpFrameMutex ); 211 212 stop(); 213 } 214 215 OSL_ENSURE( g_atomic_pointer_get( &mpLastPixbuf ), "FrameGrabber timed out without receiving a Pixbuf" ); 216 217 if( g_atomic_pointer_get( &mpLastPixbuf ) ) 218 { 219 OSL_TRACE( "FrameGrabber received a GdkPixbuf"); 220 221 g_mutex_lock( mpFrameMutex ); 222 223 const int nWidth = gdk_pixbuf_get_width( mpLastPixbuf ); 224 const int nHeight = gdk_pixbuf_get_height( mpLastPixbuf ); 225 const int nChannels = gdk_pixbuf_get_n_channels( mpLastPixbuf ); 226 const guchar* pBuffer = gdk_pixbuf_get_pixels( mpLastPixbuf ); 227 228 if( pBuffer && ( nWidth > 0 ) && ( nHeight > 0 ) ) 229 { 230 Bitmap aFrame( Size( nWidth, nHeight), 24 ); 231 bool bInit = false; 232 233 if( ( gdk_pixbuf_get_colorspace( mpLastPixbuf ) == GDK_COLORSPACE_RGB ) && 234 ( nChannels >= 3 ) && ( nChannels <= 4 ) && 235 ( gdk_pixbuf_get_bits_per_sample( mpLastPixbuf ) == 8 ) ) 236 { 237 BitmapWriteAccess* pAcc = aFrame.AcquireWriteAccess(); 238 239 if( pAcc ) 240 { 241 BitmapColor aPixel( 0, 0, 0 ); 242 const int nRowStride = gdk_pixbuf_get_rowstride( mpLastPixbuf ); 243 const bool bAlpha = ( nChannels == 4 ); 244 245 for( int nRow = 0; nRow < nHeight; ++nRow ) 246 { 247 guchar* pCur = const_cast< guchar* >( pBuffer + nRow * nRowStride ); 248 249 for( int nCol = 0; nCol < nWidth; ++nCol ) 250 { 251 aPixel.SetRed( *pCur++ ); 252 aPixel.SetGreen( *pCur++ ); 253 aPixel.SetBlue( *pCur++ ); 254 255 // ignore alpha channel 256 if( bAlpha ) 257 { 258 ++pCur; 259 } 260 261 pAcc->SetPixel( nRow, nCol, aPixel ); 262 } 263 } 264 265 aFrame.ReleaseAccess( pAcc ); 266 bInit = true; 267 } 268 } 269 270 if( !bInit ) 271 { 272 aFrame.Erase( Color( COL_BLACK ) ); 273 } 274 275 xRet = Graphic( aFrame ).GetXGraphic(); 276 } 277 278 g_object_unref( mpLastPixbuf ); 279 mpLastPixbuf = NULL; 280 281 g_mutex_unlock( mpFrameMutex ); 282 } 283 } 284 285 return xRet; 286 } 287 288 // ------------------------------------------------------------------------------ 289 290 ::rtl::OUString SAL_CALL FrameGrabber::getImplementationName( ) 291 throw (uno::RuntimeException) 292 { 293 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_FRAMEGRABBER_IMPLEMENTATIONNAME ) ); 294 } 295 296 // ------------------------------------------------------------------------------ 297 298 sal_Bool SAL_CALL FrameGrabber::supportsService( const ::rtl::OUString& ServiceName ) 299 throw (uno::RuntimeException) 300 { 301 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 302 } 303 304 // ------------------------------------------------------------------------------ 305 306 uno::Sequence< ::rtl::OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( ) 307 throw (uno::RuntimeException) 308 { 309 uno::Sequence< ::rtl::OUString > aRet(1); 310 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 311 312 return aRet; 313 } 314 315 } // namespace win 316 } // namespace avmedia 317