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

Quelle  decklink.cpp   Sprache: C

 

/*****************************************************************************
 * decklink.cpp: BlackMagic DeckLink SDI output module
 *****************************************************************************
 * Copyright (C) 2012-2013 Rafaël Carré
 * Copyright (C) 2009 Michael Niedermayer <michaelni@gmx.at>
 * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
 *
 * Authors: Rafaël Carré <funman@videolan.org>
 *
 * 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 library 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.
 *****************************************************************************/


/*
 * TODO: test non stereo audio
 */


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

#include <cinttypes>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_threads.h>

#include <vlc_vout_display.h>

#include <vlc_block.h>
#include <vlc_aout.h>
#include <vlc_cxx_helpers.hpp>

#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

#include "../access/vlc_decklink.h"
#include "../stream_out/sdi/V210.hpp"
#include "../stream_out/sdi/Ancillary.hpp"
#include "../stream_out/sdi/DBMHelper.hpp"
#include "../stream_out/sdi/SDIGenerator.hpp"
#ifndef _WIN32
#include <DeckLinkAPIDispatch.cpp>
#endif
#include <DeckLinkAPIVersion.h>
#if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
 #define IID_IDeckLinkProfileAttributes IID_IDeckLinkAttributes
 #define IDeckLinkProfileAttributes IDeckLinkAttributes
 #define bmdVideoConnectionUnspecified 0
#endif

#define FRAME_SIZE 1920
#define CHANNELS_MAX 6

#if 0
static const int pi_channels_maps[CHANNELS_MAX+1] =
{
    0,
    AOUT_CHAN_CENTER,
    AOUT_CHANS_STEREO,
    AOUT_CHANS_3_0,
    AOUT_CHANS_4_0,
    AOUT_CHANS_5_0,
    AOUT_CHANS_5_1,
};
#endif

#define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
#define NOSIGNAL_INDEX_LONGTEXT N_(\
    "Timelength after which we assume there is no signal.\n"\
    "After this delay we black out the video."\
    )

#define AFD_INDEX_TEXT N_("Active Format Descriptor value")

#define AR_INDEX_TEXT N_("Aspect Ratio")
#define AR_INDEX_LONGTEXT N_("Aspect Ratio of the source picture.")

#define AFDLINE_INDEX_TEXT N_("Active Format Descriptor line")
#define AFDLINE_INDEX_LONGTEXT N_("VBI line on which to output Active Format Descriptor.")

#define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss")

#define CARD_INDEX_TEXT N_("Output card")
#define CARD_INDEX_LONGTEXT N_(\
    "DeckLink output card, if multiple exist. " \
    "The cards are numbered from 0.")

#define MODE_TEXT N_("Desired output mode")
#define MODE_LONGTEXT N_(\
    "Desired output mode for DeckLink output. " \
    "This value should be a FOURCC code in textual " \
    "form, e.g. \"ntsc\".")

#define RATE_TEXT N_("Audio samplerate (Hz)")
#define RATE_LONGTEXT N_(\
    "Audio sampling rate (in hertz) for DeckLink output. " \
    "0 disables audio output.")

#define CHANNELS_TEXT N_("Number of audio channels")
#define CHANNELS_LONGTEXT N_(\
    "Number of output channels for DeckLink output. " \
    "Must be 2, 8 or 16. 0 disables audio output.")

#define VIDEO_CONNECTION_TEXT N_("Video connection")
#define VIDEO_CONNECTION_LONGTEXT N_(\
    "Video connection for DeckLink output.")

#define VIDEO_TENBITS_TEXT N_("10 bits")
#define VIDEO_TENBITS_LONGTEXT N_(\
    "Use 10 bits per pixel for video frames.")

#define CFG_PREFIX "decklink-output-"
#define VIDEO_CFG_PREFIX "decklink-vout-"
#define AUDIO_CFG_PREFIX "decklink-aout-"

/* Video Connections */
static const char *const ppsz_videoconns[] = {
    "sdi",
    "hdmi",
    "opticalsdi",
    "component",
    "composite",
    "svideo"
};
static const char *const ppsz_videoconns_text[] = {
    N_("SDI"),
    N_("HDMI"),
    N_("Optical SDI"),
    N_("Component"),
    N_("Composite"),
    N_("S-video"),
};
static const BMDVideoConnection rgbmd_videoconns[] =
{
    bmdVideoConnectionSDI,
    bmdVideoConnectionHDMI,
    bmdVideoConnectionOpticalSDI,
    bmdVideoConnectionComponent,
    bmdVideoConnectionComposite,
    bmdVideoConnectionSVideo,
};
static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns), "videoconn arrays messed up");
static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns_text), "videoconn arrays messed up");

static const int rgi_afd_values[] = {
    0, 2, 3, 4, 8, 9, 10, 11, 13, 14, 15,
};
static const char * const rgsz_afd_text[] = {
    /* Note: Skip further translation - too technical */
    N_("Undefined"),
    "Box 16:9 (top aligned)",
    "Box 14:9 (top aligned)",
    "Box > 16:9 (centre aligned)",
    "Same as coded frame (full frame)",
    "4:3  (centre aligned)",
    "16:9 (centre aligned)",
    "14:9 (centre aligned)",
    "4:3  (with shoot and protect 14:9 centre)",
    "16:9 (with shoot and protect 14:9 centre)",
    "16:9 (with shoot and protect  4:3 centre)",
};
static_assert(ARRAY_SIZE(rgi_afd_values) == ARRAY_SIZE(rgsz_afd_text), "afd arrays messed up");

static const int rgi_ar_values[] = {
    0, 1,
};
static const char * const rgsz_ar_text[] = {
    "4:3", "16:9",
};
static_assert(ARRAY_SIZE(rgi_ar_values) == ARRAY_SIZE(rgsz_ar_text), "afd arrays messed up");

namespace {

/* Only one audio output module and one video output module
 * can be used per process.
 * We use a static mutex in audio/video submodules entry points.  */

struct decklink_sys_t
{
    /* With LOCK */
    IDeckLinkOutput *p_output;

    /*
     * Synchronizes aout and vout modules:
     * vout module waits until aout has been initialized.
     * That means video-only output is NOT supported.
     */

    vlc_mutex_t lock;
    vlc_cond_t cond;
    uint8_t users;
    bool    b_videomodule;
    bool    b_recycling;

    //int i_channels;
    int i_rate;

    BMDTimeScale timescale;
    BMDTimeValue frameduration;

    /* XXX: workaround card clock drift */
    vlc_tick_t offset;

    /* !With LOCK */

    vlc_timer_t drain_timer;

    /* single video module exclusive */
    struct
    {
        bool tenbits;
        uint8_t afd, ar;
        int nosignal_delay;
        picture_t *pic_nosignal;
    } video;
};

} // namespace

/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/


static int  OpenVideo           (vout_display_t *,
                                 video_format_t *, vlc_video_context *);
static void CloseVideo          (vout_display_t *);
static int  OpenAudio           (vlc_object_t *);
static void CloseAudio          (vlc_object_t *);

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/


vlc_module_begin()
    set_shortname(N_("DecklinkOutput"))
    set_description(N_("Output module to write to Blackmagic SDI card"))
    set_section(N_("DeckLink General Options"), NULL)
    add_integer(CFG_PREFIX "card-index", 0,
                CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT)

    add_submodule ()
    set_description (N_("DeckLink Video Output module"))
    set_subcategory(SUBCAT_VIDEO_VOUT)
    set_callback_display(OpenVideo, 0)
    set_section(N_("DeckLink Video Options"), NULL)
    add_string(VIDEO_CFG_PREFIX "video-connection", "sdi",
                VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT)
                change_string_list(ppsz_videoconns, ppsz_videoconns_text)
    add_string(VIDEO_CFG_PREFIX "mode", "",
                MODE_TEXT, MODE_LONGTEXT)
    add_bool(VIDEO_CFG_PREFIX "tenbits", true,
                VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT)
    add_integer(VIDEO_CFG_PREFIX "nosignal-delay", 5,
                NOSIGNAL_INDEX_TEXT, NOSIGNAL_INDEX_LONGTEXT)
    add_integer(VIDEO_CFG_PREFIX "afd-line", 16,
                AFDLINE_INDEX_TEXT, AFDLINE_INDEX_LONGTEXT)
    add_integer_with_range(VIDEO_CFG_PREFIX "afd", 8, 0, 16,
                AFD_INDEX_TEXT, nullptr)
                change_integer_list(rgi_afd_values, rgsz_afd_text)
    add_integer_with_range(VIDEO_CFG_PREFIX "ar", 1, 0, 1,
                AR_INDEX_TEXT, AR_INDEX_LONGTEXT)
                change_integer_list(rgi_ar_values, rgsz_ar_text)
    add_loadfile(VIDEO_CFG_PREFIX "nosignal-image", NULL,
                 NOSIGNAL_IMAGE_TEXT, nullptr)


    add_submodule ()
    set_description (N_("DeckLink Audio Output module"))
    set_subcategory(SUBCAT_AUDIO_AOUT)
    set_capability("audio output", 0)
    set_callbacks (OpenAudio, CloseAudio)
    set_section(N_("DeckLink Audio Options"), NULL)
    add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
                RATE_TEXT, RATE_LONGTEXT)
    add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
                CHANNELS_TEXT, CHANNELS_LONGTEXT)
vlc_module_end ()

/* Protects decklink_sys_t creation/deletion */
static vlc::threads::mutex sys_lock;
static vlc::threads::condition_variable cond_video;

static decklink_sys_t *HoldDLSys(vlc_object_t *obj, int i_cat)
{
    vlc_object_t *libvlc = VLC_OBJECT(vlc_object_instance(obj));
    decklink_sys_t *sys;

    sys_lock.lock();

    if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
    {
        sys = (decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
        sys->users++;

        if(i_cat == VIDEO_ES)
        {
            while(sys->b_videomodule)
            {
                msg_Info(obj, "Waiting for previous vout module to exit");
                cond_video.wait(sys_lock);
            }
        }
    }
    else
    {
        sys = (decklink_sys_t*)malloc(sizeof(*sys));
        if (sys) {
            sys->p_output = NULL;
            sys->offset = 0;
            sys->users = 1;
            sys->b_videomodule = (i_cat == VIDEO_ES);
            sys->b_recycling = false;
            sys->i_rate = var_InheritInteger(obj, AUDIO_CFG_PREFIX "audio-rate");
            if(sys->i_rate > 0)
                sys->i_rate = -1;
            vlc_mutex_init(&sys->lock);
            vlc_cond_init(&sys->cond);
            var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
            var_SetAddress(libvlc, "decklink-sys", (void*)sys);
        }
    }

    sys_lock.unlock();
    return sys;
}

static void ReleaseDLSys(vlc_object_t *obj, int i_cat)
{
    vlc_object_t *libvlc = VLC_OBJECT(vlc_object_instance(obj));

    sys_lock.lock();

    struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");

    if (--sys->users == 0) {
        msg_Dbg(obj, "Destroying decklink data");

        if (sys->p_output) {
            sys->p_output->StopScheduledPlayback(0, NULL, 0);
            sys->p_output->DisableVideoOutput();
            sys->p_output->DisableAudioOutput();
            sys->p_output->Release();
        }

        /* Clean video specific */
        if (sys->video.pic_nosignal)
            picture_Release(sys->video.pic_nosignal);

        free(sys);
        var_Destroy(libvlc, "decklink-sys");
    }
    else if (i_cat == VIDEO_ES)
    {
        sys->b_videomodule = false;
        sys->b_recycling = true;
        cond_video.signal();
    }

    sys_lock.unlock();
}

static BMDVideoConnection getVConn(vout_display_t *vd, int mask)
{
    BMDVideoConnection conn = bmdVideoConnectionUnspecified;
    char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
    if (psz)
    {
        for(size_t i=0; i<ARRAY_SIZE(rgbmd_videoconns); i++)
        {
            if (!strcmp(psz, ppsz_videoconns[i]) && (mask & rgbmd_videoconns[i]))
            {
                conn = rgbmd_videoconns[i];
                break;
            }
        }
        free(psz);
    }
    else /* Pick one as default connection */
    {
        int iconn = vlc_ctz(mask);
        conn = iconn ? BMDVideoConnection( 1 << iconn ) : bmdVideoConnectionSDI;
    }
    return conn;
}

/*****************************************************************************
 *
 *****************************************************************************/

static int OpenDecklink(vout_display_t *vd, decklink_sys_t *sys, video_format_t *fmt)
{
#define CHECK(message) do { \
    if (result != S_OK) \
    { \
        const char *psz_err = Decklink::Helper::ErrorToString(result); \
        if(psz_err)\
            msg_Err(vd, message ": %s", psz_err); \
        else \
            msg_Err(vd, message ":0x%" PRIHR, result); \
        goto error; \
    } \
} while(0)

    HRESULT result;
    IDeckLinkIterator *decklink_iterator = NULL;
    IDeckLinkDisplayMode *p_display_mode = NULL;
    IDeckLinkConfiguration *p_config = NULL;
    IDeckLinkProfileAttributes *p_attributes = NULL;
    IDeckLink *p_card = NULL;
    void *pv;
    union {
        BMDDisplayMode id;
        char str[4];
    } wanted_mode;
    wanted_mode.id = bmdModeUnknown;

    vlc_mutex_locker locker(&sys->lock);

    /* wait until aout is ready */
    msg_Info(vd, "Waiting for DeckLink audio input module to start");
    while (sys->i_rate == -1)
        vlc_cond_wait(&sys->cond, &sys->lock);

    int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
    char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");

    if(mode)
    {
        if (strnlen(mode, 5) > 4)
        {
            msg_Err(vd, "Invalid mode %s", mode);
            free(mode);
            goto error;
        }
        strncpy(wanted_mode.str, mode, 4);
        free(mode);
    }

    if (i_card_index < 0)
    {
        msg_Err(vd, "Invalid card index %d", i_card_index);
        goto error;
    }

    decklink_iterator = CreateDeckLinkIteratorInstance();
    if (!decklink_iterator)
    {
        msg_Err(vd, "DeckLink drivers not found.");
        goto error;
    }

    for(int i = 0; i <= i_card_index; ++i)
    {
        if (p_card)
            p_card->Release();
        result = decklink_iterator->Next(&p_card);
        CHECK("Card not found");
    }

    decklink_str_t tmp_name;
    char *psz_model_name;
    result = p_card->GetModelName(&tmp_name);
    CHECK("Unknown model name");
    psz_model_name = DECKLINK_STRDUP(tmp_name);
    DECKLINK_FREE(tmp_name);

    msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
    free(psz_model_name);

    /* Read attributes */

    result = p_card->QueryInterface(IID_IDeckLinkProfileAttributes, &pv);
    CHECK("Could not get IDeckLinkAttributes");
    p_attributes = static_cast<IDeckLinkProfileAttributes*>(pv);

#ifdef _WIN32
    LONGLONG iconn;
#else
    int64_t iconn;
#endif
    BMDVideoConnection vconn;
    result = p_attributes->GetInt(BMDDeckLinkVideoOutputConnections, &iconn); /* reads mask */
    CHECK("Could not get BMDDeckLinkVideoOutputConnections");

    result = p_card->QueryInterface(IID_IDeckLinkOutput, &pv);
    CHECK("No outputs");
    sys->p_output = static_cast<IDeckLinkOutput*>(pv);

    result = p_card->QueryInterface(IID_IDeckLinkConfiguration, &pv);
    CHECK("Could not get config interface");
    p_config = static_cast<IDeckLinkConfiguration*>(pv);

    /* Now configure card */

    vconn = getVConn(vd, iconn);
    if (vconn == bmdVideoConnectionUnspecified)
    {
        msg_Err(vd, "Invalid video connection specified");
        goto error;
    }

    result = p_config->SetInt(bmdDeckLinkConfigVideoOutputConnection, vconn);
    CHECK("Could not set video output connection");

    p_display_mode = Decklink::Helper::MatchDisplayMode(VLC_OBJECT(vd), sys->p_output,
                                          vd->source, wanted_mode.id);
    if(p_display_mode == NULL)
    {
        msg_Err(vd, "Could not negotiate a compatible display mode");
        goto error;
    }
    else
    {
        union {
            BMDDisplayMode id;
            char str[4];
        } mode;
        mode.id = p_display_mode->GetDisplayMode();
        msg_Dbg(vd, "Selected mode '%4.4s'", mode.str);

        BMDPixelFormat pixelFormat = sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
        BMDVideoOutputFlags flags = bmdVideoOutputVANC;
        if (mode.id == bmdModeNTSC ||
            mode.id == bmdModeNTSC2398 ||
            mode.id == bmdModePAL)
        {
            flags = bmdVideoOutputVITC;
        }
#ifdef _WIN32
        BOOL supported;
#else
        bool supported;
#endif
#if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
        BMDDisplayModeSupport support = bmdDisplayModeNotSupported;
        result = sys->p_output->DoesSupportVideoMode(mode.id,
                                                pixelFormat,
                                                flags,
                                                &support,
                                                NULL);
        supported = (support != bmdDisplayModeNotSupported);
#else
        result = sys->p_output->DoesSupportVideoMode(vconn,
                                                mode.id,
                                                pixelFormat,
                                                bmdSupportedVideoModeDefault,
                                                NULL,
                                                &supported);
#endif
        CHECK("Does not support video mode");
        if (!supported)
        {
            msg_Err(vd, "Video mode not supported");
            goto error;
        }

        if (p_display_mode->GetWidth() <= 0 || p_display_mode->GetWidth() & 1)
        {
             msg_Err(vd, "Unknown video mode specified.");
             goto error;
        }

        result = p_display_mode->GetFrameRate(&sys->frameduration,
                                              &sys->timescale);
        CHECK("Could not read frame rate");

        result = sys->p_output->EnableVideoOutput(mode.id, flags);
        CHECK("Could not enable video output");

        video_format_Copy(fmt, vd->source);
        fmt->i_width = fmt->i_visible_width = p_display_mode->GetWidth();
        fmt->i_height = fmt->i_visible_height = p_display_mode->GetHeight();
        fmt->i_x_offset = 0;
        fmt->i_y_offset = 0;
        fmt->i_sar_num = 0;
        fmt->i_sar_den = 0;
        fmt->i_chroma = !sys->video.tenbits ? VLC_CODEC_UYVY : VLC_CODEC_I422_10L; /* we will convert to v210 */
        fmt->i_frame_rate = (unsigned) sys->frameduration;
        fmt->i_frame_rate_base = (unsigned) sys->timescale;
    }

    if (/*decklink_sys->i_channels > 0 &&*/ sys->i_rate > 0)
    {
        result = sys->p_output->EnableAudioOutput(
            BMDAudioSampleRate(sys->i_rate),
            bmdAudioSampleType16bitInteger,
            /*decklink_sys->i_channels*/ 2,
            bmdAudioOutputStreamTimestamped);
        CHECK("Could not start audio output");
    }

    /* start */
    result = sys->p_output->StartScheduledPlayback(
        samples_from_vlc_tick(vlc_tick_now(), sys->timescale), sys->timescale, 1.0);
    CHECK("Could not start playback");

    p_config->Release();
    p_display_mode->Release();
    p_card->Release();
    p_attributes->Release();
    decklink_iterator->Release();

    return VLC_SUCCESS;

error:
    if (sys->p_output) {
        sys->p_output->Release();
        sys->p_output = NULL;
    }
    if (p_card)
        p_card->Release();
    if (p_config)
        p_config->Release();
    if (p_attributes)
        p_attributes->Release();
    if (decklink_iterator)
        decklink_iterator->Release();
    if (p_display_mode)
        p_display_mode->Release();
    video_format_Clean(fmt);

    return VLC_EGENERIC;
#undef CHECK
}

/*****************************************************************************
 * Video
 *****************************************************************************/

static void PrepareVideo(vout_display_t *vd, picture_t *picture, const vlc_render_subpicture *,
                         vlc_tick_t date)
{
    decklink_sys_t *sys = (decklink_sys_t *) vd->sys;
    vlc_tick_t now = vlc_tick_now();

    if (!picture)
        return;

    if (now - date > vlc_tick_from_sec( sys->video.nosignal_delay )) {
        msg_Dbg(vd, "no signal");
        if (sys->video.pic_nosignal) {
            picture = sys->video.pic_nosignal;
        } else {
            if (sys->video.tenbits) { // I422_10L
                plane_t *y = &picture->p[0];
                memset(y->p_pixels, 0x0, y->i_lines * y->i_pitch);
                for (int i = 1; i < picture->i_planes; i++) {
                    plane_t *p = &picture->p[i];
                    size_t len = p->i_lines * p->i_pitch / 2;
                    int16_t *data = (int16_t*)p->p_pixels;
                    for (size_t j = 0; j < len; j++) // XXX: SIMD
                        data[j] = 0x200;
                }
            } else { // UYVY
                size_t len = picture->p[0].i_lines * picture->p[0].i_pitch;
                for (size_t i = 0; i < len; i+= 2) { // XXX: SIMD
                    picture->p[0].p_pixels[i+0] = 0x80;
                    picture->p[0].p_pixels[i+1] = 0;
                }
            }
        }
        date = now;
    }

    HRESULT result;
    int w, h, stride, length;
    w = vd->fmt->i_width;
    h = vd->fmt->i_height;

    IDeckLinkMutableVideoFrame *pDLVideoFrame;
    result = sys->p_output->CreateVideoFrame(w, h, w*3,
        sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
        bmdFrameFlagDefault, &pDLVideoFrame);

    if (result != S_OK) {
        msg_Err(vd, "Failed to create video frame:0x%" PRIHR, result);
        pDLVideoFrame = NULL;
        goto end;
    }

    void *frame_bytes;
    pDLVideoFrame->GetBytes((void**)&frame_bytes);
    stride = pDLVideoFrame->GetRowBytes();

    if (sys->video.tenbits) {
        IDeckLinkVideoFrameAncillary *vanc;
        int line;
        void *buf;

        result = sys->p_output->CreateAncillaryData(
                sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV, &vanc);
        if (result != S_OK) {
            msg_Err(vd, "Failed to create vanc:0x%" PRIHR, result);
            goto end;
        }

        line = var_InheritInteger(vd, VIDEO_CFG_PREFIX "afd-line");
        result = vanc->GetBufferForVerticalBlankingLine(line, &buf);
        if (result != S_OK) {
            msg_Err(vd, "Failed to get VBI line %d:0x%" PRIHR, line, result);
            goto end;
        }

        sdi::AFD afd(sys->video.afd, sys->video.ar);
        afd.FillBuffer(reinterpret_cast<uint8_t*>(buf), stride);

        sdi::V210::Convert(picture, stride, frame_bytes);

        result = pDLVideoFrame->SetAncillaryData(vanc);
        vanc->Release();
        if (result != S_OK) {
            msg_Err(vd, "Failed to set vanc:0x%" PRIHR, result);
            goto end;
        }
    }
    else for(int y = 0; y < h; ++y) {
        uint8_t *dst = (uint8_t *)frame_bytes + stride * y;
        const uint8_t *src = (const uint8_t *)picture->p[0].p_pixels +
            picture->p[0].i_pitch * y;
        memcpy(dst, src, w * 2 /* bpp */);
    }


    // compute frame duration in CLOCK_FREQ units
    length = (sys->frameduration * CLOCK_FREQ) / sys->timescale;

    date -= sys->offset;
    result = sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
        date, length, CLOCK_FREQ);

    if (result != S_OK) {
        msg_Err(vd, "Dropped Video frame %" PRId64 ":0x%" PRIHR, date, result);
        goto end;
    }

    now = vlc_tick_now() - sys->offset;

    BMDTimeValue decklink_now;
    double speed;
    sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);

    if ((now - decklink_now) > 400000) {
        /* XXX: workaround card clock drift */
        sys->offset += 50000;
        msg_Err(vd, "Delaying: offset now %" PRId64, sys->offset);
    }

end:
    if (pDLVideoFrame)
        pDLVideoFrame->Release();
}

static const auto ops = []{
    struct vlc_display_operations ops {};
    ops.close = CloseVideo;
    ops.prepare = PrepareVideo;
    return ops;
}();

static int OpenVideo(vout_display_t *vd,
                     video_format_t *fmtp, vlc_video_context *context)
{
    VLC_UNUSED(context);
    decklink_sys_t *sys = HoldDLSys(VLC_OBJECT(vd), VIDEO_ES);
    if(!sys)
        return VLC_ENOMEM;

    bool b_init;
    vlc_mutex_lock(&sys->lock);
    b_init = !sys->b_recycling;
    vlc_mutex_unlock(&sys->lock);

    if( b_init )
    {
        sys->video.tenbits = var_InheritBool(vd, VIDEO_CFG_PREFIX "tenbits");
        sys->video.nosignal_delay = var_InheritInteger(vd, VIDEO_CFG_PREFIX "nosignal-delay");
        sys->video.afd = var_InheritInteger(vd, VIDEO_CFG_PREFIX "afd");
        sys->video.ar = var_InheritInteger(vd, VIDEO_CFG_PREFIX "ar");
        sys->video.pic_nosignal = NULL;

        if (OpenDecklink(vd, sys, fmtp) != VLC_SUCCESS)
        {
            CloseVideo(vd);
            return VLC_EGENERIC;
        }

        char *pic_file = var_InheritString(vd, VIDEO_CFG_PREFIX "nosignal-image");
        if (pic_file)
        {
            sys->video.pic_nosignal = sdi::Generator::Picture(VLC_OBJECT(vd), pic_file, fmtp);
            if (!sys->video.pic_nosignal)
                msg_Err(vd, "Could not create no signal picture");
            free(pic_file);
        }
    }

    vd->ops = &ops;

    vd->sys = (void *) sys;

    return VLC_SUCCESS;
}

static void CloseVideo(vout_display_t *vd)
{
    ReleaseDLSys(VLC_OBJECT(vd), VIDEO_ES);
}

/*****************************************************************************
 * Audio
 *****************************************************************************/


static void DrainReset(audio_output_t *aout)
{
    decklink_sys_t *sys = (decklink_sys_t *) aout->sys;

    if (sys->drain_timer != NULL)
    {
        vlc_timer_destroy(sys->drain_timer);
        sys->drain_timer = NULL;
    }
}

static void Flush(audio_output_t *aout)
{
    decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
    vlc_mutex_lock(&sys->lock);
    IDeckLinkOutput *p_output = sys->p_output;
    vlc_mutex_unlock(&sys->lock);
    if (!p_output)
        return;

    DrainReset(aout);

    if (sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
        msg_Err(aout, "Flush failed");
}

static void DrainTimerCb(void *data)
{
    audio_output_t *aout = (audio_output_t *) data;

    aout_DrainedReport(aout);
}

static void Drain(audio_output_t *aout)
{
    decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
    vlc_mutex_lock(&sys->lock);
    IDeckLinkOutput *p_output = sys->p_output;
    vlc_mutex_unlock(&sys->lock);
    if (!p_output)
        return;

    assert(sys->drain_timer == NULL);

    uint32_t samples;
    sys->p_output->GetBufferedAudioSampleFrameCount(&samples);

    if (samples == 0)
    {
        aout_DrainedReport(aout);
        return;
    }

    /* Create and arm a timer to notify when drained */
    int ret = vlc_timer_create(&sys->drain_timer, DrainTimerCb, aout);
    if (ret != 0)
    {
        aout_DrainedReport(aout);
        return;
    }

    vlc_timer_schedule(sys->drain_timer, false,
                       vlc_tick_from_samples(samples, sys->i_rate),
                       VLC_TIMER_FIRE_ONCE);
}


static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
{
    decklink_sys_t *sys = (decklink_sys_t *) aout->sys;

    if (sys->i_rate == 0)
        return VLC_EGENERIC;

    fmt->i_format = VLC_CODEC_S16N;
    fmt->i_channels = 2; //decklink_sys->i_channels;
    fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
    fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
    fmt->i_rate = sys->i_rate;
    fmt->i_bitspersample = 16;
    fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
    fmt->i_frame_length  = FRAME_SIZE;

    return VLC_SUCCESS;
}

static void PlayAudio(audio_output_t *aout, block_t *audio, vlc_tick_t systempts)
{
    decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
    vlc_mutex_lock(&sys->lock);
    IDeckLinkOutput *p_output = sys->p_output;
    audio->i_pts -= sys->offset;
    vlc_mutex_unlock(&sys->lock);
    if (!p_output) {
        block_Release(audio);
        return;
    }

    uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
    uint32_t written;
    HRESULT result = p_output->ScheduleAudioSamples(
            audio->p_buffer, sampleFrameCount, systempts, CLOCK_FREQ, &written);

    if (result != S_OK)
        msg_Err(aout, "Failed to schedule audio sample:0x%" PRIHR, result);
    else if (sampleFrameCount != written)
        msg_Err(aout, "Written only %d samples out of %d", written, sampleFrameCount);

    block_Release(audio);
}

static int OpenAudio(vlc_object_t *p_this)
{
    audio_output_t *aout = (audio_output_t *)p_this;
    decklink_sys_t *sys = HoldDLSys(p_this, AUDIO_ES);
    if(!sys)
        return VLC_ENOMEM;

    aout->sys = sys;

    vlc_mutex_lock(&sys->lock);
    //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
    sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
    vlc_cond_signal(&sys->cond);
    vlc_mutex_unlock(&sys->lock);
    sys->drain_timer = NULL;

    aout->play      = PlayAudio;
    aout->start     = Start;
    aout->flush     = Flush;
    aout->drain     = Drain;

    aout->pause     = NULL;
    aout->stop      = Flush;
    aout->mute_set  = NULL;
    aout->volume_set= NULL;

    return VLC_SUCCESS;
}

static void CloseAudio(vlc_object_t *p_this)
{
    decklink_sys_t *sys = (decklink_sys_t *) ((audio_output_t *)p_this)->sys;
    vlc_mutex_lock(&sys->lock);
    vlc_mutex_unlock(&sys->lock);
    ReleaseDLSys(p_this, AUDIO_ES);
}

Messung
CHZG
81979892

¤ Dauer der Verarbeitung: 0.6 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.