* 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 "libavutil/avutil.h"
#include "libavutil/common.h"
#include "libavutil/imgutils.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
#include "libavutil/reverse.h"
#include "decklink_dec.h"
#define MAX_WIDTH_VANC 1920
+const BMDDisplayMode AUTODETECT_DEFAULT_MODE = bmdModeNTSC;
typedef struct VANCLineNumber {
BMDDisplayMode mode;
{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;
}
}
+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;
static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt)
{
AVPacketList *pkt1;
- int ret;
// Drop Packet if queue size is > maximum queue size
if (avpacket_queue_size(q) > (uint64_t)q->max_q_size) {
+ av_packet_unref(pkt);
av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n");
return -1;
}
-
- pkt1 = (AVPacketList *)av_mallocz(sizeof(AVPacketList));
- if (!pkt1) {
+ /* ensure the packet is reference counted */
+ if (av_packet_make_refcounted(pkt) < 0) {
+ av_packet_unref(pkt);
return -1;
}
- ret = av_packet_ref(&pkt1->pkt, pkt);
- av_packet_unref(pkt);
- if (ret < 0) {
- av_free(pkt1);
+
+ pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList));
+ if (!pkt1) {
+ av_packet_unref(pkt);
return -1;
}
+ av_packet_move_ref(&pkt1->pkt, pkt);
pkt1->next = NULL;
pthread_mutex_lock(&q->mutex);
virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
private:
- ULONG m_refCount;
- pthread_mutex_t m_mutex;
+ std::atomic<int> _refs;
AVFormatContext *avctx;
decklink_ctx *ctx;
int no_video;
int64_t initial_audio_pts;
};
-decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : m_refCount(0)
+decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : _refs(1)
{
avctx = _avctx;
decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
ctx = (struct decklink_ctx *)cctx->ctx;
no_video = 0;
initial_audio_pts = initial_video_pts = AV_NOPTS_VALUE;
- pthread_mutex_init(&m_mutex, NULL);
}
decklink_input_callback::~decklink_input_callback()
{
- pthread_mutex_destroy(&m_mutex);
}
ULONG decklink_input_callback::AddRef(void)
{
- pthread_mutex_lock(&m_mutex);
- m_refCount++;
- pthread_mutex_unlock(&m_mutex);
-
- return (ULONG)m_refCount;
+ return ++_refs;
}
ULONG decklink_input_callback::Release(void)
{
- pthread_mutex_lock(&m_mutex);
- m_refCount--;
- pthread_mutex_unlock(&m_mutex);
-
- if (m_refCount == 0) {
+ int ret = --_refs;
+ if (!ret)
delete this;
- return 0;
- }
-
- return (ULONG)m_refCount;
+ return ret;
}
static int64_t get_pkt_pts(IDeckLinkVideoInputFrame *videoFrame,
IDeckLinkAudioInputPacket *audioFrame,
int64_t wallclock,
+ int64_t abs_wallclock,
DecklinkPtsSource pts_src,
- AVRational time_base, int64_t *initial_pts)
+ AVRational time_base, int64_t *initial_pts,
+ int copyts)
{
int64_t pts = AV_NOPTS_VALUE;
BMDTimeValue bmd_pts;
res = videoFrame->GetHardwareReferenceTimestamp(time_base.den, &bmd_pts, &bmd_duration);
break;
case PTS_SRC_WALLCLOCK:
+ /* fall through */
+ case PTS_SRC_ABS_WALLCLOCK:
{
/* MSVC does not support compound literals like AV_TIME_BASE_Q
* in C++ code (compiler error C4576) */
AVRational timebase;
timebase.num = 1;
timebase.den = AV_TIME_BASE;
- pts = av_rescale_q(wallclock, timebase, time_base);
+ if (pts_src == PTS_SRC_WALLCLOCK)
+ pts = av_rescale_q(wallclock, timebase, time_base);
+ else
+ pts = av_rescale_q(abs_wallclock, timebase, time_base);
break;
}
}
if (res == S_OK)
pts = bmd_pts / time_base.num;
- if (pts != AV_NOPTS_VALUE && *initial_pts == AV_NOPTS_VALUE)
- *initial_pts = pts;
- if (*initial_pts != AV_NOPTS_VALUE)
- pts -= *initial_pts;
+ if (!copyts) {
+ if (pts != AV_NOPTS_VALUE && *initial_pts == AV_NOPTS_VALUE)
+ *initial_pts = pts;
+ if (*initial_pts != AV_NOPTS_VALUE)
+ pts -= *initial_pts;
+ }
return pts;
}
void *audioFrameBytes;
BMDTimeValue frameTime;
BMDTimeValue frameDuration;
- int64_t wallclock = 0;
+ int64_t wallclock = 0, abs_wallclock = 0;
+ struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data;
+
+ if (ctx->autodetect) {
+ if (videoFrame && !(videoFrame->GetFlags() & bmdFrameHasNoInputSource) &&
+ ctx->bmd_mode == bmdModeUnknown)
+ {
+ ctx->bmd_mode = AUTODETECT_DEFAULT_MODE;
+ }
+ return S_OK;
+ }
ctx->frameCount++;
if (ctx->audio_pts_source == PTS_SRC_WALLCLOCK || ctx->video_pts_source == PTS_SRC_WALLCLOCK)
wallclock = av_gettime_relative();
+ if (ctx->audio_pts_source == PTS_SRC_ABS_WALLCLOCK || ctx->video_pts_source == PTS_SRC_ABS_WALLCLOCK)
+ abs_wallclock = av_gettime();
// Handle Video Frame
if (videoFrame) {
no_video = 0;
}
- pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts);
+ pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts);
pkt.dts = pkt.pts;
pkt.duration = frameDuration;
for (i = vanc_line_numbers[idx].vanc_start; i <= vanc_line_numbers[idx].vanc_end; i++) {
uint8_t *buf;
if (vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) {
- uint16_t luma_vanc[MAX_WIDTH_VANC];
- extract_luma_from_v210(luma_vanc, buf, videoFrame->GetWidth());
- txt_buf = get_metadata(avctx, luma_vanc, videoFrame->GetWidth(),
+ uint16_t vanc[MAX_WIDTH_VANC];
+ size_t vanc_size = videoFrame->GetWidth();
+ if (ctx->bmd_mode == bmdModeNTSC && videoFrame->GetWidth() * 2 <= MAX_WIDTH_VANC) {
+ vanc_size = vanc_size * 2;
+ unpack_v210(vanc, buf, videoFrame->GetWidth());
+ } else {
+ extract_luma_from_v210(vanc, buf, videoFrame->GetWidth());
+ }
+ txt_buf = get_metadata(avctx, vanc, vanc_size,
txt_buf, sizeof(txt_buf0) - (txt_buf - txt_buf0), &pkt);
}
if (i == vanc_line_numbers[idx].field0_vanc_end)
}
}
+ pkt.buf = av_buffer_create(pkt.data, pkt.size, decklink_object_free, videoFrame, 0);
+ if (pkt.buf)
+ videoFrame->AddRef();
+
if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
++ctx->dropped;
}
pkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codecpar->channels * (ctx->audio_depth / 8);
audioFrame->GetBytes(&audioFrameBytes);
audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den);
- pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts);
+ pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->audio_pts_source, ctx->audio_st->time_base, &initial_audio_pts, cctx->copyts);
pkt.dts = pkt.pts;
//fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts);
BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode,
BMDDetectedVideoInputFormatFlags)
{
+ ctx->bmd_mode = mode->GetDisplayMode();
return S_OK;
}
-static HRESULT decklink_start_input(AVFormatContext *avctx)
-{
- struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
+static int decklink_autodetect(struct decklink_cctx *cctx) {
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+ DECKLINK_BOOL autodetect_supported = false;
+ int i;
+
+ if (ctx->attr->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &autodetect_supported) != S_OK)
+ return -1;
+ if (autodetect_supported == false)
+ return -1;
+
+ ctx->autodetect = 1;
+ ctx->bmd_mode = bmdModeUnknown;
+ if (ctx->dli->EnableVideoInput(AUTODETECT_DEFAULT_MODE,
+ bmdFormat8BitYUV,
+ bmdVideoInputEnableFormatDetection) != S_OK) {
+ return -1;
+ }
+
+ if (ctx->dli->StartStreams() != S_OK) {
+ return -1;
+ }
+
+ // 1 second timeout
+ for (i = 0; i < 10; i++) {
+ av_usleep(100000);
+ /* Sometimes VideoInputFrameArrived is called without the
+ * bmdFrameHasNoInputSource flag before VideoInputFormatChanged.
+ * So don't break for bmd_mode == AUTODETECT_DEFAULT_MODE. */
+ if (ctx->bmd_mode != bmdModeUnknown &&
+ ctx->bmd_mode != AUTODETECT_DEFAULT_MODE)
+ break;
+ }
+
+ ctx->dli->PauseStreams();
+ ctx->dli->FlushStreams();
+ ctx->autodetect = 0;
+ if (ctx->bmd_mode != bmdModeUnknown) {
+ cctx->format_code = (char *)av_mallocz(5);
+ if (!cctx->format_code)
+ return -1;
+ AV_WB32(cctx->format_code, ctx->bmd_mode);
+ return 0;
+ } else {
+ return -1;
+ }
- ctx->input_callback = new decklink_input_callback(avctx);
- ctx->dli->SetCallback(ctx->input_callback);
- return ctx->dli->StartStreams();
}
extern "C" {
{
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
struct decklink_ctx *ctx;
+ class decklink_allocator *allocator;
+ class decklink_input_callback *input_callback;
AVStream *st;
HRESULT result;
char fname[1024];
cctx->raw_format = MKBETAG('v','2','1','0');
}
- strcpy (fname, avctx->filename);
+ av_strlcpy(fname, avctx->url, sizeof(fname));
tmp=strchr (fname, '@');
if (tmp != NULL) {
av_log(avctx, AV_LOG_WARNING, "The @mode syntax is deprecated and will be removed. Please use the -format_code option.\n");
/* Get input device. */
if (ctx->dl->QueryInterface(IID_IDeckLinkInput, (void **) &ctx->dli) != S_OK) {
av_log(avctx, AV_LOG_ERROR, "Could not open input device from '%s'\n",
- avctx->filename);
+ avctx->url);
ret = AVERROR(EIO);
goto error;
}
goto error;
}
- if (mode_num > 0 || cctx->format_code) {
- if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
- av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n",
- mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname);
+ if (ff_decklink_set_configs(avctx, DIRECTION_IN) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Could not set input configuration\n");
+ ret = AVERROR(EIO);
+ goto error;
+ }
+
+ input_callback = new decklink_input_callback(avctx);
+ ret = (ctx->dli->SetCallback(input_callback) == S_OK ? 0 : AVERROR_EXTERNAL);
+ input_callback->Release();
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot set input callback\n");
+ goto error;
+ }
+
+ allocator = new decklink_allocator();
+ ret = (ctx->dli->SetVideoInputFrameMemoryAllocator(allocator) == S_OK ? 0 : AVERROR_EXTERNAL);
+ allocator->Release();
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot set custom memory allocator\n");
+ goto error;
+ }
+
+ if (mode_num == 0 && !cctx->format_code) {
+ if (decklink_autodetect(cctx) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot Autodetect input stream or No signal\n");
ret = AVERROR(EIO);
goto error;
}
+ av_log(avctx, AV_LOG_INFO, "Autodetected the input mode\n");
+ }
+ if (ff_decklink_set_format(avctx, DIRECTION_IN, mode_num) < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Could not set mode number %d or format code %s for %s\n",
+ mode_num, (cctx->format_code) ? cctx->format_code : "(unset)", fname);
+ ret = AVERROR(EIO);
+ goto error;
}
#if !CONFIG_LIBZVBI
break;
case bmdFormat8BitARGB:
st->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
- st->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);;
+ st->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag((enum AVPixelFormat)st->codecpar->format);
st->codecpar->format = AV_PIX_FMT_0RGB;
st->codecpar->bit_rate = av_rescale(ctx->bmd_width * ctx->bmd_height * 32, st->time_base.den, st->time_base.num);
break;
avpacket_queue_init (avctx, &ctx->queue);
- if (decklink_start_input (avctx) != S_OK) {
+ if (ctx->dli->StartStreams() != S_OK) {
av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n");
ret = AVERROR(EIO);
goto error;