Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  gstplayer.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <map>
#include <mutex>
#include <set>
#include <vector>
#include <math.h>

#include <com/sun/star/text/GraphicCrop.hpp>

#include <cppuhelper/supportsservice.hxx>
#include <sal/log.hxx>
#include <rtl/string.hxx>
#include <salhelper/thread.hxx>
#include <vcl/svapp.hxx>
#include <vcl/syschild.hxx>
#include <vcl/sysdata.hxx>
#include <vcl/graph.hxx>
#include <avmedia/mediaitem.hxx>

#include "gstplayer.hxx"
#include "gstframegrabber.hxx"
#include "gstwindow.hxx"

#include <gst/video/videooverlay.h>
#include <gst/pbutils/missing-plugins.h>
#include <gst/pbutils/pbutils.h>

#define AVVERSION "gst 1.0: "

using namespace ::com::sun::star;

namespace avmedia::gstreamer {

namespace {

class FlagGuard {
public:
    explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }

    ~FlagGuard() { flag_ = false; }

private:
    bool & flag_;
};

class MissingPluginInstallerThread: public salhelper::Thread {
public:
    MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}

private:
    void execute() override;
};


class MissingPluginInstaller {
    friend class MissingPluginInstallerThread;

public:
    MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}

    ~MissingPluginInstaller();

    void report(rtl::Reference<Player> const & source, GstMessage * message);

    // Player::~Player calls Player::disposing calls
    // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
    // here (which would bump its refcount back from 0 to 1):
    void detach(Player const * source);

private:
    void processQueue();

    DECL_STATIC_LINK(MissingPluginInstaller, launchUi, void*, void);

    std::recursive_mutex mutex_;
    std::set<OString> reported_;
    std::map<OString, std::set<rtl::Reference<Player>>> queued_;
    rtl::Reference<MissingPluginInstallerThread> currentThread_;
    std::vector<OString> currentDetails_;
    std::set<rtl::Reference<Player>> currentSources_;
    bool launchNewThread_;
    bool inCleanUp_;
};


MissingPluginInstaller::~MissingPluginInstaller() {
    std::unique_lock g(mutex_);
    SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer""unjoined thread");
    inCleanUp_ = true;
}


void MissingPluginInstaller::report(
    rtl::Reference<Player> const & source, GstMessage * message)
{
    // assert(gst_is_missing_plugin_message(message));
    gchar * det = gst_missing_plugin_message_get_installer_detail(message);
    if (det == nullptr) {
        SAL_WARN(
            "avmedia.gstreamer",
            "gst_missing_plugin_message_get_installer_detail failed");
        return;
    }
    std::size_t len = std::strlen(det);
    if (len > SAL_MAX_INT32) {
        SAL_WARN("avmedia.gstreamer""detail string too long");
        g_free(det);
        return;
    }
    OString detStr(det, len);
    g_free(det);
    rtl::Reference<MissingPluginInstallerThread> join;
    rtl::Reference<MissingPluginInstallerThread> launch;
    {
        std::unique_lock g(mutex_);
        if (reported_.contains(detStr)) {
            return;
        }
        auto & i = queued_[detStr];
        bool fresh = i.empty();
        i.insert(source);
        if (!(fresh && launchNewThread_)) {
            return;
        }
        join = currentThread_;
        currentThread_ = new MissingPluginInstallerThread;
        {
            FlagGuard f(inCleanUp_);
            currentSources_.clear();
        }
        processQueue();
        launchNewThread_ = false;
        launch = currentThread_;
    }
    if (join.is()) {
        join->join();
    }
    launch->acquire();
    Application::PostUserEvent(
        LINK(this, MissingPluginInstaller, launchUi), launch.get());
}


void eraseSource(std::set<rtl::Reference<Player>> & set, Player const * source)
{
    auto i = std::find_if(
        set.begin(), set.end(),
        [source](rtl::Reference<Player> const & el) {
            return el.get() == source;
        });
    if (i != set.end()) {
        set.erase(i);
    }
}


void MissingPluginInstaller::detach(Player const * source) {
    rtl::Reference<MissingPluginInstallerThread> join;
    {
        std::unique_lock g(mutex_);
        if (inCleanUp_) {
            // Guard against ~MissingPluginInstaller with erroneously un-joined
            // currentThread_ (thus non-empty currentSources_) calling
            // destructor of currentSources_, calling ~Player, calling here,
            // which would use currentSources_ while it is already being
            // destroyed:
            return;
        }
        for (auto i = queued_.begin(); i != queued_.end();) {
            eraseSource(i->second, source);
            if (i->second.empty()) {
                i = queued_.erase(i);
            } else {
                ++i;
            }
        }
        if (currentThread_.is()) {
            assert(!currentSources_.empty());
            eraseSource(currentSources_, source);
            if (currentSources_.empty()) {
                join = currentThread_;
                currentThread_.clear();
                launchNewThread_ = true;
            }
        }
    }
    if (join.is()) {
        // missing cancellability of gst_install_plugins_sync
        join->join();
    }
}


void MissingPluginInstaller::processQueue() {
    assert(!queued_.empty());
    assert(currentDetails_.empty());
    for (const auto& rEntry : queued_) {
        reported_.insert(rEntry.first);
        currentDetails_.push_back(rEntry.first);
        currentSources_.insert(rEntry.second.begin(), rEntry.second.end());
    }
    queued_.clear();
}


IMPL_STATIC_LINK(MissingPluginInstaller, launchUi, void *, p, void)
{
    MissingPluginInstallerThread* thread = static_cast<MissingPluginInstallerThread*>(p);
    rtl::Reference<MissingPluginInstallerThread> ref(thread, SAL_NO_ACQUIRE);
    gst_pb_utils_init();
        // not thread safe; hopefully fine to consistently call from our event
        // loop (which is the only reason to have this
        // Application::PostUserEvent diversion, in case
        // MissingPluginInstaller::report might be called from outside our event
        // loop), and hopefully fine to call gst_is_missing_plugin_message and
        // gst_missing_plugin_message_get_installer_detail before calling
        // gst_pb_utils_init
    ref->launch();
}


MissingPluginInstaller& TheMissingPluginInstaller()
{
    static MissingPluginInstaller theInstaller;
    return theInstaller;
}


void MissingPluginInstallerThread::execute() {
    MissingPluginInstaller & inst = TheMissingPluginInstaller();
    for (;;) {
        std::vector<OString> details;
        {
            std::unique_lock g(inst.mutex_);
            assert(!inst.currentDetails_.empty());
            details.swap(inst.currentDetails_);
        }
        std::vector<char *> args;
        args.reserve(details.size());
        for (auto const& i : details)
        {
            args.push_back(const_cast<char *>(i.getStr()));
        }
        args.push_back(nullptr);
        gst_install_plugins_sync(args.data(), nullptr);
        {
            std::unique_lock g(inst.mutex_);
            if (inst.queued_.empty() || inst.launchNewThread_) {
                inst.launchNewThread_ = true;
                break;
            }
            inst.processQueue();
        }
    }
}

// end anonymous namespace


Player::Player() :
    GstPlayer_BASE( m_aMutex ),
    mpPlaybin( nullptr ),
    mpVolumeControl( nullptr ),
    mbUseGtkSink( false ),
    mbFakeVideo (false ),
    mnUnmutedVolume( 0 ),
    mbMuted( false ),
    mbLooping( false ),
    mbInitialized( false ),
    mpDisplay( nullptr ),
    mnWindowID( 0 ),
    mpXOverlay( nullptr ),
    mnDuration( 0 ),
    mnWidth( 0 ),
    mnHeight( 0 ),
    mnWatchID( 0 ),
    mbWatchID( false )
{
    // Initialize GStreamer library
    int argc = 1;
    char name[] = "libreoffice";
    char *arguments[] = { name };
    char** argv = arguments;
    GError* pError = nullptr;

    mbInitialized = gst_init_check( &argc, &argv, &pError );

    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player" );

    if (pError != nullptr)
    {
        // TODO: throw an exception?
        SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player error '" << pError->message << "'" );
        g_error_free (pError);
    }
}


Player::~Player()
{
    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::~Player" );
    if( mbInitialized )
        disposing();
}


void SAL_CALL Player::disposing()
{
    TheMissingPluginInstaller().detach(this);

    ::osl::MutexGuard aGuard(m_aMutex);

    stop();

    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::disposing" );

    // Release the elements and pipeline
    if( mbInitialized )
    {
        if( mpPlaybin )
        {
            gst_element_set_state( mpPlaybin, GST_STATE_NULL );
            g_object_unref( G_OBJECT( mpPlaybin ) );

            mpPlaybin = nullptr;
            mpVolumeControl = nullptr;
        }

        if( mpXOverlay ) {
            g_object_unref( G_OBJECT ( mpXOverlay ) );
            mpXOverlay = nullptr;
        }

    }
    if (mbWatchID)
    {
        g_source_remove(mnWatchID);
        mbWatchID = false;
    }
}


static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
{
    Player* pPlayer = static_cast<Player*>(data);

    pPlayer->processMessage( message );

    return true;
}


static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
{
    Player* pPlayer = static_cast<Player*>(data);

    return pPlayer->processSyncMessage( message );
}


void Player::processMessage( GstMessage *message )
{
    switch( GST_MESSAGE_TYPE( message ) ) {
    case GST_MESSAGE_EOS:
        gst_element_set_state( mpPlaybin, GST_STATE_READY );
        if (mbLooping)
            start();
        break;
    case GST_MESSAGE_STATE_CHANGED:
        if (message->src == GST_OBJECT(mpPlaybin))
        {
            GstState newstate, pendingstate;

            gst_message_parse_state_changed (message, nullptr, &newstate, &pendingstate);

            if (!mbUseGtkSink && newstate == GST_STATE_PAUSED &&
                pendingstate == GST_STATE_VOID_PENDING && mpXOverlay)
            {
                gst_video_overlay_expose(mpXOverlay);
            }
        }
        break;
    default:
        break;
    }
}

#define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"

static bool lcl_is_wayland_display_handle_need_context_message(GstMessage* msg)
{
    g_return_val_if_fail(GST_IS_MESSAGE(msg), false);

    if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_NEED_CONTEXT)
        return false;
    const gchar *type = nullptr;
    if (!gst_message_parse_context_type(msg, &type))
        return false;
    return !g_strcmp0(type, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
}

static GstContext* lcl_wayland_display_handle_context_new(void* display)
{
    GstContext *context = gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE, true);
    gst_structure_set (gst_context_writable_structure (context),
                       "handle", G_TYPE_POINTER, display, nullptr);
    return context;
}

GstBusSyncReply Player::processSyncMessage( GstMessage *message )
{
#if OSL_DEBUG_LEVEL > 0
    if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
    {
        GError* error;
        gchar* error_debug;

        gst_message_parse_error( message, &error, &error_debug );
        SAL_WARN(
            "avmedia.gstreamer",
            "error: '" << error->message << "' debug: '"
                << error_debug << "'");
    }
    else if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_WARNING )
    {
        GError* error;
        gchar* error_debug;

        gst_message_parse_warning( message, &error, &error_debug );
        SAL_WARN(
            "avmedia.gstreamer",
            "warning: '" << error->message << "' debug: '"
                << error_debug << "'");
    }
    else if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_INFO )
    {
        GError* error;
        gchar* error_debug;

        gst_message_parse_info( message, &error, &error_debug );
        SAL_WARN(
            "avmedia.gstreamer",
            "info: '" << error->message << "' debug: '"
                << error_debug << "'");
    }
#endif

    if (!mbUseGtkSink)
    {
        if (gst_is_video_overlay_prepare_window_handle_message (message) )
        {
            SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " processSyncMessage prepare window id: " <<
                      GST_MESSAGE_TYPE_NAME( message ) << " " << static_cast<int>(mnWindowID) );
            if( mpXOverlay )
                g_object_unref( G_OBJECT ( mpXOverlay ) );
            g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio"FALSE, nullptr );
            mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
            g_object_ref( G_OBJECT ( mpXOverlay ) );
            if ( mnWindowID != 0 )
            {
                gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
                gst_video_overlay_handle_events(mpXOverlay, 0); // Let the parent window handle events.
                if (maArea.Width > 0 && maArea.Height > 0)
                    gst_video_overlay_set_render_rectangle(mpXOverlay, maArea.X, maArea.Y, maArea.Width, maArea.Height);
            }

            return GST_BUS_DROP;
        }
        else if (lcl_is_wayland_display_handle_need_context_message(message))
        {
            GstContext *context = lcl_wayland_display_handle_context_new(mpDisplay);
            gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context);

            return GST_BUS_DROP;
        }
    }

    if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
        if( mnDuration == 0) {
            gint64 gst_duration = 0;
            if( gst_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
                mnDuration = gst_duration;
        }
        if( mnWidth == 0 ) {
            GstPad *pad = nullptr;

            g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );

            if( pad ) {
                int w = 0, h = 0;

                GstCaps *caps = gst_pad_get_current_caps( pad );

                if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
                                       "width", G_TYPE_INT, &w,
                                       "height", G_TYPE_INT, &h,
                                       nullptr ) ) {
                    mnWidth = w;
                    mnHeight = h;

                    SAL_INFO( "avmedia.gstreamer", AVVERSION "queried size: " << mnWidth << "x" << mnHeight );

                }
                gst_caps_unref( caps );
                g_object_unref( pad );
            }

            maSizeCondition.set();
        }
    } else if (gst_is_missing_plugin_message(message)) {
        TheMissingPluginInstaller().report(this, message);
        if( mnWidth == 0 ) {
            // an error occurred, set condition so that OOo thread doesn't wait for us
            maSizeCondition.set();
        }
    } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
        if( mnWidth == 0 ) {
            // an error occurred, set condition so that OOo thread doesn't wait for us
            maSizeCondition.set();
        }
    }

    return GST_BUS_PASS;
}

void Player::preparePlaybin( std::u16string_view rURL, GstElement *pSink )
{
    if (mpPlaybin != nullptr)
    {
        gst_element_set_state( mpPlaybin, GST_STATE_NULL );
        g_object_unref( mpPlaybin );
    }

    mpPlaybin = gst_element_factory_make( "playbin", nullptr );

    //tdf#96989 on systems with flat-volumes setting the volume directly on the
    //playbin to 100% results in setting the global volume to 100% of the
    //maximum. We expect to set as % of the current volume.
    mpVolumeControl = gst_element_factory_make( "volume", nullptr );
    GstElement *pAudioSink = gst_element_factory_make( "autoaudiosink", nullptr );
    GstElement* pAudioOutput = gst_bin_new("audio-output-bin");
    assert(pAudioOutput);
    if (pAudioSink)
        gst_bin_add(GST_BIN(pAudioOutput), pAudioSink);
    if (mpVolumeControl)
    {
        gst_bin_add(GST_BIN(pAudioOutput), mpVolumeControl);
        if (pAudioSink)
            gst_element_link(mpVolumeControl, pAudioSink);
        GstPad *pPad = gst_element_get_static_pad(mpVolumeControl, "sink");
        gst_element_add_pad(GST_ELEMENT(pAudioOutput), gst_ghost_pad_new("sink", pPad));
        gst_object_unref(GST_OBJECT(pPad));
    }
    g_object_set(G_OBJECT(mpPlaybin), "audio-sink", pAudioOutput, nullptr);

    if( pSink != nullptr ) // used for getting preferred size etc.
    {
        g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, nullptr );
        mbFakeVideo = true;
    }
    else
        mbFakeVideo = false;

    OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
    g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , nullptr );

    GstBus *pBus = gst_element_get_bus( mpPlaybin );
    if (mbWatchID)
    {
        g_source_remove(mnWatchID);
        mbWatchID = false;
    }
    mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
    mbWatchID = true;
    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " set sync handler" );
    gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, nullptr );
    g_object_unref( pBus );
}


bool Player::create( const OUString& rURL )
{
    bool    bRet = false;

    // create all the elements and link them

    SAL_INFO( "avmedia.gstreamer""create player, URL: '" << rURL << "'" );

    if( mbInitialized && !rURL.isEmpty() )
    {
        // fakesink for pre-roll & sizing ...
        preparePlaybin( rURL, gst_element_factory_make( "fakesink", nullptr ) );

        gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );

        bRet = true;
    }

    if( bRet )
        maURL = rURL;
    else
        maURL.clear();

    return bRet;
}

void SAL_CALL Player::start()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    // set the pipeline state to READY and run the loop
    if( mbInitialized && mpPlaybin != nullptr )
    {
        gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
    }

    SAL_INFO( "avmedia.gstreamer", AVVERSION "start " << mpPlaybin );
}

void SAL_CALL Player::stop()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    // set the pipeline in PAUSED STATE
    if( mpPlaybin )
        gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );

    SAL_INFO( "avmedia.gstreamer", AVVERSION "stop " << mpPlaybin );
}

sal_Bool SAL_CALL Player::isPlaying()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    bool bRet = false;

    // return whether the pipeline target is PLAYING STATE or not
    if (mbInitialized && mpPlaybin)
    {
        bRet = GST_STATE_TARGET(mpPlaybin) == GST_STATE_PLAYING;
    }

    return bRet;
}

double SAL_CALL Player::getDuration()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    // slideshow checks for non-zero duration, so cheat here
    double duration = 0.3;

    if( mpPlaybin && mnDuration > 0 ) {
        duration = mnDuration / GST_SECOND;
    }

    return duration;
}


void SAL_CALL Player::setMediaTime( double fTime )
{
    ::osl::MutexGuard aGuard(m_aMutex);

    if( !mpPlaybin )
        return;

    gint64 gst_position = llround (fTime * GST_SECOND);

    gst_element_seek( mpPlaybin, 1.0,
                      GST_FORMAT_TIME,
                      GST_SEEK_FLAG_FLUSH,
                      GST_SEEK_TYPE_SET, gst_position,
                      GST_SEEK_TYPE_NONE, 0 );

    SAL_INFO( "avmedia.gstreamer", AVVERSION "seek to: " << gst_position << " ns original: " << fTime << " s" );
}


double SAL_CALL Player::getMediaTime()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    double position = 0.0;

    if( mpPlaybin ) {
        // get current position in the stream
        gint64 gst_position;
        if( gst_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
            position = gst_position / GST_SECOND;
    }

    return position;
}


void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
{
    ::osl::MutexGuard aGuard(m_aMutex);
    // TODO check how to do with GST
    mbLooping = bSet;
}


sal_Bool SAL_CALL Player::isPlaybackLoop()
{
    ::osl::MutexGuard aGuard(m_aMutex);
    // TODO check how to do with GST
    return mbLooping;
}


void SAL_CALL Player::setMute( sal_Bool bSet )
{
    ::osl::MutexGuard aGuard(m_aMutex);

    SAL_INFO( "avmedia.gstreamer", AVVERSION "set mute: " << bSet << " muted: " << mbMuted << " unmuted volume: " << mnUnmutedVolume );

    // change the volume to 0 or the unmuted volume
    if (mpVolumeControl && mbMuted != bool(bSet))
    {
        double nVolume = mnUnmutedVolume;
        if( bSet )
        {
            nVolume = 0.0;
        }

        g_object_set( G_OBJECT( mpVolumeControl ), "volume", nVolume, nullptr );

        mbMuted = bSet;
    }
}


sal_Bool SAL_CALL Player::isMute()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    return mbMuted;
}


void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
{
    ::osl::MutexGuard aGuard(m_aMutex);

    mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );

    SAL_INFO( "avmedia.gstreamer", AVVERSION "set volume: " << nVolumeDB << " gst volume: " << mnUnmutedVolume );

    // change volume
    if (mpVolumeControl && !mbMuted)
    {
        g_object_set( G_OBJECT( mpVolumeControl ), "volume", mnUnmutedVolume, nullptr );
    }
}


sal_Int16 SAL_CALL Player::getVolumeDB()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    sal_Int16 nVolumeDB(0);

    if (mpVolumeControl)
    {
        double nGstVolume = 0.0;

        g_object_get( G_OBJECT( mpVolumeControl ), "volume", &nGstVolume, nullptr );

        nVolumeDB = static_cast<sal_Int16>( 20.0*log10 ( nGstVolume ) );
    }

    return nVolumeDB;
}


awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
{
    ::osl::MutexGuard aGuard(m_aMutex);

    awt::Size aSize( 0, 0 );

    if( maURL.isEmpty() )
    {
        SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
        return aSize;
    }

    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth << "x" << mnHeight );

    osl::Condition::Result aResult = maSizeCondition.wait( std::chrono::seconds(10) );

    SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult << ", member " << mnWidth << "x" << mnHeight );

    if( mnWidth != 0 && mnHeight != 0 ) {
        aSize.Width = mnWidth;
        aSize.Height = mnHeight;
    }

    return aSize;
}

uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
{
    ::osl::MutexGuard aGuard(m_aMutex);

    if (rArguments.getLength() > 1)
       rArguments[1] >>= maArea;

    awt::Size aSize = getPreferredPlayerWindowSize();

    if( mbFakeVideo )
        preparePlaybin( maURL, nullptr );

    SAL_INFO( "avmedia.gstreamer", AVVERSION "Player::createPlayerWindow " << aSize.Width << "x" << aSize.Height << " length: " << rArguments.getLength() );

    if( aSize.Width <= 0 || aSize.Height <= 0 )
        return nullptr;

    if (rArguments.getLength() <= 2)
    {
        return new ::avmedia::gstreamer::Window;
    }

    sal_IntPtr pIntPtr = 0;
    rArguments[ 2 ] >>= pIntPtr;
    SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
    if (!pParentWindow)
        return nullptr;

    const SystemEnvData* pEnvData = pParentWindow->GetSystemData();
    if (!pEnvData)
        return nullptr;

    // tdf#124027: the position of embedded window is identical w/ the position
    // of media object in all other vclplugs (kf5, gen), in gtk3 w/o gtksink it
    // needs to be translated
    if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
    {
        Point aPoint = pParentWindow->GetPosPixel();
        maArea.X = aPoint.getX();
        maArea.Y = aPoint.getY();
    }

    mbUseGtkSink = false;

    GstElement *pVideosink = static_cast<GstElement*>(pParentWindow->CreateGStreamerSink());
    if (pVideosink)
    {
        if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
            mbUseGtkSink = true;
    }
    else
    {
        if (pEnvData->platform == SystemEnvData::Platform::Wayland)
            pVideosink = gst_element_factory_make("waylandsink""video-output");
        else
            pVideosink = gst_element_factory_make("autovideosink""video-output");
        if (!pVideosink)
            return nullptr;
    }

    rtl::Reference< ::avmedia::gstreamer::Window > xRet = new ::avmedia::gstreamer::Window;

    g_object_set(G_OBJECT(mpPlaybin), "video-sink", pVideosink, nullptr);
    g_object_set(G_OBJECT(mpPlaybin), "force-aspect-ratio"FALSE, nullptr);

    if ((rArguments.getLength() >= 4) && (rArguments[3] >>= pIntPtr) && pIntPtr)
    {
        auto pItem = reinterpret_cast<const avmedia::MediaItem*>(pIntPtr);
        Graphic aGraphic = pItem->getGraphic();
        const text::GraphicCrop& rCrop = pItem->getCrop();
        if (!aGraphic.IsNone() && (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0))
        {
            // The media item has a non-empty cropping set. Try to crop the video accordingly.
            Size aPref = aGraphic.GetPrefSize();
            Size aPixel = aGraphic.GetSizePixel();
            tools::Long nLeft = aPixel.getWidth() * rCrop.Left / aPref.getWidth();
            tools::Long nTop = aPixel.getHeight() * rCrop.Top / aPref.getHeight();
            tools::Long nRight = aPixel.getWidth() * rCrop.Right / aPref.getWidth();
            tools::Long nBottom = aPixel.getHeight() * rCrop.Bottom / aPref.getHeight();
            GstElement* pVideoFilter = gst_element_factory_make("videocrop", nullptr);
            if (pVideoFilter)
            {
                g_object_set(G_OBJECT(pVideoFilter), "left", nLeft, nullptr);
                g_object_set(G_OBJECT(pVideoFilter), "top", nTop, nullptr);
                g_object_set(G_OBJECT(pVideoFilter), "right", nRight, nullptr);
                g_object_set(G_OBJECT(pVideoFilter), "bottom", nBottom, nullptr);
                g_object_set(G_OBJECT(mpPlaybin), "video-filter", pVideoFilter, nullptr);
            }
        }
    }

    if (!mbUseGtkSink)
    {
        mnWindowID = pEnvData->GetWindowHandle(pParentWindow->ImplGetFrame());
        mpDisplay = pEnvData->pDisplay;
        SAL_INFO( "avmedia.gstreamer", AVVERSION "set window id to " << static_cast<int>(mnWindowID) << " XOverlay " << mpXOverlay);
    }
    gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
    if (!mbUseGtkSink && mpXOverlay)
        gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );

    return xRet;
}

uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
{
    ::osl::MutexGuard aGuard(m_aMutex);
    rtl::Reference<FrameGrabber> pFrameGrabber;
    const awt::Size aPrefSize( getPreferredPlayerWindowSize() );

    if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
        pFrameGrabber = FrameGrabber::create( maURL );
    SAL_INFO( "avmedia.gstreamer", AVVERSION "created FrameGrabber " << pFrameGrabber.get() );

    return pFrameGrabber;
}


OUString SAL_CALL Player::getImplementationName()
{
    return u"com.sun.star.comp.avmedia.Player_GStreamer"_ustr;
}


sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
{
    return cppu::supportsService(this, ServiceName);
}


uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
{
    return { u"com.sun.star.media.Player_GStreamer"_ustr };
}

// namespace

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=95 H=96 G=95

¤ Dauer der Verarbeitung: 0.12 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge