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