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