bump product version to 4.1.6.2
[LibreOffice.git] / avmedia / source / gstreamer / gstplayer.cxx
blob14fecf3b5253a84986e639191e628242ff588835
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <math.h>
22 #include <rtl/string.hxx>
24 #include <vcl/syschild.hxx>
25 #include <vcl/sysdata.hxx>
27 #include "gstplayer.hxx"
28 #include "gstframegrabber.hxx"
29 #include "gstwindow.hxx"
31 #ifdef AVMEDIA_GST_0_10
32 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer_0_10"
33 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer_0_10"
34 #else
35 # include <gst/video/videooverlay.h>
36 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
37 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
38 #endif
40 #if !defined DBG
41 # if OSL_DEBUG_LEVEL > 2
42 #ifdef AVMEDIA_GST_0_10
43 # define AVVERSION "gst 0.10: "
44 #else
45 # define AVVERSION "gst 1.0: "
46 #endif
47 #define DBG(...) do { fprintf (stderr, "%s", AVVERSION); fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0);
48 # else
49 #define DBG(...)
50 # endif
51 #endif
53 using namespace ::com::sun::star;
55 namespace avmedia { namespace gstreamer {
57 // ----------------
58 // - Player -
59 // ----------------
61 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) :
62 GstPlayer_BASE( m_aMutex ),
63 mxMgr( rxMgr ),
64 mpPlaybin( NULL ),
65 mbFakeVideo (sal_False ),
66 mnUnmutedVolume( 0 ),
67 mbPlayPending ( false ),
68 mbMuted( false ),
69 mbLooping( false ),
70 mbInitialized( false ),
71 mnWindowID( 0 ),
72 mpXOverlay( NULL ),
73 mnDuration( 0 ),
74 mnWidth( 0 ),
75 mnHeight( 0 )
77 // Initialize GStreamer library
78 int argc = 1;
79 char name[] = "libreoffice";
80 char *arguments[] = { name };
81 char** argv = arguments;
82 GError* pError = NULL;
84 mbInitialized = gst_init_check( &argc, &argv, &pError );
86 DBG( "%p Player::Player", this );
88 if (pError != NULL)
90 // TODO: thow an exception?
91 DBG( "%p Player::Player error '%s'", this, pError->message );
92 g_error_free (pError);
96 // ------------------------------------------------------------------------------
98 Player::~Player()
100 DBG( "%p Player::~Player", this );
101 if( mbInitialized )
102 disposing();
105 void SAL_CALL Player::disposing()
107 ::osl::MutexGuard aGuard(m_aMutex);
109 stop();
111 DBG( "%p Player::disposing", this );
113 // Release the elements and pipeline
114 if( mbInitialized )
116 if( mpPlaybin )
118 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
119 g_object_unref( G_OBJECT( mpPlaybin ) );
121 mpPlaybin = NULL;
124 if( mpXOverlay ) {
125 g_object_unref( G_OBJECT ( mpXOverlay ) );
126 mpXOverlay = NULL;
131 // ------------------------------------------------------------------------------
133 static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
135 Player* pPlayer = static_cast<Player*>(data);
137 pPlayer->processMessage( message );
139 return TRUE;
142 static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
144 Player* pPlayer = static_cast<Player*>(data);
146 return pPlayer->processSyncMessage( message );
149 void Player::processMessage( GstMessage *message )
151 switch( GST_MESSAGE_TYPE( message ) ) {
152 case GST_MESSAGE_EOS:
153 gst_element_set_state( mpPlaybin, GST_STATE_READY );
154 mbPlayPending = false;
155 if (mbLooping)
156 start();
157 break;
158 case GST_MESSAGE_STATE_CHANGED:
159 if( message->src == GST_OBJECT( mpPlaybin ) ) {
160 GstState newstate, pendingstate;
162 gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate);
164 if( newstate == GST_STATE_PAUSED &&
165 pendingstate == GST_STATE_VOID_PENDING &&
166 mpXOverlay )
167 gst_video_overlay_expose( mpXOverlay );
169 if (mbPlayPending)
170 mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED));
172 default:
173 break;
177 static gboolean wrap_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
179 #ifdef AVMEDIA_GST_0_10
180 GstFormat my_format = format;
181 return gst_element_query_position( element, &my_format, cur) && my_format == format && *cur > 0L;
182 #else
183 return gst_element_query_position( element, format, cur );
184 #endif
187 static gboolean wrap_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
189 #ifdef AVMEDIA_GST_0_10
190 GstFormat my_format = format;
191 return gst_element_query_duration( element, &my_format, duration) && my_format == format && *duration > 0L;
192 #else
193 return gst_element_query_duration( element, format, duration );
194 #endif
197 GstBusSyncReply Player::processSyncMessage( GstMessage *message )
199 // DBG( "%p processSyncMessage has handle: %s", this, GST_MESSAGE_TYPE_NAME( message ) );
201 #if OSL_DEBUG_LEVEL > 0
202 if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
204 GError* error;
205 gchar* error_debug;
207 gst_message_parse_error( message, &error, &error_debug );
208 SAL_WARN(
209 "avmedia",
210 "gstreamer error: '" << error->message << "' debug: '"
211 << error_debug << "'");
213 #endif
215 #ifdef AVMEDIA_GST_0_10
216 if (message->structure &&
217 !strcmp( gst_structure_get_name( message->structure ), "prepare-xwindow-id" ) )
218 #else
219 if (gst_is_video_overlay_prepare_window_handle_message (message) )
220 #endif
222 DBG( "%p processSyncMessage prepare window id: %s %d", this,
223 GST_MESSAGE_TYPE_NAME( message ), (int)mnWindowID );
224 if( mpXOverlay )
225 g_object_unref( G_OBJECT ( mpXOverlay ) );
226 g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio", FALSE, NULL );
227 mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
228 g_object_ref( G_OBJECT ( mpXOverlay ) );
229 if ( mnWindowID != 0 )
230 gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
231 return GST_BUS_DROP;
234 #ifdef AVMEDIA_GST_0_10
235 if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_STATE_CHANGED ) {
236 if( message->src == GST_OBJECT( mpPlaybin ) ) {
237 GstState newstate, pendingstate;
239 gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate);
241 DBG( "%p state change received, new state %d pending %d", this,
242 (int)newstate, (int)pendingstate );
243 if( newstate == GST_STATE_PAUSED &&
244 pendingstate == GST_STATE_VOID_PENDING ) {
246 DBG( "%p change to paused received", this );
248 if( mnDuration == 0) {
249 gint64 gst_duration = 0L;
250 if( wrap_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
251 mnDuration = gst_duration;
254 if( mnWidth == 0 ) {
255 GList *pStreamInfo = NULL;
257 g_object_get( G_OBJECT( mpPlaybin ), "stream-info", &pStreamInfo, NULL );
259 for ( ; pStreamInfo != NULL; pStreamInfo = pStreamInfo->next) {
260 GObject *pInfo = G_OBJECT( pStreamInfo->data );
262 if( !pInfo )
263 continue;
265 int nType;
266 g_object_get( pInfo, "type", &nType, NULL );
267 GEnumValue *pValue = g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo ), "type" ) )->enum_class,
268 nType );
270 if( !g_ascii_strcasecmp( pValue->value_nick, "video" ) ) {
271 GstStructure *pStructure;
272 GstPad *pPad;
274 g_object_get( pInfo, "object", &pPad, NULL );
275 pStructure = gst_caps_get_structure( GST_PAD_CAPS( pPad ), 0 );
276 if( pStructure ) {
277 gst_structure_get_int( pStructure, "width", &mnWidth );
278 gst_structure_get_int( pStructure, "height", &mnHeight );
279 DBG( "queried size: %d x %d", mnWidth, mnHeight );
281 g_object_unref (pPad);
285 maSizeCondition.set();
289 #else
290 // We get to use the exciting new playbin2 ! (now known as playbin)
291 if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
292 if( mnDuration == 0) {
293 gint64 gst_duration = 0L;
294 if( wrap_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
295 mnDuration = gst_duration;
297 if( mnWidth == 0 ) {
298 GstPad *pad = NULL;
299 GstCaps *caps;
301 g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
303 if( pad ) {
304 int w = 0, h = 0;
306 caps = gst_pad_get_current_caps( pad );
308 if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
309 "width", G_TYPE_INT, &w,
310 "height", G_TYPE_INT, &h,
311 NULL ) ) {
312 mnWidth = w;
313 mnHeight = h;
315 DBG( "queried size: %d x %d", mnWidth, mnHeight );
317 maSizeCondition.set();
319 gst_caps_unref( caps );
320 g_object_unref( pad );
323 #endif
324 } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
325 DBG( "Error !\n" );
326 if( mnWidth == 0 ) {
327 // an error occurred, set condition so that OOo thread doesn't wait for us
328 maSizeCondition.set();
332 return GST_BUS_PASS;
335 void Player::preparePlaybin( const OUString& rURL, GstElement *pSink )
337 GstBus *pBus;
339 if( mpPlaybin != NULL ) {
340 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
341 mbPlayPending = false;
342 g_object_unref( mpPlaybin );
345 mpPlaybin = gst_element_factory_make( "playbin", NULL );
346 if( pSink != NULL ) // used for getting prefered size etc.
348 g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, NULL );
349 mbFakeVideo = true;
351 else
352 mbFakeVideo = false;
354 OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
355 g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , NULL );
357 pBus = gst_element_get_bus( mpPlaybin );
358 gst_bus_add_watch( pBus, pipeline_bus_callback, this );
359 DBG( "%p set sync handler", this );
360 #ifdef AVMEDIA_GST_0_10
361 gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this );
362 #else
363 gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, NULL );
364 #endif
365 g_object_unref( pBus );
368 bool Player::create( const OUString& rURL )
370 bool bRet = false;
372 // create all the elements and link them
374 DBG("create player, URL: %s", OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr());
376 if( mbInitialized && !rURL.isEmpty() )
378 // fakesink for pre-roll & sizing ...
379 preparePlaybin( rURL, gst_element_factory_make( "fakesink", NULL ) );
381 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
382 mbPlayPending = false;
384 bRet = true;
387 if( bRet )
388 maURL = rURL;
389 else
390 maURL = OUString();
392 return bRet;
395 // ------------------------------------------------------------------------------
397 void SAL_CALL Player::start()
398 throw (uno::RuntimeException)
400 ::osl::MutexGuard aGuard(m_aMutex);
402 // set the pipeline state to READY and run the loop
403 if( mbInitialized && NULL != mpPlaybin )
405 gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
406 mbPlayPending = true;
410 // ------------------------------------------------------------------------------
412 void SAL_CALL Player::stop()
413 throw (uno::RuntimeException)
415 ::osl::MutexGuard aGuard(m_aMutex);
417 // set the pipeline in PAUSED STATE
418 if( mpPlaybin )
419 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
421 mbPlayPending = false;
422 DBG( "stop %p", mpPlaybin );
425 // ------------------------------------------------------------------------------
427 sal_Bool SAL_CALL Player::isPlaying()
428 throw (uno::RuntimeException)
430 ::osl::MutexGuard aGuard(m_aMutex);
432 bool bRet = mbPlayPending;
434 // return whether the pipeline is in PLAYING STATE or not
435 if( !mbPlayPending && mbInitialized && mpPlaybin )
437 bRet = GST_STATE_PLAYING == GST_STATE( mpPlaybin );
440 DBG( "isPlaying %d", bRet );
442 return bRet;
445 // ------------------------------------------------------------------------------
447 double SAL_CALL Player::getDuration()
448 throw (uno::RuntimeException)
450 ::osl::MutexGuard aGuard(m_aMutex);
452 // slideshow checks for non-zero duration, so cheat here
453 double duration = 0.01;
455 if( mpPlaybin && mnDuration > 0 ) {
456 duration = mnDuration / 1E9;
459 return duration;
462 // ------------------------------------------------------------------------------
464 void SAL_CALL Player::setMediaTime( double fTime )
465 throw (uno::RuntimeException)
467 ::osl::MutexGuard aGuard(m_aMutex);
469 if( mpPlaybin ) {
470 gint64 gst_position = llround (fTime * 1E9);
472 gst_element_seek( mpPlaybin, 1.0,
473 GST_FORMAT_TIME,
474 GST_SEEK_FLAG_FLUSH,
475 GST_SEEK_TYPE_SET, gst_position,
476 GST_SEEK_TYPE_NONE, 0 );
477 if( !isPlaying() )
478 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
480 DBG( "seek to: %" SAL_PRIdINT64 " ns original: %lf s", gst_position, fTime );
484 // ------------------------------------------------------------------------------
486 double SAL_CALL Player::getMediaTime()
487 throw (uno::RuntimeException)
489 ::osl::MutexGuard aGuard(m_aMutex);
491 double position = 0.0;
493 if( mpPlaybin ) {
494 // get current position in the stream
495 gint64 gst_position;
496 if( wrap_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
497 position = gst_position / 1E9;
500 return position;
503 // ------------------------------------------------------------------------------
505 double SAL_CALL Player::getRate()
506 throw (uno::RuntimeException)
508 ::osl::MutexGuard aGuard(m_aMutex);
510 double rate = 1.0;
512 // TODO get the window rate - but no need since
513 // higher levels never set rate > 1
515 return rate;
518 // ------------------------------------------------------------------------------
520 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
521 throw (uno::RuntimeException)
523 ::osl::MutexGuard aGuard(m_aMutex);
524 // TODO check how to do with GST
525 mbLooping = bSet;
528 // ------------------------------------------------------------------------------
530 sal_Bool SAL_CALL Player::isPlaybackLoop()
531 throw (uno::RuntimeException)
533 ::osl::MutexGuard aGuard(m_aMutex);
534 // TODO check how to do with GST
535 return mbLooping;
538 // ------------------------------------------------------------------------------
540 void SAL_CALL Player::setMute( sal_Bool bSet )
541 throw (uno::RuntimeException)
543 ::osl::MutexGuard aGuard(m_aMutex);
545 DBG( "set mute: %d muted: %d unmuted volume: %lf", bSet, mbMuted, mnUnmutedVolume );
547 // change the volume to 0 or the unmuted volume
548 if( mpPlaybin && mbMuted != bSet )
550 double nVolume = mnUnmutedVolume;
551 if( bSet )
553 nVolume = 0.0;
556 g_object_set( G_OBJECT( mpPlaybin ), "volume", nVolume, NULL );
558 mbMuted = bSet;
562 // ------------------------------------------------------------------------------
564 sal_Bool SAL_CALL Player::isMute()
565 throw (uno::RuntimeException)
567 ::osl::MutexGuard aGuard(m_aMutex);
569 return mbMuted;
572 // ------------------------------------------------------------------------------
574 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
575 throw (uno::RuntimeException)
577 ::osl::MutexGuard aGuard(m_aMutex);
579 mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );
581 DBG( "set volume: %d gst volume: %lf", nVolumeDB, mnUnmutedVolume );
583 // change volume
584 if( !mbMuted && mpPlaybin )
586 g_object_set( G_OBJECT( mpPlaybin ), "volume", (gdouble) mnUnmutedVolume, NULL );
590 // ------------------------------------------------------------------------------
592 sal_Int16 SAL_CALL Player::getVolumeDB()
593 throw (uno::RuntimeException)
595 ::osl::MutexGuard aGuard(m_aMutex);
597 sal_Int16 nVolumeDB(0);
599 if( mpPlaybin ) {
600 double nGstVolume = 0.0;
602 g_object_get( G_OBJECT( mpPlaybin ), "volume", &nGstVolume, NULL );
604 nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) );
607 return nVolumeDB;
610 // ------------------------------------------------------------------------------
612 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
613 throw (uno::RuntimeException)
615 ::osl::MutexGuard aGuard(m_aMutex);
617 awt::Size aSize( 0, 0 );
619 if( maURL.isEmpty() )
621 DBG( "%p Player::getPreferredPlayerWindowSize - empty URL => 0x0", this );
622 return aSize;
625 DBG( "%p pre-Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth, mnHeight );
627 TimeValue aTimeout = { 10, 0 };
628 #if OSL_DEBUG_LEVEL > 2
629 osl::Condition::Result aResult =
630 #endif
631 maSizeCondition.wait( &aTimeout );
633 DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult, mnWidth, mnHeight );
635 if( mnWidth != 0 && mnHeight != 0 ) {
636 aSize.Width = mnWidth;
637 aSize.Height = mnHeight;
640 return aSize;
643 // ------------------------------------------------------------------------------
645 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
646 throw (uno::RuntimeException)
648 ::osl::MutexGuard aGuard(m_aMutex);
650 uno::Reference< ::media::XPlayerWindow > xRet;
651 awt::Size aSize( getPreferredPlayerWindowSize() );
653 if( mbFakeVideo )
654 preparePlaybin( maURL, NULL );
656 DBG( "Player::createPlayerWindow %d %d length: %d", aSize.Width, aSize.Height, rArguments.getLength() );
658 if( aSize.Width > 0 && aSize.Height > 0 )
660 ::avmedia::gstreamer::Window* pWindow = new ::avmedia::gstreamer::Window( mxMgr, *this );
662 xRet = pWindow;
664 if( rArguments.getLength() > 2 )
666 sal_IntPtr pIntPtr = 0;
667 rArguments[ 2 ] >>= pIntPtr;
668 SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
669 const SystemEnvData* pEnvData = pParentWindow ? pParentWindow->GetSystemData() : NULL;
670 OSL_ASSERT(pEnvData);
671 if (pEnvData)
673 mnWindowID = pEnvData->aWindow;
674 DBG( "set window id to %d XOverlay %p\n", (int)mnWindowID, mpXOverlay);
675 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
676 if ( mpXOverlay != NULL )
677 gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
682 return xRet;
685 // ------------------------------------------------------------------------------
687 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
688 throw (uno::RuntimeException)
690 ::osl::MutexGuard aGuard(m_aMutex);
691 FrameGrabber* pFrameGrabber = NULL;
692 const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
694 if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
695 pFrameGrabber = FrameGrabber::create( maURL );
696 DBG( "created FrameGrabber %p", pFrameGrabber );
698 return pFrameGrabber;
701 // ------------------------------------------------------------------------------
703 OUString SAL_CALL Player::getImplementationName()
704 throw (uno::RuntimeException)
706 return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME );
709 // ------------------------------------------------------------------------------
711 sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
712 throw (uno::RuntimeException)
714 return ServiceName == AVMEDIA_GST_PLAYER_SERVICENAME;
717 // ------------------------------------------------------------------------------
719 uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
720 throw (uno::RuntimeException)
722 uno::Sequence< OUString > aRet(1);
723 aRet[0] = AVMEDIA_GST_PLAYER_SERVICENAME ;
725 return aRet;
728 } // namespace gstreamer
729 } // namespace avmedia
731 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */