Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/codec/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 20 kB image not shown  

Quelle  vpx_alpha.c   Sprache: C

 

// SPDX-License-Identifier: LGPL-2.1-or-later

// vpx_alpha.c : pseudo-decoder for VP8/VP9 streams with alpha side-channel
// Copyright © 2023 VideoLabs, VLC authors and VideoLAN

// Authors: Steve Lhomme <robux4@videolabs.io>
//          Alexandre Janniaux <ajanni@videolabs.io>

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

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
#include <vlc_ancillary.h>
#include <vlc_modules.h>
#include <vlc_atomic.h>
#include <vlc_picture_pool.h>

#include "alpha_combine.h"

static int OpenDecoder(vlc_object_t *);
static void CloseDecoder(vlc_object_t *);

vlc_module_begin ()
    set_description(N_("VPx+alpha video decoder"))
    set_capability("video decoder", 150)
    set_callbacks(OpenDecoder, CloseDecoder)
    set_subcategory(SUBCAT_INPUT_VCODEC)
vlc_module_end ()

struct vp_decoder
{
    decoder_t           dec;
    es_format_t         fmt_in;
    es_format_t         fmt_out;
    vlc_picture_chain_t decoded;
};

typedef struct
{
    struct vp_decoder *opaque;
    struct vp_decoder *alpha;
    vlc_mutex_t       lock;
    vlc_video_context *vctx;
    picture_t *(*pf_combine)(decoder_t *, picture_t *opaque, picture_t *alpha, vlc_video_context *);

    struct VLC_VECTOR(vlc_tick_t) missing_alpha;

    picture_pool_t    *pool;
} vpx_alpha;


static vlc_decoder_device *GetDevice( decoder_t *dec )
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    vpx_alpha *p_sys = bdec->p_sys;
    vlc_mutex_lock(&p_sys->lock);
    vlc_decoder_device *res = decoder_GetDecoderDevice(bdec);
    vlc_mutex_unlock(&p_sys->lock);
    return res;
}

struct cpu_alpha_context
{
    vlc_atomic_rc_t    rc;
    picture_context_t  ctx;
    picture_t          *opaque;
    picture_t          *alpha; // may be NULL if the alpha layer was missing
    struct pic_alpha_plane *plane;
};

static void cpu_alpha_destroy(picture_context_t *ctx)
{
    struct cpu_alpha_context *pctx = container_of(ctx, struct cpu_alpha_context, ctx);

    if (!vlc_atomic_rc_dec(&pctx->rc))
        return;

    free(pctx->plane);
    picture_Release(pctx->opaque);
    if (pctx->alpha)
        picture_Release(pctx->alpha);
    free(pctx);
}

static picture_context_t *cpu_alpha_copy(picture_context_t *src)
{
    struct cpu_alpha_context *pctx = container_of(src, struct cpu_alpha_context, ctx);

    vlc_atomic_rc_inc(&pctx->rc);
    return &pctx->ctx;
}

struct pic_alpha_plane
{
    plane_t p;
    uint8_t buffer[];
};

static void DestroyPoolPic(picture_t *pic)
{
    free(pic->p_sys);
}

static picture_t *CombinePicturesCPU(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
{
    assert(vctx == NULL); VLC_UNUSED(vctx);
    vpx_alpha *p_sys = bdec->p_sys;
    picture_t *out = picture_pool_Wait(p_sys->pool);
    if (out == NULL)
        return NULL;

    struct cpu_alpha_context *alpha_ctx = calloc(1, sizeof(*alpha_ctx));
    if (unlikely(alpha_ctx == NULL))
    {
        picture_Release(out);
        return NULL;
    }
    vlc_atomic_rc_init(&alpha_ctx->rc);
    alpha_ctx->ctx = (picture_context_t) {
        cpu_alpha_destroy, cpu_alpha_copy, NULL
    };
    alpha_ctx->opaque = picture_Hold(opaque);
    alpha_ctx->alpha  = alpha ? picture_Hold(alpha) : NULL;
    out->context = &alpha_ctx->ctx;

    for (int i=0; i<opaque->i_planes; i++)
        out->p[i] = opaque->p[i];

    if (alpha)
    {
        out->p[opaque->i_planes] = alpha->p[0];
        return out;
    }

    // use the dummy opaque plane attached in the picture context
    struct pic_alpha_plane *p = alpha_ctx->plane;
    if (p == NULL)
    {
        int plane_size = bdec->fmt_out.video.i_width * bdec->fmt_out.video.i_height;
        p = malloc(sizeof(*p) + plane_size);
        if (unlikely(p == NULL))
        {
            picture_Release(out);
            return NULL;
        }

        p->p.i_lines = bdec->fmt_out.video.i_height;
        p->p.i_visible_lines = bdec->fmt_out.video.i_y_offset + bdec->fmt_out.video.i_visible_height;

        p->p.i_pitch = bdec->fmt_out.video.i_width;
        p->p.i_visible_pitch = bdec->fmt_out.video.i_x_offset + bdec->fmt_out.video.i_visible_width;
        p->p.i_pixel_pitch = 1;
        p->p.p_pixels = p->buffer;
        memset(p->p.p_pixels, 0xFF, plane_size);

        alpha_ctx->plane = p;
    }
    out->p[opaque->i_planes] = p->p;
    return out;
}

static int SetupCPU(decoder_t *bdec)
{
    vpx_alpha *p_sys = bdec->p_sys;
    picture_t *pics[4];
    size_t i=0;

    if (p_sys->pool)
    {
        picture_pool_Release(p_sys->pool);
        p_sys->pool = NULL;
    }

    for (; i<ARRAY_SIZE(pics); i++)
    {
        picture_resource_t res = { .pf_destroy = DestroyPoolPic };
        pics[i] = picture_NewFromResource(&bdec->fmt_out.video, &res);
        if (pics[i] == NULL)
            goto error;
    }
    p_sys->pool = picture_pool_New(ARRAY_SIZE(pics), pics);
    if (p_sys->pool)
        return VLC_SUCCESS;

error:
    while (i > 0)
        picture_Release(pics[--i]);
    return VLC_EGENERIC;
}

static picture_t *CombineKeepAlpha(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
{
    VLC_UNUSED(bdec); VLC_UNUSED(opaque); VLC_UNUSED(vctx);
    return picture_Hold(alpha);
}

static picture_t *CombineKeepOpaque(decoder_t *bdec, picture_t *opaque, picture_t *alpha, vlc_video_context *vctx)
{
    VLC_UNUSED(bdec); VLC_UNUSED(alpha); VLC_UNUSED(vctx);
    return picture_Hold(opaque);
}

static int FormatUpdate( decoder_t *dec, vlc_video_context *vctx )
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    vpx_alpha *p_sys = bdec->p_sys;
    vlc_mutex_lock(&p_sys->lock);

    int res = VLC_SUCCESS;
    if (dec == &p_sys->alpha->dec)
    {
        if (es_format_IsSimilar(&p_sys->alpha->fmt_out, &dec->fmt_out))
            // nothing changed
            goto done;
        es_format_Clean(&p_sys->alpha->fmt_out);
        es_format_Copy(&p_sys->alpha->fmt_out, &dec->fmt_out);
        if (p_sys->opaque->dec.fmt_out.video.i_chroma == 0)
        {
            // not ready
            bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
            p_sys->pf_combine = CombineKeepAlpha;
            goto done;
        }
    }
    else
    {
        if (es_format_IsSimilar(&p_sys->opaque->fmt_out, &dec->fmt_out))
            // nothing changed
            goto done;
        es_format_Clean(&p_sys->opaque->fmt_out);
        es_format_Copy(&p_sys->opaque->fmt_out, &dec->fmt_out);
        if (p_sys->alpha->dec.fmt_out.video.i_chroma == 0)
        {
            // not ready
            bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = dec->fmt_out.video.i_chroma;
            p_sys->pf_combine = CombineKeepOpaque;
            if (p_sys->missing_alpha.size == 0)
                goto done;
            // we need to send pictures without waiting for the alpha
        }
    }
    es_format_Clean(&bdec->fmt_out);
    es_format_Copy(&bdec->fmt_out, &dec->fmt_out);

    switch (dec->fmt_out.video.i_chroma)
    {
        case VLC_CODEC_I420:
            // TODO support other formats
            bdec->fmt_out.video.i_chroma = bdec->fmt_out.i_codec = VLC_CODEC_YUV420A;
            res = SetupCPU(bdec);
            if (res == VLC_SUCCESS)
            {
                p_sys->pf_combine = CombinePicturesCPU;
            }
            break;
#ifdef _WIN32
        case VLC_CODEC_D3D11_OPAQUE:
            if (dec == &p_sys->alpha->dec)
            {
                switch (p_sys->opaque->dec.fmt_out.video.i_chroma)
                {
                    case VLC_CODEC_D3D11_OPAQUE:
                        res = SetupD3D11(bdec, vctx, &p_sys->vctx);
                        if (res == VLC_SUCCESS)
                        {
                            p_sys->pf_combine = CombineD3D11;
                            vctx = p_sys->vctx;
                        }
                        break;
                    default:
                        msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->opaque->dec.fmt_out.video.i_chroma);
                        res = VLC_EGENERIC;
                }
            }
            else
            {
                switch (p_sys->alpha->dec.fmt_out.video.i_chroma)
                {
                    case VLC_CODEC_D3D11_OPAQUE:
                        res = SetupD3D11(bdec, vctx, &p_sys->vctx);
                        if (res == VLC_SUCCESS)
                        {
                            p_sys->pf_combine = CombineD3D11;
                            vctx = p_sys->vctx;
                        }
                        break;
                    default:
                        msg_Err(dec, "unsupported opaque D3D11 combination %4.4s", (char*)&p_sys->alpha->dec.fmt_out.video.i_chroma);
                        res = VLC_EGENERIC;
                }
            }
            break;
#endif // _WIN32
        default:
            msg_Warn(dec, "unsupported decoder output %4.4s, not handling alpha",
                     (char*)&dec->fmt_out.video.i_chroma);
            break;
    }
    if (res == VLC_SUCCESS)
        res = decoder_UpdateVideoOutput(bdec, vctx);

done:
    vlc_mutex_unlock(&p_sys->lock);
    return res;
}

static bool CheckMissingAlpha(decoder_t *bdec, vlc_tick_t pts)
{
    vpx_alpha *p_sys = bdec->p_sys;
    vlc_tick_t missing_pts;
    vlc_vector_foreach(missing_pts, &p_sys->missing_alpha)
    {
        if (missing_pts == pts)
            return true;
        if (missing_pts < pts)
            break;
    }
    return false;
}

static void PurgeMissingAlpha(decoder_t *bdec, vlc_tick_t pts)
{
    vpx_alpha *p_sys = bdec->p_sys;
    size_t count = 0;
    vlc_tick_t missing_pts;
    vlc_vector_foreach(missing_pts, &p_sys->missing_alpha)
    {
        if (missing_pts > pts)
            break; // in VPx there are not frames out of order
        count++;
    }
    if (count > 0)
        vlc_vector_remove_slice(&p_sys->missing_alpha, 0, count);
}

static bool SendMergedLocked(decoder_t *bdec)
{
    vpx_alpha *p_sys = bdec->p_sys;

    picture_t *opaque = vlc_picture_chain_PeekFront(&p_sys->opaque->decoded);
    picture_t *alpha  = vlc_picture_chain_PeekFront(&p_sys->alpha->decoded);
    while (opaque != NULL && (alpha != NULL || CheckMissingAlpha(bdec, opaque->date)))
    {
        if (alpha == NULL || opaque->date == alpha->date)
        {
            // dequeue if both first of the queue match DTS/PTS
            // merge alpha and opaque pictures with same DTS/PTS and send them
            picture_t *out = p_sys->pf_combine(bdec, opaque, alpha, p_sys->vctx);
            if (out != NULL)
            {
                video_format_CopyCropAr(&out->format, &opaque->format);
                picture_CopyProperties(out, opaque);
            }

            vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
            picture_Release(opaque);

            if (alpha != NULL)
            {
                vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
                picture_Release(alpha);
            }

            PurgeMissingAlpha(bdec, opaque->date);
            if (out == NULL)
                break;

            decoder_QueueVideo(bdec, out);
            return true;
        }

        // in case decoders drop some frames
        if (opaque->date > alpha->date)
        {
            msg_Dbg(bdec, "missing decoded opaque at %" PRId64 " dropping alpha", alpha->date);
            vlc_picture_chain_PopFront(&p_sys->alpha->decoded);
            picture_Release(alpha);
        }
        else
        {
            msg_Dbg(bdec, "missing decoded alpha at %" PRId64 " dropping opaque", opaque->date);
            vlc_picture_chain_PopFront(&p_sys->opaque->decoded);
            picture_Release(opaque);
        }
        opaque = vlc_picture_chain_PeekFront(&p_sys->opaque->decoded);
        alpha  = vlc_picture_chain_PeekFront(&p_sys->alpha->decoded);
    }
    return false;
}

static void QueuePic( decoder_t *dec, picture_t *pic )
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    vpx_alpha *p_sys = bdec->p_sys;

    vlc_mutex_lock(&p_sys->lock);
    if (dec == &p_sys->alpha->dec)
    {
        vlc_picture_chain_Append(&p_sys->alpha->decoded, pic);
    }
    else
    {
        vlc_picture_chain_Append(&p_sys->opaque->decoded, pic);
    }

    SendMergedLocked(bdec);
    vlc_mutex_unlock(&p_sys->lock);
}

static vlc_tick_t GetDisplayDate( decoder_t *dec, vlc_tick_t sys_now, vlc_tick_t ts)
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    return decoder_GetDisplayDate(bdec, sys_now, ts);
}

static float GetDisplayRate( decoder_t *dec )
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    return decoder_GetDisplayRate(bdec);
}

static int GetAttachments( decoder_t *dec,
                            input_attachment_t ***ppp_attachment,
                            int *pi_attachment )
{
    decoder_t *bdec = container_of(vlc_object_parent(dec), decoder_t, obj);
    return decoder_GetInputAttachments(bdec, ppp_attachment, pi_attachment);
}

struct alpha_frame
{
    vlc_atomic_rc_t rc;
    vlc_frame_t     *frame; // source frame
    vlc_frame_t     opaque; // opaque bitstream
    vlc_frame_t     alpha;  // alpha bitstream
};

static void ReleaseAlphaFrame(vlc_frame_t *frame)
{
    struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, alpha);

    if (vlc_atomic_rc_dec(&alpha_frame->rc))
    {
        vlc_frame_Release(alpha_frame->frame);
        free(alpha_frame);
    }
}

static void ReleaseOpaqueFrame(vlc_frame_t *frame)
{
    struct alpha_frame *alpha_frame = container_of(frame, struct alpha_frame, opaque);

    if (vlc_atomic_rc_dec(&alpha_frame->rc))
    {
        vlc_frame_Release(alpha_frame->frame);
        free(alpha_frame);
    }
}

static int Decode( decoder_t *dec, vlc_frame_t *frame )
{
    vpx_alpha *p_sys = dec->p_sys;

    int res;
    if (frame != NULL)
    {
        struct vlc_ancillary *p_alpha;
        p_alpha = vlc_frame_GetAncillary(frame, VLC_ANCILLARY_ID_VPX_ALPHA);

        struct alpha_frame *alpha_frame = malloc(sizeof(*alpha_frame));
        if (unlikely(alpha_frame == NULL))
            return VLCDEC_ECRITICAL;

        vlc_vpx_alpha_t *alpha = p_alpha ? vlc_ancillary_GetData(p_alpha) : NULL;

        static const struct vlc_frame_callbacks cbs_alpha = {
            ReleaseAlphaFrame,
        };

        static const struct vlc_frame_callbacks cbs_opaque = {
            ReleaseOpaqueFrame,
        };

        alpha_frame->frame = frame;
        vlc_atomic_rc_init(&alpha_frame->rc);

        bool b_has_alpha = alpha != NULL;
        if (b_has_alpha)
        {
            vlc_frame_Init(&alpha_frame->alpha, &cbs_alpha, alpha->data, alpha->size);
            vlc_atomic_rc_inc(&alpha_frame->rc);
            alpha_frame->alpha.i_dts = frame->i_dts;
            alpha_frame->alpha.i_pts = frame->i_pts;
            alpha_frame->alpha.i_length = frame->i_length;
            alpha_frame->alpha.i_flags = frame->i_flags;
        }

        vlc_frame_Init(&alpha_frame->opaque, &cbs_opaque, frame->p_buffer, frame->i_buffer);
        alpha_frame->opaque.i_dts = frame->i_dts;
        alpha_frame->opaque.i_pts = frame->i_pts;
        alpha_frame->opaque.i_length = frame->i_length;
        alpha_frame->opaque.i_flags = frame->i_flags;

        if (b_has_alpha)
        {
            res = p_sys->alpha->dec.pf_decode(&p_sys->alpha->dec, &alpha_frame->alpha);
            if (res != VLCDEC_SUCCESS)
            {
                ReleaseAlphaFrame(&alpha_frame->alpha);
                return VLCDEC_ECRITICAL;
            }
        }
        else
        {
            assert(frame->i_pts != VLC_TICK_INVALID);
            vlc_vector_push(&p_sys->missing_alpha, frame->i_pts);
        }

        res = p_sys->opaque->dec.pf_decode(&p_sys->opaque->dec, &alpha_frame->opaque);
        if (res != VLCDEC_SUCCESS)
        {
            ReleaseOpaqueFrame(&alpha_frame->opaque);
            return VLCDEC_ECRITICAL;
        }
    }
    else
    {
        // drain
        vlc_mutex_lock(&p_sys->lock);
        while ( !vlc_picture_chain_IsEmpty(&p_sys->opaque->decoded) &&
                !vlc_picture_chain_IsEmpty(&p_sys->alpha->decoded) )
            SendMergedLocked(dec);

        // drain remaining pushed pictures from one decoder
        picture_t *picture;
        while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
            picture_Release(picture);
        while ((picture = vlc_picture_chain_PopFront(&p_sys->opaque->decoded)) != NULL)
            picture_Release(picture);
        vlc_mutex_unlock(&p_sys->lock);
    }

    return VLCDEC_SUCCESS;
}

static void Flush( decoder_t *dec )
{
    vpx_alpha *p_sys = dec->p_sys;

    if ( p_sys->opaque->dec.pf_flush != NULL )
        p_sys->opaque->dec.pf_flush( &p_sys->opaque->dec );

    if ( p_sys->alpha->dec.pf_flush != NULL )
        p_sys->alpha->dec.pf_flush( &p_sys->alpha->dec );

    vlc_mutex_lock(&p_sys->lock);
    picture_t *picture;
    while ((picture = vlc_picture_chain_PopFront(&p_sys->opaque->decoded)) != NULL)
        picture_Release(picture);
    while ((picture = vlc_picture_chain_PopFront(&p_sys->alpha->decoded)) != NULL)
        picture_Release(picture);
    vlc_mutex_unlock(&p_sys->lock);
}

static void DestroyDecoder(struct vp_decoder *vpdec)
{
    decoder_Clean(&vpdec->dec);
    es_format_Clean(&vpdec->fmt_out);
    es_format_Clean(&vpdec->fmt_in);
    vlc_object_delete(&vpdec->dec);
}

int OpenDecoder(vlc_object_t *o)
{
    decoder_t *dec = container_of(o, decoder_t, obj);
    if (dec->fmt_in->i_codec != VLC_CODEC_VP8 && dec->fmt_in->i_codec != VLC_CODEC_VP9)
        return VLC_ENOTSUP;
    if (!es_format_HasVpxAlpha(dec->fmt_in))
        return VLC_ENOTSUP;

    vpx_alpha *p_sys = vlc_obj_calloc(o, 1, sizeof(*p_sys));
    if (unlikely(p_sys == NULL))
        return VLC_ENOMEM;

    p_sys->opaque = vlc_object_create( o, sizeof( *p_sys->opaque ) );
    if (unlikely(p_sys->opaque == NULL))
        return VLC_EGENERIC;
    p_sys->alpha = vlc_object_create( o, sizeof( *p_sys->alpha ) );
    if (unlikely(p_sys->alpha == NULL))
    {
        vlc_object_delete(&p_sys->opaque->dec);
        return VLC_EGENERIC;
    }

    es_format_t fmt;
    es_format_Copy(&fmt, dec->fmt_in);
    if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
        fmt.i_codec = VLC_CODEC_VP8;
    else
        fmt.i_codec = VLC_CODEC_VP9;
    fmt.i_level &= ~0x1000;
    decoder_Init( &p_sys->opaque->dec, &p_sys->opaque->fmt_in, &fmt );
    vlc_picture_chain_Init(&p_sys->opaque->decoded);
    es_format_Init(&p_sys->opaque->fmt_out, VIDEO_ES, 0);

    if (dec->fmt_in->i_codec == VLC_CODEC_VP8)
        fmt.i_codec = VLC_CODEC_VP8ALPHA_ES;
    else
        fmt.i_codec = VLC_CODEC_VP9ALPHA_ES;
    decoder_Init( &p_sys->alpha->dec,  &p_sys->alpha->fmt_in,  &fmt );
    vlc_picture_chain_Init(&p_sys->alpha->decoded);
    es_format_Init(&p_sys->alpha->fmt_out, VIDEO_ES, 0);

    es_format_Clean(&fmt);

    vlc_mutex_init(&p_sys->lock);
    vlc_vector_init(&p_sys->missing_alpha);
    dec->p_sys = p_sys;

    static const struct decoder_owner_callbacks dec_cbs =
    {
        .video = {
            .get_device = GetDevice,
            .format_update = FormatUpdate,
            .queue = QueuePic,
            .get_display_date = GetDisplayDate,
            .get_display_rate = GetDisplayRate,
        },
        .get_attachments = GetAttachments,
    };

    p_sys->opaque->dec.cbs = &dec_cbs;
    decoder_LoadModule(&p_sys->opaque->dec, false, true);
    if (p_sys->opaque->dec.p_module == NULL)
    {
        DestroyDecoder(p_sys->alpha);
        DestroyDecoder(p_sys->opaque);
        return VLC_EGENERIC;
    }
    p_sys->alpha->dec.cbs = &dec_cbs;
    decoder_LoadModule(&p_sys->alpha->dec, false, true);
    if (p_sys->alpha->dec.p_module == NULL)
    {
        DestroyDecoder(p_sys->alpha);
        DestroyDecoder(p_sys->opaque);
        return VLC_EGENERIC;
    }

    dec->pf_decode = Decode;
    dec->pf_flush = Flush;

    return VLC_SUCCESS;
}

void CloseDecoder(vlc_object_t *o)
{
    decoder_t *dec = container_of(o, decoder_t, obj);
    vpx_alpha *p_sys = dec->p_sys;

    DestroyDecoder(p_sys->opaque);
    DestroyDecoder(p_sys->alpha);

    if (p_sys->pool)
        picture_pool_Release(p_sys->pool);

    if (p_sys->vctx)
        vlc_video_context_Release(p_sys->vctx);
    vlc_vector_destroy(&p_sys->missing_alpha);
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© 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.