Skip to content
Snippets Groups Projects
decklink_dec.cpp 43.6 KiB
Newer Older
 * Blackmagic DeckLink input
 * Copyright (c) 2013-2014 Luca Barbato, Deti Fliegl
 * Copyright (c) 2014 Rafaël Carré
 * Copyright (c) 2017 Akamai Technologies, Inc.
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg 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.
 *
 * FFmpeg 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 FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <atomic>
using std::atomic;

/* Include internal.h first to avoid conflict between winsock.h (used by
 * DeckLink headers) and winsock2.h (used by libavformat) in MSVC++ builds */
extern "C" {
#include "libavformat/internal.h"
}

#include <DeckLinkAPI.h>

extern "C" {
#include "config.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
#include "libavutil/reverse.h"
#if CONFIG_LIBZVBI
#include <libzvbi.h>
#endif
}

#include "decklink_common.h"
#include "decklink_dec.h"

const BMDDisplayMode AUTODETECT_DEFAULT_MODE = bmdModeNTSC;
typedef struct VANCLineNumber {
    BMDDisplayMode mode;
    int vanc_start;
    int field0_vanc_end;
    int field1_vanc_start;
    int vanc_end;
} VANCLineNumber;

/* These VANC line numbers need not be very accurate. In any case
 * GetBufferForVerticalBlankingLine() will return an error when invalid
 * ancillary line number was requested. We just need to make sure that the
 * entire VANC region is covered, while making sure we don't decode VANC of
 * another source during switching*/
static VANCLineNumber vanc_line_numbers[] = {
    /* SD Modes */

    {bmdModeNTSC, 11, 19, 274, 282},
    {bmdModeNTSC2398, 11, 19, 274, 282},
    {bmdModePAL, 7, 22, 320, 335},
    {bmdModeNTSCp, 11, -1, -1, 39},
    {bmdModePALp, 7, -1, -1, 45},

    /* HD 1080 Modes */

    {bmdModeHD1080p2398, 8, -1, -1, 42},
    {bmdModeHD1080p24, 8, -1, -1, 42},
    {bmdModeHD1080p25, 8, -1, -1, 42},
    {bmdModeHD1080p2997, 8, -1, -1, 42},
    {bmdModeHD1080p30, 8, -1, -1, 42},
    {bmdModeHD1080i50, 8, 20, 570, 585},
    {bmdModeHD1080i5994, 8, 20, 570, 585},
    {bmdModeHD1080i6000, 8, 20, 570, 585},
    {bmdModeHD1080p50, 8, -1, -1, 42},
    {bmdModeHD1080p5994, 8, -1, -1, 42},
    {bmdModeHD1080p6000, 8, -1, -1, 42},

     /* HD 720 Modes */

    {bmdModeHD720p50, 8, -1, -1, 26},
    {bmdModeHD720p5994, 8, -1, -1, 26},
    {bmdModeHD720p60, 8, -1, -1, 26},

    /* For all other modes, for which we don't support VANC */
    {bmdModeUnknown, 0, -1, -1, -1}
};

class decklink_allocator : public IDeckLinkMemoryAllocator
{
public:
        decklink_allocator(): _refs(1) { }
        virtual ~decklink_allocator() { }

        // IDeckLinkMemoryAllocator methods
        virtual HRESULT STDMETHODCALLTYPE AllocateBuffer(unsigned int bufferSize, void* *allocatedBuffer)
        {
            void *buf = av_malloc(bufferSize + AV_INPUT_BUFFER_PADDING_SIZE);
            if (!buf)
                return E_OUTOFMEMORY;
            *allocatedBuffer = buf;
            return S_OK;
        }
        virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(void* buffer)
        {
            av_free(buffer);
            return S_OK;
        }
        virtual HRESULT STDMETHODCALLTYPE Commit() { return S_OK; }
        virtual HRESULT STDMETHODCALLTYPE Decommit() { return S_OK; }

        // IUnknown methods
        virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
        virtual ULONG   STDMETHODCALLTYPE AddRef(void) { return ++_refs; }
        virtual ULONG   STDMETHODCALLTYPE Release(void)
        {
            int ret = --_refs;
            if (!ret)
                delete this;
            return ret;
        }

private:
        std::atomic<int>  _refs;
};

extern "C" {
static void decklink_object_free(void *opaque, uint8_t *data)
{
    IUnknown *obj = (class IUnknown *)opaque;
    obj->Release();
}
}

static int get_vanc_line_idx(BMDDisplayMode mode)
{
    unsigned int i;
    for (i = 0; i < FF_ARRAY_ELEMS(vanc_line_numbers); i++) {
        if (mode == vanc_line_numbers[i].mode)
            return i;
    }
    /* Return the VANC idx for Unknown mode */
    return i - 1;
}

static inline void clear_parity_bits(uint16_t *buf, int len) {
    int i;
    for (i = 0; i < len; i++)
        buf[i] &= 0xff;
}

static int check_vanc_parity_checksum(uint16_t *buf, int len, uint16_t checksum) {
    int i;
    uint16_t vanc_sum = 0;
    for (i = 3; i < len - 1; i++) {
        uint16_t v = buf[i];
        int np = v >> 8;
        if ((!!p ^ !!(v & 0x100)) || (np != 1 && np != 2)) {
            // Parity check failed
            return -1;
        }
        vanc_sum += v;
    }
    vanc_sum &= 0x1ff;
    vanc_sum |= ((~vanc_sum & 0x100) << 1);
    if (checksum != vanc_sum) {
        // Checksum verification failed
        return -1;
    }
    return 0;
}

/* The 10-bit VANC data is packed in V210, we only need the luma component. */
static void extract_luma_from_v210(uint16_t *dst, const uint8_t *src, int width)
{
    int i;
    for (i = 0; i < width / 3; i++) {
        *dst++ = (src[1] >> 2) + ((src[2] & 15) << 6);
        *dst++ =  src[4]       + ((src[5] &  3) << 8);
        *dst++ = (src[6] >> 4) + ((src[7] & 63) << 4);
        src += 8;
    }
}

static void unpack_v210(uint16_t *dst, const uint8_t *src, int width)
{
    int i;
    for (i = 0; i < width * 2 / 3; i++) {
        *dst++ =  src[0]       + ((src[1] & 3)  << 8);
        *dst++ = (src[1] >> 2) + ((src[2] & 15) << 6);
        *dst++ = (src[2] >> 4) + ((src[3] & 63) << 4);
        src += 4;
    }
}

static uint8_t calc_parity_and_line_offset(int line)
{
    uint8_t ret = (line < 313) << 5;
    if (line >= 7 && line <= 22)
        ret += line;
    if (line >= 320 && line <= 335)
        ret += (line - 313);
    return ret;
}

static void fill_data_unit_head(int line, uint8_t *tgt)
{
    tgt[0] = 0x02; // data_unit_id
    tgt[1] = 0x2c; // data_unit_length
    tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset
    tgt[3] = 0xe4; // framing code
}

static uint8_t* teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt, vbi_pixfmt fmt)
{
    vbi_bit_slicer slicer;

    vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, fmt);

    if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE)
    return tgt + 46;
}

static uint8_t* teletext_data_unit_from_vbi_data_10bit(int line, uint8_t *src, uint8_t *tgt)
{
    uint8_t y[720];
    uint8_t *py = y;
    uint8_t *pend = y + 720;
    /* The 10-bit VBI data is packed in V210, but libzvbi only supports 8-bit,
     * so we extract the 8 MSBs of the luma component, that is enough for
     * teletext bit slicing. */
    while (py < pend) {
        *py++ = (src[1] >> 4) + ((src[2] & 15) << 4);
        *py++ = (src[4] >> 2) + ((src[5] & 3 ) << 6);
        *py++ = (src[6] >> 6) + ((src[7] & 63) << 2);
        src += 8;
    }
    return teletext_data_unit_from_vbi_data(line, y, tgt, VBI_PIXFMT_YUV420);
static uint8_t* teletext_data_unit_from_op47_vbi_packet(int line, uint16_t *py, uint8_t *tgt)
{
    int i;

    if (py[0] != 0x255 || py[1] != 0x255 || py[2] != 0x227)
        return tgt;

    fill_data_unit_head(line, tgt);

    py += 3;
    tgt += 4;

    for (i = 0; i < 42; i++)
       *tgt++ = ff_reverse[py[i] & 255];

    return tgt;
}

static int linemask_matches(int line, int64_t mask)
{
    int shift = -1;
    if (line >= 6 && line <= 22)
        shift = line - 6;
    if (line >= 318 && line <= 335)
        shift = line - 318 + 17;
    return shift >= 0 && ((1ULL << shift) & mask);
}

static uint8_t* teletext_data_unit_from_op47_data(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines)
{
    if (py < pend - 9) {
        if (py[0] == 0x151 && py[1] == 0x115 && py[3] == 0x102) {       // identifier, identifier, format code for WST teletext
            uint16_t *descriptors = py + 4;
            int i;
            py += 9;
            for (i = 0; i < 5 && py < pend - 45; i++, py += 45) {
                int line = (descriptors[i] & 31) + (!(descriptors[i] & 128)) * 313;
                if (line && linemask_matches(line, wanted_lines))
                    tgt = teletext_data_unit_from_op47_vbi_packet(line, py, tgt);
            }
        }
    }
    return tgt;
}

static uint8_t* teletext_data_unit_from_ancillary_packet(uint16_t *py, uint16_t *pend, uint8_t *tgt, int64_t wanted_lines, int allow_multipacket)
{
    uint16_t did = py[0];                                               // data id
    uint16_t sdid = py[1];                                              // secondary data id
    uint16_t dc = py[2] & 255;                                          // data count
    py += 3;
    pend = FFMIN(pend, py + dc);
    if (did == 0x143 && sdid == 0x102) {                                // subtitle distribution packet
        tgt = teletext_data_unit_from_op47_data(py, pend, tgt, wanted_lines);
    } else if (allow_multipacket && did == 0x143 && sdid == 0x203) {    // VANC multipacket
        py += 2;                                                        // priority, line/field
        while (py < pend - 3) {
            tgt = teletext_data_unit_from_ancillary_packet(py, pend, tgt, wanted_lines, 0);
            py += 4 + (py[2] & 255);                                    // ndid, nsdid, ndc, line/field
        }
    }
    return tgt;
}

static uint8_t *vanc_to_cc(AVFormatContext *avctx, uint16_t *buf, size_t words,
                           unsigned &cc_count)
    size_t i, len = (buf[5] & 0xff) + 6 + 1;
    uint8_t cdp_sum, rate;
    uint16_t hdr, ftr;
    uint8_t *cc;
    uint16_t *cdp = &buf[6]; // CDP follows
    if (cdp[0] != 0x96 || cdp[1] != 0x69) {
        av_log(avctx, AV_LOG_WARNING, "Invalid CDP header 0x%.2x 0x%.2x\n", cdp[0], cdp[1]);
        return NULL;
    }
Loading
Loading full blame...