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 
FrameGrabber(GString * pURI)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 
~FrameGrabber()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 
create(const GString * pURI)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 
busCallback(GstBus * pBus,GstMessage * pMsg)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 
grabFrame(double fMediaTime)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 
getImplementationName()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 
supportsService(const::rtl::OUString & ServiceName)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 
getSupportedServiceNames()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