X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavdevice%2Fdecklink_dec.cpp;h=7fabef231c71fe16231afc75101382bc8d4f99c9;hb=98e18910a3f19a1139654b2d14d0f3b2b3625cd1;hp=28a748a28f8ffc4873c31ebb9f15c8336c4f3a25;hpb=7386b4ff39506b7a34b3689cc4b05993ed9b4a4f;p=ffmpeg diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 28a748a28f8..7fabef231c7 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" { @@ -98,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; @@ -464,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); @@ -546,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; @@ -555,42 +604,30 @@ 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, @@ -715,6 +752,35 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); } no_video = 0; + + // Handle Timecode (if requested) + if (ctx->tc_format) { + IDeckLinkTimecode *timecode; + if (videoFrame->GetTimecode(ctx->tc_format, &timecode) == S_OK) { + const char *tc = NULL; + DECKLINK_STR decklink_tc; + if (timecode->GetString(&decklink_tc) == S_OK) { + tc = DECKLINK_STRDUP(decklink_tc); + DECKLINK_FREE(decklink_tc); + } + timecode->Release(); + if (tc) { + AVDictionary* metadata_dict = NULL; + int metadata_len; + uint8_t* packed_metadata; + if (av_dict_set(&metadata_dict, "timecode", tc, AV_DICT_DONT_STRDUP_VAL) >= 0) { + packed_metadata = av_packet_pack_dictionary(metadata_dict, &metadata_len); + av_dict_free(&metadata_dict); + if (packed_metadata) { + if (av_packet_add_side_data(&pkt, AV_PKT_DATA_STRINGS_METADATA, packed_metadata, metadata_len) < 0) + av_freep(&packed_metadata); + } + } + } + } else { + av_log(avctx, AV_LOG_DEBUG, "Unable to find timecode.\n"); + } + } } pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock, abs_wallclock, ctx->video_pts_source, ctx->video_st->time_base, &initial_video_pts, cctx->copyts); @@ -799,6 +865,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; } @@ -911,6 +981,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]; @@ -926,6 +998,8 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) ctx->teletext_lines = cctx->teletext_lines; ctx->preroll = cctx->preroll; ctx->duplex_mode = cctx->duplex_mode; + if (cctx->tc_format > 0 && (unsigned int)cctx->tc_format < FF_ARRAY_ELEMS(decklink_timecode_format_map)) + ctx->tc_format = decklink_timecode_format_map[cctx->tc_format]; if (cctx->video_input > 0 && (unsigned int)cctx->video_input < FF_ARRAY_ELEMS(decklink_video_connection_map)) ctx->video_input = decklink_video_connection_map[cctx->video_input]; if (cctx->audio_input > 0 && (unsigned int)cctx->audio_input < FF_ARRAY_ELEMS(decklink_audio_connection_map)) @@ -1001,8 +1075,21 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) goto error; } - ctx->input_callback = new decklink_input_callback(avctx); - ctx->dli->SetCallback(ctx->input_callback); + 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) { @@ -1166,6 +1253,15 @@ int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) avpacket_queue_get(&ctx->queue, pkt, 1); + if (ctx->tc_format && !(av_dict_get(ctx->video_st->metadata, "timecode", NULL, 0))) { + int size; + const uint8_t *side_metadata = av_packet_get_side_data(pkt, AV_PKT_DATA_STRINGS_METADATA, &size); + if (side_metadata) { + if (av_packet_unpack_dictionary(side_metadata, size, &ctx->video_st->metadata) < 0) + av_log(avctx, AV_LOG_ERROR, "Unable to set timecode\n"); + } + } + return 0; }