mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-01-13 21:28:01 +02:00
9e86a61870
Purpose: avdevice/decklink: Removed pthread dependency by replacing semaphore used in code appropriately. Doing so makes it easier to build ffmpeg using Visual C++ on Windows. This is a contination of Kyle Schwarz's "avdevice/decklink: Remove pthread dependency" patch that is available at https://patchwork.ffmpeg.org/patch/2654/ . This patch wasn't accepted, and as far as I can tell, there was no follow-up after it was rejected. Notes: Used Visual Studio 2015 (with update 3) for this. Comments: -- configure: Eliminated pthreads dependency for decklink_indev_deps and decklink_outdev_deps and replaced with threads dependency -- libavdevice/decklink_common.cpp / .h: a) Eliminated semaphore and replaced with a combination of a mutex, condition variable, and a counter (frames_buffer_available_spots). b) Removed include of pthread.h and semaphore.h and now using libavutil/thread.h instead. -- libavdevice/decklink_dec.cpp: Eliminated include of pthread.h and semaphore.h. -- libavdevice/decklink_enc.cpp: a) Eliminated include of pthread.h and semaphore.h. b) Replaced use of semaphore with the equivalent using a combination of a mutex, condition variable, and a counter (frames_buffer_available_spots). In theory, libavutil/thread.h and the associated code could have been modified instead to add cross-platform implementations of the sem_ functions, but an inspection of the ffmpeg source base indicates that there are only two cases in which semaphores are used (including this one that was replaced), so it was deemed to not be worth the effort. Signed-off-by: Marton Balint <cus@passwd.hu>
387 lines
14 KiB
C++
387 lines
14 KiB
C++
/*
|
|
* Blackmagic DeckLink output
|
|
* Copyright (c) 2013-2014 Ramiro Polla, Luca Barbato, Deti Fliegl
|
|
*
|
|
* 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 <DeckLinkAPI.h>
|
|
#ifdef _WIN32
|
|
#include <DeckLinkAPI_i.c>
|
|
#else
|
|
#include <DeckLinkAPIDispatch.cpp>
|
|
#endif
|
|
|
|
extern "C" {
|
|
#include "libavformat/avformat.h"
|
|
#include "libavformat/internal.h"
|
|
#include "libavutil/imgutils.h"
|
|
#include "libavutil/intreadwrite.h"
|
|
#include "libavutil/bswap.h"
|
|
}
|
|
|
|
#include "decklink_common.h"
|
|
|
|
#ifdef _WIN32
|
|
IDeckLinkIterator *CreateDeckLinkIteratorInstance(void)
|
|
{
|
|
IDeckLinkIterator *iter;
|
|
|
|
if (CoInitialize(NULL) < 0) {
|
|
av_log(NULL, AV_LOG_ERROR, "COM initialization failed.\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (CoCreateInstance(CLSID_CDeckLinkIterator, NULL, CLSCTX_ALL,
|
|
IID_IDeckLinkIterator, (void**) &iter) != S_OK) {
|
|
av_log(NULL, AV_LOG_ERROR, "DeckLink drivers not installed.\n");
|
|
return NULL;
|
|
}
|
|
|
|
return iter;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
static char *dup_wchar_to_utf8(wchar_t *w)
|
|
{
|
|
char *s = NULL;
|
|
int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
|
|
s = (char *) av_malloc(l);
|
|
if (s)
|
|
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
|
|
return s;
|
|
}
|
|
#define DECKLINK_STR OLECHAR *
|
|
#define DECKLINK_STRDUP dup_wchar_to_utf8
|
|
#define DECKLINK_FREE(s) SysFreeString(s)
|
|
#define DECKLINK_BOOL BOOL
|
|
#elif defined(__APPLE__)
|
|
static char *dup_cfstring_to_utf8(CFStringRef w)
|
|
{
|
|
char s[256];
|
|
CFStringGetCString(w, s, 255, kCFStringEncodingUTF8);
|
|
return av_strdup(s);
|
|
}
|
|
#define DECKLINK_STR const __CFString *
|
|
#define DECKLINK_STRDUP dup_cfstring_to_utf8
|
|
#define DECKLINK_FREE(s) free((void *) s)
|
|
#define DECKLINK_BOOL bool
|
|
#else
|
|
#define DECKLINK_STR const char *
|
|
#define DECKLINK_STRDUP av_strdup
|
|
/* free() is needed for a string returned by the DeckLink SDL. */
|
|
#define DECKLINK_FREE(s) free((void *) s)
|
|
#define DECKLINK_BOOL bool
|
|
#endif
|
|
|
|
HRESULT ff_decklink_get_display_name(IDeckLink *This, const char **displayName)
|
|
{
|
|
DECKLINK_STR tmpDisplayName;
|
|
HRESULT hr = This->GetDisplayName(&tmpDisplayName);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
*displayName = DECKLINK_STRDUP(tmpDisplayName);
|
|
DECKLINK_FREE(tmpDisplayName);
|
|
return hr;
|
|
}
|
|
|
|
static int decklink_select_input(AVFormatContext *avctx, BMDDeckLinkConfigurationID cfg_id)
|
|
{
|
|
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
|
|
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
|
|
BMDDeckLinkAttributeID attr_id = (cfg_id == bmdDeckLinkConfigAudioInputConnection) ? BMDDeckLinkAudioInputConnections : BMDDeckLinkVideoInputConnections;
|
|
int64_t bmd_input = (cfg_id == bmdDeckLinkConfigAudioInputConnection) ? (int64_t)ctx->audio_input : (int64_t)ctx->video_input;
|
|
const char *type_name = (cfg_id == bmdDeckLinkConfigAudioInputConnection) ? "audio" : "video";
|
|
int64_t supported_connections = 0;
|
|
HRESULT res;
|
|
|
|
if (bmd_input) {
|
|
res = ctx->attr->GetInt(attr_id, &supported_connections);
|
|
if (res != S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to query supported %s inputs.\n", type_name);
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
if ((supported_connections & bmd_input) != bmd_input) {
|
|
av_log(avctx, AV_LOG_ERROR, "Device does not support selected %s input.\n", type_name);
|
|
return AVERROR(ENOSYS);
|
|
}
|
|
res = ctx->cfg->SetInt(cfg_id, bmd_input);
|
|
if (res != S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Failed to select %s input.\n", type_name);
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static DECKLINK_BOOL field_order_eq(enum AVFieldOrder field_order, BMDFieldDominance bmd_field_order)
|
|
{
|
|
if (field_order == AV_FIELD_UNKNOWN)
|
|
return true;
|
|
if ((field_order == AV_FIELD_TT || field_order == AV_FIELD_TB) && bmd_field_order == bmdUpperFieldFirst)
|
|
return true;
|
|
if ((field_order == AV_FIELD_BB || field_order == AV_FIELD_BT) && bmd_field_order == bmdLowerFieldFirst)
|
|
return true;
|
|
if (field_order == AV_FIELD_PROGRESSIVE && (bmd_field_order == bmdProgressiveFrame || bmd_field_order == bmdProgressiveSegmentedFrame))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
int ff_decklink_set_format(AVFormatContext *avctx,
|
|
int width, int height,
|
|
int tb_num, int tb_den,
|
|
enum AVFieldOrder field_order,
|
|
decklink_direction_t direction, int num)
|
|
{
|
|
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
|
|
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
|
|
BMDDisplayModeSupport support;
|
|
IDeckLinkDisplayModeIterator *itermode;
|
|
IDeckLinkDisplayMode *mode;
|
|
int i = 1;
|
|
HRESULT res;
|
|
|
|
av_log(avctx, AV_LOG_DEBUG, "Trying to find mode for frame size %dx%d, frame timing %d/%d, field order %d, direction %d, mode number %d, format code %s\n",
|
|
width, height, tb_num, tb_den, field_order, direction, num, (cctx->format_code) ? cctx->format_code : "(unset)");
|
|
|
|
if (ctx->duplex_mode) {
|
|
DECKLINK_BOOL duplex_supported = false;
|
|
|
|
if (ctx->attr->GetFlag(BMDDeckLinkSupportsDuplexModeConfiguration, &duplex_supported) != S_OK)
|
|
duplex_supported = false;
|
|
|
|
if (duplex_supported) {
|
|
res = ctx->cfg->SetInt(bmdDeckLinkConfigDuplexMode, ctx->duplex_mode == 2 ? bmdDuplexModeFull : bmdDuplexModeHalf);
|
|
if (res != S_OK)
|
|
av_log(avctx, AV_LOG_WARNING, "Setting duplex mode failed.\n");
|
|
else
|
|
av_log(avctx, AV_LOG_VERBOSE, "Successfully set duplex mode to %s duplex.\n", ctx->duplex_mode == 2 ? "full" : "half");
|
|
} else {
|
|
av_log(avctx, AV_LOG_WARNING, "Unable to set duplex mode, because it is not supported.\n");
|
|
}
|
|
}
|
|
|
|
if (direction == DIRECTION_IN) {
|
|
int ret;
|
|
ret = decklink_select_input(avctx, bmdDeckLinkConfigAudioInputConnection);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = decklink_select_input(avctx, bmdDeckLinkConfigVideoInputConnection);
|
|
if (ret < 0)
|
|
return ret;
|
|
res = ctx->dli->GetDisplayModeIterator (&itermode);
|
|
} else {
|
|
res = ctx->dlo->GetDisplayModeIterator (&itermode);
|
|
}
|
|
|
|
if (res!= S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
|
|
return AVERROR(EIO);
|
|
}
|
|
|
|
char format_buf[] = " ";
|
|
if (cctx->format_code)
|
|
memcpy(format_buf, cctx->format_code, FFMIN(strlen(cctx->format_code), sizeof(format_buf)));
|
|
BMDDisplayMode target_mode = (BMDDisplayMode)AV_RB32(format_buf);
|
|
AVRational target_tb = av_make_q(tb_num, tb_den);
|
|
ctx->bmd_mode = bmdModeUnknown;
|
|
while ((ctx->bmd_mode == bmdModeUnknown) && itermode->Next(&mode) == S_OK) {
|
|
BMDTimeValue bmd_tb_num, bmd_tb_den;
|
|
int bmd_width = mode->GetWidth();
|
|
int bmd_height = mode->GetHeight();
|
|
BMDDisplayMode bmd_mode = mode->GetDisplayMode();
|
|
BMDFieldDominance bmd_field_dominance = mode->GetFieldDominance();
|
|
|
|
mode->GetFrameRate(&bmd_tb_num, &bmd_tb_den);
|
|
AVRational mode_tb = av_make_q(bmd_tb_num, bmd_tb_den);
|
|
|
|
if ((bmd_width == width &&
|
|
bmd_height == height &&
|
|
!av_cmp_q(mode_tb, target_tb) &&
|
|
field_order_eq(field_order, bmd_field_dominance))
|
|
|| i == num
|
|
|| target_mode == bmd_mode) {
|
|
ctx->bmd_mode = bmd_mode;
|
|
ctx->bmd_width = bmd_width;
|
|
ctx->bmd_height = bmd_height;
|
|
ctx->bmd_tb_den = bmd_tb_den;
|
|
ctx->bmd_tb_num = bmd_tb_num;
|
|
ctx->bmd_field_dominance = bmd_field_dominance;
|
|
av_log(avctx, AV_LOG_INFO, "Found Decklink mode %d x %d with rate %.2f%s\n",
|
|
bmd_width, bmd_height, 1/av_q2d(mode_tb),
|
|
(ctx->bmd_field_dominance==bmdLowerFieldFirst || ctx->bmd_field_dominance==bmdUpperFieldFirst)?"(i)":"");
|
|
}
|
|
|
|
mode->Release();
|
|
i++;
|
|
}
|
|
|
|
itermode->Release();
|
|
|
|
if (ctx->bmd_mode == bmdModeUnknown)
|
|
return -1;
|
|
if (direction == DIRECTION_IN) {
|
|
if (ctx->dli->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
|
|
bmdVideoOutputFlagDefault,
|
|
&support, NULL) != S_OK)
|
|
return -1;
|
|
} else {
|
|
if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV,
|
|
bmdVideoOutputFlagDefault,
|
|
&support, NULL) != S_OK)
|
|
return -1;
|
|
}
|
|
if (support == bmdDisplayModeSupported)
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t direction, int num) {
|
|
return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction, num);
|
|
}
|
|
|
|
int ff_decklink_list_devices(AVFormatContext *avctx)
|
|
{
|
|
IDeckLink *dl = NULL;
|
|
IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance();
|
|
if (!iter) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
|
|
return AVERROR(EIO);
|
|
}
|
|
av_log(avctx, AV_LOG_INFO, "Blackmagic DeckLink devices:\n");
|
|
while (iter->Next(&dl) == S_OK) {
|
|
const char *displayName;
|
|
ff_decklink_get_display_name(dl, &displayName);
|
|
av_log(avctx, AV_LOG_INFO, "\t'%s'\n", displayName);
|
|
av_free((void *) displayName);
|
|
dl->Release();
|
|
}
|
|
iter->Release();
|
|
return 0;
|
|
}
|
|
|
|
int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction)
|
|
{
|
|
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
|
|
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
|
|
IDeckLinkDisplayModeIterator *itermode;
|
|
IDeckLinkDisplayMode *mode;
|
|
uint32_t format_code;
|
|
HRESULT res;
|
|
|
|
if (direction == DIRECTION_IN) {
|
|
int ret;
|
|
ret = decklink_select_input(avctx, bmdDeckLinkConfigAudioInputConnection);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = decklink_select_input(avctx, bmdDeckLinkConfigVideoInputConnection);
|
|
if (ret < 0)
|
|
return ret;
|
|
res = ctx->dli->GetDisplayModeIterator (&itermode);
|
|
} else {
|
|
res = ctx->dlo->GetDisplayModeIterator (&itermode);
|
|
}
|
|
|
|
if (res!= S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n");
|
|
return AVERROR(EIO);
|
|
}
|
|
|
|
av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n\tformat_code\tdescription",
|
|
avctx->filename);
|
|
while (itermode->Next(&mode) == S_OK) {
|
|
BMDTimeValue tb_num, tb_den;
|
|
mode->GetFrameRate(&tb_num, &tb_den);
|
|
format_code = av_bswap32(mode->GetDisplayMode());
|
|
av_log(avctx, AV_LOG_INFO, "\n\t%.4s\t\t%ldx%ld at %d/%d fps",
|
|
(char*) &format_code, mode->GetWidth(), mode->GetHeight(),
|
|
(int) tb_den, (int) tb_num);
|
|
switch (mode->GetFieldDominance()) {
|
|
case bmdLowerFieldFirst:
|
|
av_log(avctx, AV_LOG_INFO, " (interlaced, lower field first)"); break;
|
|
case bmdUpperFieldFirst:
|
|
av_log(avctx, AV_LOG_INFO, " (interlaced, upper field first)"); break;
|
|
}
|
|
mode->Release();
|
|
}
|
|
av_log(avctx, AV_LOG_INFO, "\n");
|
|
|
|
itermode->Release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ff_decklink_cleanup(AVFormatContext *avctx)
|
|
{
|
|
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
|
|
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
|
|
|
|
if (ctx->dli)
|
|
ctx->dli->Release();
|
|
if (ctx->dlo)
|
|
ctx->dlo->Release();
|
|
if (ctx->attr)
|
|
ctx->attr->Release();
|
|
if (ctx->cfg)
|
|
ctx->cfg->Release();
|
|
if (ctx->dl)
|
|
ctx->dl->Release();
|
|
}
|
|
|
|
int ff_decklink_init_device(AVFormatContext *avctx, const char* name)
|
|
{
|
|
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
|
|
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
|
|
IDeckLink *dl = NULL;
|
|
IDeckLinkIterator *iter = CreateDeckLinkIteratorInstance();
|
|
if (!iter) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n");
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
|
|
while (iter->Next(&dl) == S_OK) {
|
|
const char *displayName;
|
|
ff_decklink_get_display_name(dl, &displayName);
|
|
if (!strcmp(name, displayName)) {
|
|
av_free((void *)displayName);
|
|
ctx->dl = dl;
|
|
break;
|
|
}
|
|
av_free((void *)displayName);
|
|
dl->Release();
|
|
}
|
|
iter->Release();
|
|
if (!ctx->dl)
|
|
return AVERROR(ENXIO);
|
|
|
|
if (ctx->dl->QueryInterface(IID_IDeckLinkConfiguration, (void **)&ctx->cfg) != S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not get configuration interface for '%s'\n", name);
|
|
ff_decklink_cleanup(avctx);
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
|
|
if (ctx->dl->QueryInterface(IID_IDeckLinkAttributes, (void **)&ctx->attr) != S_OK) {
|
|
av_log(avctx, AV_LOG_ERROR, "Could not get attributes interface for '%s'\n", name);
|
|
ff_decklink_cleanup(avctx);
|
|
return AVERROR_EXTERNAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|