Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  FakeESOut.cpp   Sprache: C

 

/*
 * FakeESOut.cpp
 *****************************************************************************
 * Copyright © 2014-2015 VideoLAN and VLC Authors
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "FakeESOut.hpp"
#include "FakeESOutID.hpp"
#include "CommandsQueue.hpp"
#include <vlc_es_out.h>
#include <vlc_block.h>
#include <cassert>
#include <cstdlib>

using namespace adaptive;

namespace adaptive
{
    class EsOutCallbacks
    {
        public:
            /* static callbacks for demuxer */
            static es_out_id_t *es_out_Add( es_out_t *, input_source_t *, const es_format_t * );
            static int es_out_Send( es_out_t *, es_out_id_t *, block_t * );
            static void es_out_Del( es_out_t *, es_out_id_t * );
            static int es_out_Control( es_out_t *, input_source_t *in, int, va_list );
            static void es_out_Destroy( es_out_t * );
            static const struct es_out_callbacks cbs;
            struct Private
            {
                AbstractFakeEsOut *fake;
                es_out_t es_out;
            };
    };
}

const struct es_out_callbacks EsOutCallbacks::cbs = []() constexpr
{
    struct es_out_callbacks cbs {};
    cbs.add = EsOutCallbacks::es_out_Add;
    cbs.send = EsOutCallbacks::es_out_Send;
    cbs.del = EsOutCallbacks::es_out_Del;
    cbs.control = EsOutCallbacks::es_out_Control;
    cbs.destroy = EsOutCallbacks::es_out_Destroy;
    return cbs;
}();

es_out_id_t * EsOutCallbacks::es_out_Add(es_out_t *fakees, input_source_t *, const es_format_t *p_fmt)
{
    AbstractFakeEsOut *me = container_of(fakees, Private, es_out)->fake;
    return me->esOutAdd(p_fmt);
}

int EsOutCallbacks::es_out_Send(es_out_t *fakees, es_out_id_t *p_es, block_t *p_block)
{
    AbstractFakeEsOut *me = container_of(fakees, Private, es_out)->fake;
    return me->esOutSend(p_es, p_block);
}

void EsOutCallbacks::es_out_Del(es_out_t *fakees, es_out_id_t *p_es)
{
    AbstractFakeEsOut *me = container_of(fakees, Private, es_out)->fake;
    me->esOutDel(p_es);
}

int EsOutCallbacks::es_out_Control(es_out_t *fakees, input_source_t *, int i_query, va_list args)
{
    AbstractFakeEsOut *me = container_of(fakees, Private, es_out)->fake;
    return me->esOutControl(i_query, args);
}

void EsOutCallbacks::es_out_Destroy(es_out_t *fakees)
{
    AbstractFakeEsOut *me = container_of(fakees, Private, es_out)->fake;
    me->esOutDestroy();
}

AbstractFakeEsOut::AbstractFakeEsOut()
{
    EsOutCallbacks::Private *priv = new EsOutCallbacks::Private;
    priv->fake = this;
    priv->es_out.cbs = &EsOutCallbacks::cbs;
    esoutpriv = priv;
}

AbstractFakeEsOut::~AbstractFakeEsOut()
{
    delete reinterpret_cast<EsOutCallbacks::Private *>(esoutpriv);
}

AbstractFakeEsOut::operator es_out_t *()
{
    return & reinterpret_cast<EsOutCallbacks::Private *>(esoutpriv)->es_out;
}

FakeESOut::LockedFakeEsOut::LockedFakeEsOut(FakeESOut &q)
{
    p = &q;
    vlc_mutex_lock(&p->lock);
}

FakeESOut::LockedFakeEsOut::~LockedFakeEsOut()
{
    vlc_mutex_unlock(&p->lock);
}

FakeESOut::LockedFakeEsOut::operator es_out_t*()
{
    return (es_out_t *) *p;
}

FakeESOut & FakeESOut::LockedFakeEsOut::operator *()
{
    return *p;
}

FakeESOut * FakeESOut::LockedFakeEsOut::operator ->()
{
    return p;
}

FakeESOut::FakeESOut( es_out_t *es, AbstractCommandsQueue *queue,
                      CommandsFactory *cf )
    : AbstractFakeEsOut()
    , real_es_out( es )
    , extrainfo( nullptr )
    , commandsqueue( queue )
    , commandsfactory( cf )
    , timestamps_offset( 0 )
{
    associated.b_timestamp_set = false;
    expected.b_timestamp_set = false;
    priority = ES_PRIORITY_SELECTABLE_MIN;
    pcrstatus = PcrStatus::Valid;
    pcrcomparison = VLC_TICK_INVALID;
    vlc_mutex_init(&lock);
}

FakeESOut::LockedFakeEsOut FakeESOut::WithLock()
{
    return LockedFakeEsOut(*this);
}

AbstractCommandsQueue * FakeESOut::commandsQueue()
{
    return commandsqueue;
}

CommandsFactory * FakeESOut::commandsFactory() const
{
    return commandsfactory;
}

FakeESOut::~FakeESOut()
{
    recycleAll();
    gc();

    delete commandsqueue;
    delete commandsfactory;
}

void FakeESOut::resetTimestamps()
{
    setExpectedTimestamp(VLC_TICK_INVALID);
    setAssociatedTimestamp(VLC_TICK_INVALID);
    startTimes = SegmentTimes();
}

void FakeESOut::setExpectedTimestamp(vlc_tick_t ts)
{
    if(ts == VLC_TICK_INVALID)
    {
        expected.b_timestamp_set = false;
        timestamps_offset = 0;
    }
    else if(!expected.b_timestamp_set)
    {
        expected.b_timestamp_set = true;
        expected.timestamp = ts;
        expected.b_offset_calculated = false;
    }
}

void FakeESOut::setAssociatedTimestamp(vlc_tick_t ts)
{
    if(ts == VLC_TICK_INVALID)
    {
        associated.b_timestamp_set = false;
        timestamps_offset = 0;
    }
    else if(!associated.b_timestamp_set)
    {
        associated.b_timestamp_set = true;
        associated.timestamp = ts;
        associated.b_offset_calculated = false;
    }
}

void FakeESOut::setAssociatedTimestamp(vlc_tick_t mpegts, vlc_tick_t muxed)
{
    if(mpegts == VLC_TICK_INVALID)
    {
        setAssociatedTimestamp(mpegts);
    }
    else
    {
        associated.b_timestamp_set = true;
        associated.b_offset_calculated = true;
        timestamps_offset = mpegts - muxed;
    }
}

void FakeESOut::setExtraInfoProvider( ExtraFMTInfoInterface *extra )
{
    extrainfo = extra;
}

FakeESOutID * FakeESOut::createNewID( const es_format_t *p_fmt )
{
    es_format_t fmtcopy;
    es_format_Init( &fmtcopy, p_fmt->i_cat, p_fmt->i_codec );
    es_format_Copy( &fmtcopy, p_fmt );
    fmtcopy.i_group = 0; /* Always ignore group for adaptive */
    fmtcopy.i_id = -1;

    fmtcopy.i_priority = priority;

    if( extrainfo )
        extrainfo->fillExtraFMTInfo( &fmtcopy );

    FakeESOutID *es_id = new (std::nothrow) FakeESOutID( this, &fmtcopy, srcID );

    es_format_Clean( &fmtcopy );

    return es_id;
}

void FakeESOut::createOrRecycleRealEsID( AbstractFakeESOutID *es_id_ )
{
    FakeESOutID *es_id = static_cast<FakeESOutID *>(es_id_);
    std::list<FakeESOutID *>::iterator it;
    es_out_id_t *realid = nullptr;

    /* declared ES must are temporary until real ES decl */
    recycle_candidates.insert(recycle_candidates.begin(), declared.begin(), declared.end());
    declared.clear();

    bool b_replace_es = true;
    bool b_preexisting = false;
    bool b_select = false;
    for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
    {
        FakeESOutID *cand = *it;
        if ( cand->isCompatible( es_id ) )
        {
            realid = cand->realESID();
            cand->setRealESID( nullptr );
            delete *it;
            recycle_candidates.erase( it );
            break;
        }
        else if( cand->getFmt()->i_cat == es_id->getFmt()->i_cat && cand->realESID() )
        {
            if( b_replace_es )
            {
                b_preexisting = true;
                /* We need to enforce same selection when not reused
                   Otherwise the es will select any other compatible track
                   and will end this in a activate/select loop when reactivating a track */

                if( !b_select )
                    b_select = hasSelectedEs( cand );
            }
            else /* replace format instead of new ES */
            {
                realid = cand->realESID();
                cand->setRealESID( nullptr );
                es_out_Control( real_es_out, ES_OUT_SET_ES_FMT, realid, es_id->getFmt() );
                delete *it;
                recycle_candidates.erase( it );
                break;
            }
        }
    }

    if( !realid )
    {
        es_format_t fmt;
        es_format_Copy( &fmt, es_id->getFmt() );
        if( b_preexisting && !b_select ) /* was not previously selected on other format */
            fmt.i_priority = ES_PRIORITY_NOT_DEFAULTABLE;
        else
            fmt.i_priority = priority;//ES_PRIORITY_SELECTABLE_MIN;
        realid = es_out_Add( real_es_out, &fmt );
        if( b_preexisting && b_select ) /* was previously selected on other format */
            es_out_Control( real_es_out, ES_OUT_SET_ES, realid );
        es_format_Clean( &fmt );
    }

    es_id->setRealESID( realid );
}

void FakeESOut::setPriority( int p )
{
    priority = p;
}

void FakeESOut::sendData( AbstractFakeESOutID *id_, block_t *p_block )
{
    FakeESOutID *id = static_cast<FakeESOutID *>(id_);
    /* Be sure to notify Data before Sending, because UI would still not pick new ES */
    gc();
    if( !id->realESID() ||
        es_out_Send( real_es_out, id->realESID(), p_block ) != VLC_SUCCESS )
    {
        block_Release( p_block );
    }
    gc();
}

void FakeESOut::sendMeta( int group, const vlc_meta_t *p_meta )
{
    es_out_Control( real_es_out, ES_OUT_SET_GROUP_META, group, p_meta );
}

size_t FakeESOut::esCount() const
{
    if(!declared.empty())
        return declared.size();

    size_t i_count = 0;
    std::list<FakeESOutID *>::const_iterator it;
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
        if( (*it)->realESID() )
            i_count++;
    return i_count;
}

bool FakeESOut::hasSegmentStartTimes() const
{
    return startTimes.media != VLC_TICK_INVALID;
}

void FakeESOut::setSegmentStartTimes(const SegmentTimes &t)
{
    startTimes = t;
}

void FakeESOut::setSegmentProgressTimes(const SegmentTimes &t)
{
    AbstractCommand *c = commandsFactory()->createEsOutMediaProgressCommand(t);
    if(c)
        commandsQueue()->Schedule(c);
}

bool FakeESOut::hasSynchronizationReference() const
{
    return synchronizationReference.second.continuous != VLC_TICK_INVALID;
}

void FakeESOut::setSynchronizationReference(const SynchronizationReference &r)
{
    synchronizationReference = r;
}

void FakeESOut::setSrcID( const SrcID &s )
{
    srcID = s;
}

void FakeESOut::setPCRTrusted( bool b )
{
    pcrstatus = b ? PcrStatus::Valid : PcrStatus::WaitingFirst;
    pcrcomparison = VLC_TICK_INVALID;
}

void FakeESOut::schedulePCRReset()
{
    AbstractCommand *command = commandsfactory->creatEsOutControlResetPCRCommand();
    if( likely(command) )
        commandsqueue->Schedule( command );
}

void FakeESOut::scheduleAllForDeletion()
{
    std::list<FakeESOutID *>::const_iterator it;
    for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
    {
        FakeESOutID *es_id = *it;
        if(!es_id->scheduledForDeletion())
        {
            AbstractCommand *command = commandsfactory->createEsOutDelCommand( es_id );
            if( likely(command) )
            {
                commandsqueue->Schedule( command );
                es_id->setScheduledForDeletion();
            }
        }
    }
}

void FakeESOut::recycleAll()
{
    /* Only used when demux is killed and commands queue is cancelled */
    commandsqueue->Abort( true );
    assert(commandsqueue->isEmpty());
    recycle_candidates.splice( recycle_candidates.end(), fakeesidlist );
}

void FakeESOut::gc()
{
    recycle_candidates.insert(recycle_candidates.begin(), declared.begin(), declared.end());
    declared.clear();

    if( recycle_candidates.empty() )
    {
        return;
    }

    std::list<FakeESOutID *>::iterator it;
    for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
    {
        if( (*it)->realESID() )
        {
            es_out_Control( real_es_out, ES_OUT_SET_ES_STATE, (*it)->realESID(), false );
            es_out_Del( real_es_out, (*it)->realESID() );
        }
        delete *it;
    }
    recycle_candidates.clear();
}

bool FakeESOut::hasSelectedEs() const
{
    std::list<FakeESOutID *> const * lists[2] = {&declared, &fakeesidlist};
    std::list<FakeESOutID *>::const_iterator it;
    for(int i=0; i<2; i++)
        for( it=lists[i]->begin(); it!=lists[i]->end(); ++it )
            if( hasSelectedEs( *it ) )
                return true;
    return false;
}

bool FakeESOut::hasSelectedEs(const AbstractFakeESOutID *id) const
{
    if( id->realESID() )
    {
        bool b_selected;
        return es_out_Control( real_es_out, ES_OUT_GET_ES_STATE,
                               id->realESID(), &b_selected ) == VLC_SUCCESS
               && b_selected;
    }
    return false;
}

bool FakeESOut::restarting() const
{
    bool b = !recycle_candidates.empty();
    return b;
}

void FakeESOut::recycle( AbstractFakeESOutID *id_ )
{
    FakeESOutID *id = static_cast<FakeESOutID *>(id_);
    fakeesidlist.remove( id );
    recycle_candidates.push_back( id );
}

void FakeESOut::milestoneReached()
{
    gc();
}

void FakeESOut::scheduleNecessaryMilestone()
{
    if( b_in_commands_group )
    {
        AbstractCommand *command = commandsfactory->createEsOutMilestoneCommand( this );
         if( likely(command) )
             commandsqueue->Schedule( command );
         b_in_commands_group = false;
    }
}

vlc_tick_t FakeESOut::fixTimestamp(vlc_tick_t ts)
{
    if(ts != VLC_TICK_INVALID)
    {
        if(associated.b_timestamp_set)
        {
            /* Some streams (ex: HLS) have a timestamp mapping
               embedded in some header or metadata (ID3 crap for HLS).
               In that case it needs to remap original timestamp. */

            if(!associated.b_offset_calculated)
            {
                timestamps_offset = associated.timestamp - ts;
                associated.b_offset_calculated = true;
            }
        }
        else if(expected.b_timestamp_set)
        {
            /* Some streams (ex: smooth mp4 without TFDT)
             * do not have proper timestamps and will always start 0.
             * In that case we need to enforce playlist time */

            if(!expected.b_offset_calculated)
            {
                if(ts < VLC_TICK_FROM_SEC(1)) /* Starting 0 */
                    timestamps_offset = expected.timestamp - ts;
                else
                    timestamps_offset = 0;
                expected.b_offset_calculated = true;
            }
        }
        ts += timestamps_offset;
    }
    return ts;
}

vlc_tick_t FakeESOut::applyTimestampContinuity(vlc_tick_t ts)
{
    if(ts == VLC_TICK_INVALID)
        return ts;

    constexpr vlc_tick_t rollover = INT64_C(0x1FFFFFFFF) * 100 / 9;
    constexpr vlc_tick_t halfroll = INT64_C(0x0FFFFFFFF) * 100 / 9;
    if(synchronizationReference.second.segment.demux != VLC_TICK_INVALID)
    {
        while(ts - synchronizationReference.second.segment.demux > halfroll)
        {
            ts -= rollover;
        }
        while(synchronizationReference.second.segment.demux - ts > halfroll)
        {
            ts += rollover;
        }
    }

    if(synchronizationReference.second.segment.demux != VLC_TICK_INVALID &&
       synchronizationReference.second.continuous != VLC_TICK_INVALID)
    {
        vlc_tick_t continuityoffset = synchronizationReference.second.continuous -
                                      synchronizationReference.second.segment.demux;

        /* avoid triggering false roll when reference is 13 hours away */
        if(ts - synchronizationReference.second.segment.demux > halfroll / 2)
            synchronizationReference.second.offsetBy(halfroll / 2);

        ts += continuityoffset;
        assert(ts >= VLC_TICK_INVALID);
    }
    else /* First synchronization point */
    {
        synchronizationReference.second.segment = startTimes;
        synchronizationReference.second.segment.demux = ts;
        synchronizationReference.second.continuous = ts;
    }

    return ts;
}

void FakeESOut::declareEs(const es_format_t *fmt)
{
    /* Declared ES are only visible until stream data flows.
       They are then recycled to create the real ES. */

    if(!recycle_candidates.empty() || !fakeesidlist.empty())
    {
        assert(recycle_candidates.empty());
        assert(fakeesidlist.empty());
        return;
    }

    FakeESOutID *fakeid = createNewID(fmt);
    if( likely(fakeid) )
    {
        es_out_id_t *realid = es_out_Add( real_es_out, fakeid->getFmt() );
        if( likely(realid) )
        {
            fakeid->setRealESID(realid);
            declared.push_front(fakeid);
        }
        else delete fakeid;
    }
}

/* Static callbacks */
/* Always pass Fake ES ID to slave demuxes, it is just an opaque struct to them */
es_out_id_t * FakeESOut::esOutAdd(const es_format_t *p_fmt)
{
    vlc_mutex_locker locker(&lock);

    if( p_fmt->i_cat != VIDEO_ES && p_fmt->i_cat != AUDIO_ES && p_fmt->i_cat != SPU_ES )
        return nullptr;

    /* Feed the slave demux/stream_Demux with FakeESOutID struct,
     * we'll create real ES later on main demux on execution */

    FakeESOutID *es_id = createNewID( p_fmt );
    if( likely(es_id) )
    {
        assert(!es_id->scheduledForDeletion());
        AbstractCommand *command = commandsfactory->createEsOutAddCommand( es_id );
        if( likely(command) )
        {
            fakeesidlist.push_back(es_id);
            commandsqueue->Schedule( command );
            b_in_commands_group = true;
            return reinterpret_cast<es_out_id_t *>(es_id);
        }
        else
        {
            delete es_id;
        }
    }
    return nullptr;
}

int FakeESOut::esOutSend(es_out_id_t *p_es, block_t *p_block)
{
    vlc_mutex_locker locker(&lock);

    scheduleNecessaryMilestone();

    FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
    assert(!es_id->scheduledForDeletion());

    p_block->i_dts = fixTimestamp( p_block->i_dts );
    p_block->i_pts = fixTimestamp( p_block->i_pts );

    if(!hasSynchronizationReference() && p_block->i_dts != VLC_TICK_INVALID)
    {
        synchronizationReference.second.segment = startTimes;
        synchronizationReference.second.continuous = p_block->i_dts;
        synchronizationReference.second.segment.demux = p_block->i_dts;
        assert(hasSynchronizationReference());
    }

    p_block->i_dts = applyTimestampContinuity( p_block->i_dts );
    p_block->i_pts = applyTimestampContinuity( p_block->i_pts );

    SegmentTimes times;
    if(p_block->i_dts != VLC_TICK_INVALID)
    {
        times = synchronizationReference.second.segment;
        times.offsetBy(p_block->i_dts - times.demux);
        assert(times.media != VLC_TICK_INVALID);
        /* For bogus PCR */
        pcrcomparison = p_block->i_dts;
    }

   AbstractCommand *command = commandsfactory->createEsOutSendCommand( es_id, times, p_block );
    if( likely(command) )
    {
        commandsqueue->Schedule( command );
        return VLC_SUCCESS;
    }
    return VLC_EGENERIC;
}

void FakeESOut::esOutDel(es_out_id_t *p_es)
{
    vlc_mutex_locker locker(&lock);

    FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
    AbstractCommand *command = commandsfactory->createEsOutDelCommand( es_id );
    if( likely(command) )
    {
        es_id->setScheduledForDeletion();
        commandsqueue->Schedule( command );
    }
    b_in_commands_group = true;
}

int FakeESOut::esOutControl(int i_query, va_list args)
{
    vlc_mutex_locker locker(&lock);

    scheduleNecessaryMilestone();

    switch( i_query )
    {
        case ES_OUT_SET_PCR:
        case ES_OUT_SET_GROUP_PCR:
        {
            int i_group;
            if( i_query == ES_OUT_SET_GROUP_PCR )
                i_group = va_arg( args, int );
            else
                i_group = 0;
            vlc_tick_t  pcr = va_arg( args, vlc_tick_t );

            SegmentTimes times;

            if( pcrstatus != PcrStatus::WaitingFirst &&
                synchronizationReference.second.segment.demux != VLC_TICK_INVALID )
            {
                pcr = fixTimestamp( pcr );

                pcr = applyTimestampContinuity( pcr );

                times = synchronizationReference.second.segment;
                times.offsetBy(pcr - times.demux);
            }
            else pcr = VLC_TICK_INVALID;

            /* Offset/Bad PCR handling */
            if( pcrstatus == PcrStatus::WaitingFirst )
            {
                pcrstatus = PcrStatus::Evaluating;
            }
            else if( pcrstatus == PcrStatus::Evaluating &&
                pcrcomparison != VLC_TICK_INVALID && pcr != VLC_TICK_INVALID )
            {
                if( std::llabs(pcr - pcrcomparison) > vlc_tick_from_sec(2) )
                    pcrstatus = PcrStatus::Broken;
                else
                    pcrstatus = PcrStatus::Valid;
            }

            if( pcrstatus == PcrStatus::Broken )
            {
                times = SegmentTimes();
                pcr = VLC_TICK_INVALID;
            }

            AbstractCommand *command = commandsfactory->createEsOutControlPCRCommand( i_group, times, pcr );
            if( likely(command) )
            {
                commandsqueue->Schedule( command );
                return VLC_SUCCESS;
            }
        }
        break;

        case ES_OUT_SET_GROUP_META:
        {
            static_cast<void>(va_arg( args, int )); /* ignore group */
            const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
            AbstractCommand *command = commandsfactory->createEsOutMetaCommand( this,-1, p_meta );
            if( likely(command) )
            {
                commandsqueue->Schedule( command );
                return VLC_SUCCESS;
            }
        }
        break;

        /* For others, we don't have the delorean, so always lie */
        case ES_OUT_GET_ES_STATE:
        {
            static_cast<void>(va_arg( args, es_out_id_t * ));
            bool *pb = va_arg( args, bool * );
            *pb = true;
            return VLC_SUCCESS;
        }

        case ES_OUT_SET_ES:
        case ES_OUT_SET_ES_DEFAULT:
        case ES_OUT_SET_ES_STATE:
            return VLC_SUCCESS;
    }

    return VLC_EGENERIC;
}

void FakeESOut::esOutDestroy()
{
    vlc_mutex_locker locker(&lock);

    scheduleNecessaryMilestone();

    AbstractCommand *command = commandsfactory->createEsOutDestroyCommand();
    if( likely(command) )
        commandsqueue->Schedule( command );
}
/* !Static callbacks */

Messung
CHZG
869810094

¤ Dauer der Verarbeitung: 0.8 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

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

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=418204