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