Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <sal/config.h>
21 :
22 : #include <algorithm>
23 : #include <cassert>
24 : #include <cstddef>
25 : #include <cstring>
26 : #include <map>
27 : #include <set>
28 : #include <vector>
29 : #include <math.h>
30 :
31 : #include <cppuhelper/supportsservice.hxx>
32 :
33 : #include <rtl/string.hxx>
34 : #include <salhelper/thread.hxx>
35 : #include <vcl/svapp.hxx>
36 : #include <vcl/syschild.hxx>
37 : #include <vcl/sysdata.hxx>
38 :
39 : #include "gstplayer.hxx"
40 : #include "gstframegrabber.hxx"
41 : #include "gstwindow.hxx"
42 :
43 : #ifdef AVMEDIA_GST_0_10
44 : # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer_0_10"
45 : # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer_0_10"
46 : #else
47 : # include <gst/video/videooverlay.h>
48 : # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
49 : # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
50 : #endif
51 :
52 : #include <gst/pbutils/missing-plugins.h>
53 : #include <gst/pbutils/pbutils.h>
54 :
55 : #if !defined DBG
56 : # if OSL_DEBUG_LEVEL > 2
57 : #ifdef AVMEDIA_GST_0_10
58 : # define AVVERSION "gst 0.10: "
59 : #else
60 : # define AVVERSION "gst 1.0: "
61 : #endif
62 : #define DBG(...) do { fprintf (stderr, "%s", AVVERSION); fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0);
63 : # else
64 : #define DBG(...)
65 : # endif
66 : #endif
67 :
68 : using namespace ::com::sun::star;
69 :
70 : namespace avmedia { namespace gstreamer {
71 :
72 : namespace {
73 :
74 : class FlagGuard {
75 : public:
76 0 : explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }
77 :
78 0 : ~FlagGuard() { flag_ = false; }
79 :
80 : private:
81 : bool & flag_;
82 : };
83 :
84 0 : class MissingPluginInstallerThread: public salhelper::Thread {
85 : public:
86 0 : MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
87 :
88 : private:
89 : void execute() SAL_OVERRIDE;
90 : };
91 :
92 : class MissingPluginInstaller {
93 : friend class MissingPluginInstallerThread;
94 :
95 : public:
96 0 : MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
97 :
98 : ~MissingPluginInstaller();
99 :
100 : void report(rtl::Reference<Player> const & source, GstMessage * message);
101 :
102 : // Player::~Player calls Player::disposing calls
103 : // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
104 : // here (which would bump its refcount back from 0 to 1):
105 : void detach(Player const * source);
106 :
107 : private:
108 : void processQueue();
109 :
110 : DECL_STATIC_LINK(
111 : MissingPluginInstaller, launchUi, MissingPluginInstallerThread *);
112 :
113 : osl::Mutex mutex_;
114 : std::set<OString> reported_;
115 : std::map<OString, std::set<rtl::Reference<Player>>> queued_;
116 : rtl::Reference<MissingPluginInstallerThread> currentThread_;
117 : std::vector<OString> currentDetails_;
118 : std::set<rtl::Reference<Player>> currentSources_;
119 : bool launchNewThread_;
120 : bool inCleanUp_;
121 : };
122 :
123 0 : MissingPluginInstaller::~MissingPluginInstaller() {
124 0 : osl::MutexGuard g(mutex_);
125 : SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer", "unjoined thread");
126 0 : inCleanUp_ = true;
127 0 : }
128 :
129 0 : void MissingPluginInstaller::report(
130 : rtl::Reference<Player> const & source, GstMessage * message)
131 : {
132 : // assert(gst_is_missing_plugin_message(message));
133 0 : gchar * det = gst_missing_plugin_message_get_installer_detail(message);
134 0 : if (det == nullptr) {
135 : SAL_WARN(
136 : "avmedia.gstreamer",
137 : "gst_missing_plugin_message_get_installer_detail failed");
138 0 : return;
139 : }
140 0 : std::size_t len = std::strlen(det);
141 0 : if (len > sal_uInt32(SAL_MAX_INT32)) {
142 : SAL_WARN("avmedia.gstreamer", "detail string too long");
143 0 : g_free(det);
144 0 : return;
145 : }
146 0 : OString detStr(det, len);
147 0 : g_free(det);
148 0 : rtl::Reference<MissingPluginInstallerThread> join;
149 0 : rtl::Reference<MissingPluginInstallerThread> launch;
150 : {
151 0 : osl::MutexGuard g(mutex_);
152 0 : if (reported_.find(detStr) != reported_.end()) {
153 0 : return;
154 : }
155 0 : auto & i = queued_[detStr];
156 0 : bool fresh = i.empty();
157 0 : i.insert(source);
158 0 : if (!(fresh && launchNewThread_)) {
159 0 : return;
160 : }
161 0 : join = currentThread_;
162 0 : currentThread_ = new MissingPluginInstallerThread;
163 : {
164 0 : FlagGuard f(inCleanUp_);
165 0 : currentSources_.clear();
166 : }
167 0 : processQueue();
168 0 : launchNewThread_ = false;
169 0 : launch = currentThread_;
170 : }
171 0 : if (join.is()) {
172 0 : join->join();
173 : }
174 0 : launch->acquire();
175 : Application::PostUserEvent(
176 0 : LINK(this, MissingPluginInstaller, launchUi), launch.get());
177 : }
178 :
179 0 : void eraseSource(std::set<rtl::Reference<Player>> & set, Player const * source)
180 : {
181 : auto i = std::find_if(
182 : set.begin(), set.end(),
183 0 : [source](rtl::Reference<Player> const & el) {
184 0 : return el.get() == source;
185 0 : });
186 0 : if (i != set.end()) {
187 0 : set.erase(i);
188 : }
189 0 : }
190 :
191 0 : void MissingPluginInstaller::detach(Player const * source) {
192 0 : rtl::Reference<MissingPluginInstallerThread> join;
193 : {
194 0 : osl::MutexGuard g(mutex_);
195 0 : if (inCleanUp_) {
196 : // Guard against ~MissingPluginInstaller with erroneously un-joined
197 : // currentThread_ (thus non-empty currentSources_) calling
198 : // destructor of currentSources_, calling ~Player, calling here,
199 : // which would use currentSources_ while it is already being
200 : // destroyed:
201 0 : return;
202 : }
203 0 : for (auto i = queued_.begin(); i != queued_.end();) {
204 0 : eraseSource(i->second, source);
205 0 : if (i->second.empty()) {
206 0 : i = queued_.erase(i);
207 : } else {
208 0 : ++i;
209 : }
210 : }
211 0 : if (currentThread_.is()) {
212 : assert(!currentSources_.empty());
213 0 : eraseSource(currentSources_, source);
214 0 : if (currentSources_.empty()) {
215 0 : join = currentThread_;
216 0 : currentThread_.clear();
217 0 : launchNewThread_ = true;
218 : }
219 0 : }
220 : }
221 0 : if (join.is()) {
222 : // missing cancelability of gst_install_plugins_sync
223 0 : join->join();
224 0 : }
225 : }
226 :
227 0 : void MissingPluginInstaller::processQueue() {
228 : assert(!queued_.empty());
229 : assert(currentDetails_.empty());
230 0 : for (auto i = queued_.begin(); i != queued_.end(); ++i) {
231 0 : reported_.insert(i->first);
232 0 : currentDetails_.push_back(i->first);
233 0 : currentSources_.insert(i->second.begin(), i->second.end());
234 : }
235 0 : queued_.clear();
236 0 : }
237 :
238 0 : IMPL_STATIC_LINK(
239 : MissingPluginInstaller, launchUi, MissingPluginInstallerThread *, thread)
240 : {
241 0 : rtl::Reference<MissingPluginInstallerThread> ref(thread, SAL_NO_ACQUIRE);
242 0 : gst_pb_utils_init();
243 : // not thread safe; hopefully fine to consistently call from our event
244 : // loop (which is the only reason to have this
245 : // Application::PostUserEvent diversion, in case
246 : // MissingPluginInstaller::report might be called from outside our event
247 : // loop), and hopefully fine to call gst_is_missing_plugin_message and
248 : // gst_missing_plugin_message_get_installer_detail before calling
249 : // gst_pb_utils_init
250 0 : ref->launch();
251 0 : return 0;
252 : }
253 :
254 : struct TheMissingPluginInstaller:
255 : public rtl::Static<MissingPluginInstaller, TheMissingPluginInstaller>
256 : {};
257 :
258 0 : void MissingPluginInstallerThread::execute() {
259 0 : MissingPluginInstaller & inst = TheMissingPluginInstaller::get();
260 : for (;;) {
261 0 : std::vector<OString> details;
262 : {
263 0 : osl::MutexGuard g(inst.mutex_);
264 : assert(!inst.currentDetails_.empty());
265 0 : details.swap(inst.currentDetails_);
266 : }
267 0 : std::vector<char *> args;
268 0 : for (auto const & i: details) {
269 0 : args.push_back(const_cast<char *>(i.getStr()));
270 : }
271 0 : args.push_back(nullptr);
272 0 : gst_install_plugins_sync(args.data(), nullptr);
273 : {
274 0 : osl::MutexGuard g(inst.mutex_);
275 0 : if (inst.queued_.empty() || inst.launchNewThread_) {
276 0 : inst.launchNewThread_ = true;
277 0 : break;
278 : }
279 0 : inst.processQueue();
280 : }
281 0 : }
282 0 : }
283 :
284 : }
285 :
286 : // - Player -
287 :
288 :
289 0 : Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) :
290 : GstPlayer_BASE( m_aMutex ),
291 : mxMgr( rxMgr ),
292 : mpPlaybin( NULL ),
293 : mbFakeVideo (false ),
294 : mnUnmutedVolume( 0 ),
295 : mbPlayPending ( false ),
296 : mbMuted( false ),
297 : mbLooping( false ),
298 : mbInitialized( false ),
299 : mnWindowID( 0 ),
300 : mpXOverlay( NULL ),
301 : mnDuration( 0 ),
302 : mnWidth( 0 ),
303 : mnHeight( 0 ),
304 : mnWatchID( 0 ),
305 0 : mbWatchID( false )
306 : {
307 : // Initialize GStreamer library
308 0 : int argc = 1;
309 0 : char name[] = "libreoffice";
310 0 : char *arguments[] = { name };
311 0 : char** argv = arguments;
312 0 : GError* pError = NULL;
313 :
314 0 : mbInitialized = gst_init_check( &argc, &argv, &pError );
315 :
316 : DBG( "%p Player::Player", this );
317 :
318 0 : if (pError != NULL)
319 : {
320 : // TODO: throw an exception?
321 : DBG( "%p Player::Player error '%s'", this, pError->message );
322 0 : g_error_free (pError);
323 : }
324 0 : }
325 :
326 :
327 :
328 0 : Player::~Player()
329 : {
330 : DBG( "%p Player::~Player", this );
331 0 : if( mbInitialized )
332 0 : disposing();
333 0 : }
334 :
335 0 : void SAL_CALL Player::disposing()
336 : {
337 0 : TheMissingPluginInstaller::get().detach(this);
338 :
339 0 : ::osl::MutexGuard aGuard(m_aMutex);
340 :
341 0 : stop();
342 :
343 : DBG( "%p Player::disposing", this );
344 :
345 : // Release the elements and pipeline
346 0 : if( mbInitialized )
347 : {
348 0 : if( mpPlaybin )
349 : {
350 0 : gst_element_set_state( mpPlaybin, GST_STATE_NULL );
351 0 : g_object_unref( G_OBJECT( mpPlaybin ) );
352 :
353 0 : mpPlaybin = NULL;
354 : }
355 :
356 0 : if( mpXOverlay ) {
357 0 : g_object_unref( G_OBJECT ( mpXOverlay ) );
358 0 : mpXOverlay = NULL;
359 : }
360 :
361 : }
362 0 : if (mbWatchID)
363 : {
364 0 : g_source_remove(mnWatchID);
365 0 : mbWatchID = false;
366 0 : }
367 0 : }
368 :
369 0 : static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
370 : {
371 0 : Player* pPlayer = static_cast<Player*>(data);
372 :
373 0 : pPlayer->processMessage( message );
374 :
375 0 : return TRUE;
376 : }
377 :
378 0 : static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
379 : {
380 0 : Player* pPlayer = static_cast<Player*>(data);
381 :
382 0 : return pPlayer->processSyncMessage( message );
383 : }
384 :
385 0 : void Player::processMessage( GstMessage *message )
386 : {
387 0 : switch( GST_MESSAGE_TYPE( message ) ) {
388 : case GST_MESSAGE_EOS:
389 0 : gst_element_set_state( mpPlaybin, GST_STATE_READY );
390 0 : mbPlayPending = false;
391 0 : if (mbLooping)
392 0 : start();
393 0 : break;
394 : case GST_MESSAGE_STATE_CHANGED:
395 0 : if( message->src == GST_OBJECT( mpPlaybin ) ) {
396 : GstState newstate, pendingstate;
397 :
398 0 : gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate);
399 :
400 0 : if( newstate == GST_STATE_PAUSED &&
401 0 : pendingstate == GST_STATE_VOID_PENDING &&
402 : mpXOverlay )
403 0 : gst_video_overlay_expose( mpXOverlay );
404 :
405 0 : if (mbPlayPending)
406 0 : mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED));
407 : }
408 : default:
409 0 : break;
410 : }
411 0 : }
412 :
413 0 : static gboolean wrap_element_query_position (GstElement *element, GstFormat format, gint64 *cur)
414 : {
415 : #ifdef AVMEDIA_GST_0_10
416 : GstFormat my_format = format;
417 : return gst_element_query_position( element, &my_format, cur) && my_format == format && *cur > 0L;
418 : #else
419 0 : return gst_element_query_position( element, format, cur );
420 : #endif
421 : }
422 :
423 0 : static gboolean wrap_element_query_duration (GstElement *element, GstFormat format, gint64 *duration)
424 : {
425 : #ifdef AVMEDIA_GST_0_10
426 : GstFormat my_format = format;
427 : return gst_element_query_duration( element, &my_format, duration) && my_format == format && *duration > 0L;
428 : #else
429 0 : return gst_element_query_duration( element, format, duration );
430 : #endif
431 : }
432 :
433 0 : GstBusSyncReply Player::processSyncMessage( GstMessage *message )
434 : {
435 : // DBG( "%p processSyncMessage has handle: %s", this, GST_MESSAGE_TYPE_NAME( message ) );
436 :
437 : #if OSL_DEBUG_LEVEL > 0
438 : if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
439 : {
440 : GError* error;
441 : gchar* error_debug;
442 :
443 : gst_message_parse_error( message, &error, &error_debug );
444 : SAL_WARN(
445 : "avmedia",
446 : "gstreamer error: '" << error->message << "' debug: '"
447 : << error_debug << "'");
448 : }
449 : #endif
450 :
451 : #ifdef AVMEDIA_GST_0_10
452 : if (message->structure &&
453 : !strcmp( gst_structure_get_name( message->structure ), "prepare-xwindow-id" ) )
454 : #else
455 0 : if (gst_is_video_overlay_prepare_window_handle_message (message) )
456 : #endif
457 : {
458 : DBG( "%p processSyncMessage prepare window id: %s %d", this,
459 : GST_MESSAGE_TYPE_NAME( message ), (int)mnWindowID );
460 0 : if( mpXOverlay )
461 0 : g_object_unref( G_OBJECT ( mpXOverlay ) );
462 0 : g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio", FALSE, NULL );
463 0 : mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
464 0 : g_object_ref( G_OBJECT ( mpXOverlay ) );
465 0 : if ( mnWindowID != 0 )
466 0 : gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
467 0 : return GST_BUS_DROP;
468 : }
469 :
470 : #ifdef AVMEDIA_GST_0_10
471 : if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_STATE_CHANGED ) {
472 : if( message->src == GST_OBJECT( mpPlaybin ) ) {
473 : GstState newstate, pendingstate;
474 :
475 : gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate);
476 :
477 : DBG( "%p state change received, new state %d pending %d", this,
478 : (int)newstate, (int)pendingstate );
479 : if( newstate == GST_STATE_PAUSED &&
480 : pendingstate == GST_STATE_VOID_PENDING ) {
481 :
482 : DBG( "%p change to paused received", this );
483 :
484 : if( mnDuration == 0) {
485 : gint64 gst_duration = 0L;
486 : if( wrap_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
487 : mnDuration = gst_duration;
488 : }
489 :
490 : if( mnWidth == 0 ) {
491 : GList *pStreamInfo = NULL;
492 :
493 : g_object_get( G_OBJECT( mpPlaybin ), "stream-info", &pStreamInfo, NULL );
494 :
495 : for ( ; pStreamInfo != NULL; pStreamInfo = pStreamInfo->next) {
496 : GObject *pInfo = G_OBJECT( pStreamInfo->data );
497 :
498 : if( !pInfo )
499 : continue;
500 :
501 : int nType;
502 : g_object_get( pInfo, "type", &nType, NULL );
503 : GEnumValue *pValue = g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo ), "type" ) )->enum_class,
504 : nType );
505 :
506 : if( !g_ascii_strcasecmp( pValue->value_nick, "video" ) ) {
507 : GstStructure *pStructure;
508 : GstPad *pPad;
509 :
510 : g_object_get( pInfo, "object", &pPad, NULL );
511 : pStructure = gst_caps_get_structure( GST_PAD_CAPS( pPad ), 0 );
512 : if( pStructure ) {
513 : gst_structure_get_int( pStructure, "width", &mnWidth );
514 : gst_structure_get_int( pStructure, "height", &mnHeight );
515 : DBG( "queried size: %d x %d", mnWidth, mnHeight );
516 : }
517 : g_object_unref (pPad);
518 : }
519 : }
520 :
521 : maSizeCondition.set();
522 : }
523 : }
524 : }
525 : #else
526 : // We get to use the exciting new playbin2 ! (now known as playbin)
527 0 : if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
528 0 : if( mnDuration == 0) {
529 0 : gint64 gst_duration = 0L;
530 0 : if( wrap_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
531 0 : mnDuration = gst_duration;
532 : }
533 0 : if( mnWidth == 0 ) {
534 0 : GstPad *pad = NULL;
535 :
536 0 : g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
537 :
538 0 : if( pad ) {
539 0 : int w = 0, h = 0;
540 :
541 0 : GstCaps *caps = gst_pad_get_current_caps( pad );
542 :
543 0 : if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
544 : "width", G_TYPE_INT, &w,
545 : "height", G_TYPE_INT, &h,
546 0 : NULL ) ) {
547 0 : mnWidth = w;
548 0 : mnHeight = h;
549 :
550 : DBG( "queried size: %d x %d", mnWidth, mnHeight );
551 :
552 0 : maSizeCondition.set();
553 : }
554 0 : gst_caps_unref( caps );
555 0 : g_object_unref( pad );
556 : }
557 : }
558 : #endif
559 0 : } else if (gst_is_missing_plugin_message(message)) {
560 0 : TheMissingPluginInstaller::get().report(this, message);
561 0 : if( mnWidth == 0 ) {
562 : // an error occurred, set condition so that OOo thread doesn't wait for us
563 0 : maSizeCondition.set();
564 : }
565 0 : } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
566 : DBG( "Error !\n" );
567 0 : if( mnWidth == 0 ) {
568 : // an error occurred, set condition so that OOo thread doesn't wait for us
569 0 : maSizeCondition.set();
570 : }
571 : }
572 :
573 0 : return GST_BUS_PASS;
574 : }
575 :
576 0 : void Player::preparePlaybin( const OUString& rURL, GstElement *pSink )
577 : {
578 : GstBus *pBus;
579 :
580 0 : if( mpPlaybin != NULL ) {
581 0 : gst_element_set_state( mpPlaybin, GST_STATE_NULL );
582 0 : mbPlayPending = false;
583 0 : g_object_unref( mpPlaybin );
584 : }
585 :
586 0 : mpPlaybin = gst_element_factory_make( "playbin", NULL );
587 0 : if( pSink != NULL ) // used for getting preferred size etc.
588 : {
589 0 : g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, NULL );
590 0 : mbFakeVideo = true;
591 : }
592 : else
593 0 : mbFakeVideo = false;
594 :
595 0 : OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
596 0 : g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , NULL );
597 :
598 0 : pBus = gst_element_get_bus( mpPlaybin );
599 0 : if (mbWatchID)
600 : {
601 0 : g_source_remove(mnWatchID);
602 0 : mbWatchID = false;
603 : }
604 0 : mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
605 0 : mbWatchID = true;
606 : DBG( "%p set sync handler", this );
607 : #ifdef AVMEDIA_GST_0_10
608 : gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this );
609 : #else
610 0 : gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, NULL );
611 : #endif
612 0 : g_object_unref( pBus );
613 0 : }
614 :
615 0 : bool Player::create( const OUString& rURL )
616 : {
617 0 : bool bRet = false;
618 :
619 : // create all the elements and link them
620 :
621 : DBG("create player, URL: %s", OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr());
622 :
623 0 : if( mbInitialized && !rURL.isEmpty() )
624 : {
625 : // fakesink for pre-roll & sizing ...
626 0 : preparePlaybin( rURL, gst_element_factory_make( "fakesink", NULL ) );
627 :
628 0 : gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
629 0 : mbPlayPending = false;
630 :
631 0 : bRet = true;
632 : }
633 :
634 0 : if( bRet )
635 0 : maURL = rURL;
636 : else
637 0 : maURL.clear();
638 :
639 0 : return bRet;
640 : }
641 :
642 :
643 :
644 0 : void SAL_CALL Player::start()
645 : throw (uno::RuntimeException, std::exception)
646 : {
647 0 : ::osl::MutexGuard aGuard(m_aMutex);
648 :
649 : // set the pipeline state to READY and run the loop
650 0 : if( mbInitialized && NULL != mpPlaybin )
651 : {
652 0 : gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
653 0 : mbPlayPending = true;
654 0 : }
655 0 : }
656 :
657 :
658 :
659 0 : void SAL_CALL Player::stop()
660 : throw (uno::RuntimeException, std::exception)
661 : {
662 0 : ::osl::MutexGuard aGuard(m_aMutex);
663 :
664 : // set the pipeline in PAUSED STATE
665 0 : if( mpPlaybin )
666 0 : gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
667 :
668 0 : mbPlayPending = false;
669 0 : DBG( "stop %p", mpPlaybin );
670 0 : }
671 :
672 :
673 :
674 0 : sal_Bool SAL_CALL Player::isPlaying()
675 : throw (uno::RuntimeException, std::exception)
676 : {
677 0 : ::osl::MutexGuard aGuard(m_aMutex);
678 :
679 0 : bool bRet = mbPlayPending;
680 :
681 : // return whether the pipeline is in PLAYING STATE or not
682 0 : if( !mbPlayPending && mbInitialized && mpPlaybin )
683 : {
684 0 : bRet = GST_STATE_PLAYING == GST_STATE( mpPlaybin );
685 : }
686 :
687 : DBG( "isPlaying %d", bRet );
688 :
689 0 : return bRet;
690 : }
691 :
692 :
693 :
694 0 : double SAL_CALL Player::getDuration()
695 : throw (uno::RuntimeException, std::exception)
696 : {
697 0 : ::osl::MutexGuard aGuard(m_aMutex);
698 :
699 : // slideshow checks for non-zero duration, so cheat here
700 0 : double duration = 0.01;
701 :
702 0 : if( mpPlaybin && mnDuration > 0 ) {
703 0 : duration = mnDuration / GST_SECOND;
704 : }
705 :
706 0 : return duration;
707 : }
708 :
709 :
710 :
711 0 : void SAL_CALL Player::setMediaTime( double fTime )
712 : throw (uno::RuntimeException, std::exception)
713 : {
714 0 : ::osl::MutexGuard aGuard(m_aMutex);
715 :
716 0 : if( mpPlaybin ) {
717 0 : gint64 gst_position = llround (fTime * GST_SECOND);
718 :
719 : gst_element_seek( mpPlaybin, 1.0,
720 : GST_FORMAT_TIME,
721 : GST_SEEK_FLAG_FLUSH,
722 : GST_SEEK_TYPE_SET, gst_position,
723 0 : GST_SEEK_TYPE_NONE, 0 );
724 0 : if( !isPlaying() )
725 0 : gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
726 :
727 : DBG( "seek to: %" SAL_PRIdINT64 " ns original: %lf s", gst_position, fTime );
728 0 : }
729 0 : }
730 :
731 :
732 :
733 0 : double SAL_CALL Player::getMediaTime()
734 : throw (uno::RuntimeException, std::exception)
735 : {
736 0 : ::osl::MutexGuard aGuard(m_aMutex);
737 :
738 0 : double position = 0.0;
739 :
740 0 : if( mpPlaybin ) {
741 : // get current position in the stream
742 : gint64 gst_position;
743 0 : if( wrap_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
744 0 : position = gst_position / GST_SECOND;
745 : }
746 :
747 0 : return position;
748 : }
749 :
750 0 : void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
751 : throw (uno::RuntimeException, std::exception)
752 : {
753 0 : ::osl::MutexGuard aGuard(m_aMutex);
754 : // TODO check how to do with GST
755 0 : mbLooping = bSet;
756 0 : }
757 :
758 :
759 :
760 0 : sal_Bool SAL_CALL Player::isPlaybackLoop()
761 : throw (uno::RuntimeException, std::exception)
762 : {
763 0 : ::osl::MutexGuard aGuard(m_aMutex);
764 : // TODO check how to do with GST
765 0 : return mbLooping;
766 : }
767 :
768 :
769 :
770 0 : void SAL_CALL Player::setMute( sal_Bool bSet )
771 : throw (uno::RuntimeException, std::exception)
772 : {
773 0 : ::osl::MutexGuard aGuard(m_aMutex);
774 :
775 : DBG( "set mute: %d muted: %d unmuted volume: %lf", bSet, mbMuted, mnUnmutedVolume );
776 :
777 : // change the volume to 0 or the unmuted volume
778 0 : if( mpPlaybin && mbMuted != bool(bSet) )
779 : {
780 0 : double nVolume = mnUnmutedVolume;
781 0 : if( bSet )
782 : {
783 0 : nVolume = 0.0;
784 : }
785 :
786 0 : g_object_set( G_OBJECT( mpPlaybin ), "volume", nVolume, NULL );
787 :
788 0 : mbMuted = bSet;
789 0 : }
790 0 : }
791 :
792 :
793 :
794 0 : sal_Bool SAL_CALL Player::isMute()
795 : throw (uno::RuntimeException, std::exception)
796 : {
797 0 : ::osl::MutexGuard aGuard(m_aMutex);
798 :
799 0 : return mbMuted;
800 : }
801 :
802 :
803 :
804 0 : void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
805 : throw (uno::RuntimeException, std::exception)
806 : {
807 0 : ::osl::MutexGuard aGuard(m_aMutex);
808 :
809 0 : mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );
810 :
811 : DBG( "set volume: %d gst volume: %lf", nVolumeDB, mnUnmutedVolume );
812 :
813 : // change volume
814 0 : if( !mbMuted && mpPlaybin )
815 : {
816 0 : g_object_set( G_OBJECT( mpPlaybin ), "volume", (gdouble) mnUnmutedVolume, NULL );
817 0 : }
818 0 : }
819 :
820 :
821 :
822 0 : sal_Int16 SAL_CALL Player::getVolumeDB()
823 : throw (uno::RuntimeException, std::exception)
824 : {
825 0 : ::osl::MutexGuard aGuard(m_aMutex);
826 :
827 0 : sal_Int16 nVolumeDB(0);
828 :
829 0 : if( mpPlaybin ) {
830 0 : double nGstVolume = 0.0;
831 :
832 0 : g_object_get( G_OBJECT( mpPlaybin ), "volume", &nGstVolume, NULL );
833 :
834 0 : nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) );
835 : }
836 :
837 0 : return nVolumeDB;
838 : }
839 :
840 :
841 :
842 0 : awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
843 : throw (uno::RuntimeException, std::exception)
844 : {
845 0 : ::osl::MutexGuard aGuard(m_aMutex);
846 :
847 0 : awt::Size aSize( 0, 0 );
848 :
849 0 : if( maURL.isEmpty() )
850 : {
851 : DBG( "%p Player::getPreferredPlayerWindowSize - empty URL => 0x0", this );
852 0 : return aSize;
853 : }
854 :
855 : DBG( "%p pre-Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth, mnHeight );
856 :
857 0 : TimeValue aTimeout = { 10, 0 };
858 : #if OSL_DEBUG_LEVEL > 2
859 : osl::Condition::Result aResult =
860 : #endif
861 0 : maSizeCondition.wait( &aTimeout );
862 :
863 : DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult, mnWidth, mnHeight );
864 :
865 0 : if( mnWidth != 0 && mnHeight != 0 ) {
866 0 : aSize.Width = mnWidth;
867 0 : aSize.Height = mnHeight;
868 : }
869 :
870 0 : return aSize;
871 : }
872 :
873 :
874 :
875 0 : uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
876 : throw (uno::RuntimeException, std::exception)
877 : {
878 0 : ::osl::MutexGuard aGuard(m_aMutex);
879 :
880 0 : uno::Reference< ::media::XPlayerWindow > xRet;
881 0 : awt::Size aSize( getPreferredPlayerWindowSize() );
882 :
883 0 : if( mbFakeVideo )
884 0 : preparePlaybin( maURL, NULL );
885 :
886 : DBG( "Player::createPlayerWindow %d %d length: %d", aSize.Width, aSize.Height, rArguments.getLength() );
887 :
888 0 : if( aSize.Width > 0 && aSize.Height > 0 )
889 : {
890 0 : ::avmedia::gstreamer::Window* pWindow = new ::avmedia::gstreamer::Window( mxMgr, *this );
891 :
892 0 : xRet = pWindow;
893 :
894 0 : if( rArguments.getLength() > 2 )
895 : {
896 0 : sal_IntPtr pIntPtr = 0;
897 0 : rArguments[ 2 ] >>= pIntPtr;
898 0 : SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
899 0 : const SystemEnvData* pEnvData = pParentWindow ? pParentWindow->GetSystemData() : NULL;
900 : OSL_ASSERT(pEnvData);
901 0 : if (pEnvData)
902 : {
903 0 : mnWindowID = pEnvData->aWindow;
904 : DBG( "set window id to %d XOverlay %p\n", (int)mnWindowID, mpXOverlay);
905 0 : gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
906 0 : if ( mpXOverlay != NULL )
907 0 : gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
908 : }
909 : }
910 : }
911 :
912 0 : return xRet;
913 : }
914 :
915 :
916 :
917 0 : uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
918 : throw (uno::RuntimeException, std::exception)
919 : {
920 0 : ::osl::MutexGuard aGuard(m_aMutex);
921 0 : FrameGrabber* pFrameGrabber = NULL;
922 0 : const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
923 :
924 0 : if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
925 0 : pFrameGrabber = FrameGrabber::create( maURL );
926 : DBG( "created FrameGrabber %p", pFrameGrabber );
927 :
928 0 : return pFrameGrabber;
929 : }
930 :
931 :
932 :
933 0 : OUString SAL_CALL Player::getImplementationName()
934 : throw (uno::RuntimeException, std::exception)
935 : {
936 0 : return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME );
937 : }
938 :
939 :
940 :
941 0 : sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
942 : throw (uno::RuntimeException, std::exception)
943 : {
944 0 : return cppu::supportsService(this, ServiceName);
945 : }
946 :
947 :
948 :
949 0 : uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
950 : throw (uno::RuntimeException, std::exception)
951 : {
952 0 : uno::Sequence< OUString > aRet(1);
953 0 : aRet[0] = AVMEDIA_GST_PLAYER_SERVICENAME ;
954 :
955 0 : return aRet;
956 : }
957 :
958 : } // namespace gstreamer
959 : } // namespace avmedia
960 :
961 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|