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