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 && gst_message_get_structure( pMsg ) ) 134 { 135 const GstStructure* pStruct = gst_message_get_structure( pMsg ); 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 { 177 uno::Reference< graphic::XGraphic > xRet; 178 179 if( implInitPlayer() ) 180 { 181 OSL_TRACE( "Trying to grab frame at %fs", fMediaTime ); 182 183 GTimeVal aTimeoutTime; 184 185 g_get_current_time( &aTimeoutTime ); 186 g_time_val_add( &aTimeoutTime, GRAB_TIMEOUT ); 187 setMediaTime( fMediaTime ); 188 start(); 189 190 if( isPlaying() ) 191 { 192 g_mutex_lock( mpFrameMutex ); 193 194 mbIsInGrabMode = true; 195 mfGrabTime = fMediaTime; 196 g_cond_timed_wait( mpFrameCond, mpFrameMutex, &aTimeoutTime ); 197 mbIsInGrabMode = false; 198 199 g_mutex_unlock( mpFrameMutex ); 200 201 stop(); 202 } 203 204 OSL_ENSURE( g_atomic_pointer_get( &mpLastPixbuf ), "FrameGrabber timed out without receiving a Pixbuf" ); 205 206 if( g_atomic_pointer_get( &mpLastPixbuf ) ) 207 { 208 OSL_TRACE( "FrameGrabber received a GdkPixbuf"); 209 210 g_mutex_lock( mpFrameMutex ); 211 212 const int nWidth = gdk_pixbuf_get_width( mpLastPixbuf ); 213 const int nHeight = gdk_pixbuf_get_height( mpLastPixbuf ); 214 const int nChannels = gdk_pixbuf_get_n_channels( mpLastPixbuf ); 215 const guchar* pBuffer = gdk_pixbuf_get_pixels( mpLastPixbuf ); 216 217 if( pBuffer && ( nWidth > 0 ) && ( nHeight > 0 ) ) 218 { 219 Bitmap aFrame( Size( nWidth, nHeight), 24 ); 220 bool bInit = false; 221 222 if( ( gdk_pixbuf_get_colorspace( mpLastPixbuf ) == GDK_COLORSPACE_RGB ) && 223 ( nChannels >= 3 ) && ( nChannels <= 4 ) && 224 ( gdk_pixbuf_get_bits_per_sample( mpLastPixbuf ) == 8 ) ) 225 { 226 BitmapWriteAccess* pAcc = aFrame.AcquireWriteAccess(); 227 228 if( pAcc ) 229 { 230 BitmapColor aPixel( 0, 0, 0 ); 231 const int nRowStride = gdk_pixbuf_get_rowstride( mpLastPixbuf ); 232 const bool bAlpha = ( nChannels == 4 ); 233 234 for( int nRow = 0; nRow < nHeight; ++nRow ) 235 { 236 guchar* pCur = const_cast< guchar* >( pBuffer + nRow * nRowStride ); 237 238 for( int nCol = 0; nCol < nWidth; ++nCol ) 239 { 240 aPixel.SetRed( *pCur++ ); 241 aPixel.SetGreen( *pCur++ ); 242 aPixel.SetBlue( *pCur++ ); 243 244 // ignore alpha channel 245 if( bAlpha ) 246 { 247 ++pCur; 248 } 249 250 pAcc->SetPixel( nRow, nCol, aPixel ); 251 } 252 } 253 254 aFrame.ReleaseAccess( pAcc ); 255 bInit = true; 256 } 257 } 258 259 if( !bInit ) 260 { 261 aFrame.Erase( Color( COL_BLACK ) ); 262 } 263 264 xRet = Graphic( aFrame ).GetXGraphic(); 265 } 266 267 g_object_unref( mpLastPixbuf ); 268 mpLastPixbuf = NULL; 269 270 g_mutex_unlock( mpFrameMutex ); 271 } 272 } 273 274 return xRet; 275 } 276 277 // ------------------------------------------------------------------------------ 278 279 ::rtl::OUString SAL_CALL FrameGrabber::getImplementationName( ) 280 { 281 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_FRAMEGRABBER_IMPLEMENTATIONNAME ) ); 282 } 283 284 // ------------------------------------------------------------------------------ 285 286 sal_Bool SAL_CALL FrameGrabber::supportsService( const ::rtl::OUString& ServiceName ) 287 { 288 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 289 } 290 291 // ------------------------------------------------------------------------------ 292 293 uno::Sequence< ::rtl::OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( ) 294 { 295 uno::Sequence< ::rtl::OUString > aRet(1); 296 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_GSTREAMER_FRAMEGRABBER_SERVICENAME ) ); 297 298 return aRet; 299 } 300 301 } // namespace win 302 } // namespace avmedia 303