X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavdevice%2Fdecklink_dec.cpp;h=94dae2600328b0eb489165af063854a5aebed2a6;hb=ba8a1d161820f9e5f1485118a0f508e312f69a8b;hp=e90b42811a9044f68928e2121824bfb6dc62c506;hpb=814e217d14e4edfb9932f10d323a0208c9af2068;p=ffmpeg diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index e90b42811a9..974ee1d94c3 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -21,6 +21,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +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" { @@ -36,6 +39,7 @@ 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" @@ -49,6 +53,7 @@ extern "C" { #include "decklink_dec.h" #define MAX_WIDTH_VANC 1920 +const BMDDisplayMode AUTODETECT_DEFAULT_MODE = bmdModeNTSC; typedef struct VANCLineNumber { BMDDisplayMode mode; @@ -96,6 +101,52 @@ static VANCLineNumber vanc_line_numbers[] = { {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 _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; @@ -147,6 +198,17 @@ static void extract_luma_from_v210(uint16_t *dst, const uint8_t *src, int width) } } +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; @@ -451,24 +513,25 @@ static unsigned long long avpacket_queue_size(AVPacketQueue *q) 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); @@ -533,8 +596,7 @@ public: virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*); private: - ULONG m_refCount; - pthread_mutex_t m_mutex; + std::atomic _refs; AVFormatContext *avctx; decklink_ctx *ctx; int no_video; @@ -542,49 +604,39 @@ private: 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; @@ -604,23 +656,30 @@ static int64_t get_pkt_pts(IDeckLinkVideoInputFrame *videoFrame, 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; } @@ -632,11 +691,23 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( 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) { @@ -683,7 +754,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( 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; @@ -729,9 +800,15 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( 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) @@ -759,6 +836,10 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( } } + 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; } @@ -774,7 +855,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( 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); @@ -794,17 +875,56 @@ HRESULT decklink_input_callback::VideoInputFormatChanged( 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" { @@ -832,6 +952,8 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) { 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]; @@ -889,7 +1011,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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"); @@ -904,7 +1026,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) /* 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; } @@ -916,13 +1038,41 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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 @@ -976,7 +1126,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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; @@ -1052,7 +1202,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) 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;