Signed-off-by: Deti fliegl <fli...@baycom.de> --- libavdevice/decklink_common.cpp | 227 ++++++++++++++++++ libavdevice/decklink_common.h | 98 ++++++++ libavdevice/decklink_common_c.h | 32 +++ libavdevice/decklink_dec.cpp | 520 ++++++++++++++++++++++++++++++++++++++++ libavdevice/decklink_dec.h | 32 +++ libavdevice/decklink_dec_c.c | 54 +++++ 6 files changed, 963 insertions(+) create mode 100644 libavdevice/decklink_common.cpp create mode 100644 libavdevice/decklink_common.h create mode 100644 libavdevice/decklink_common_c.h create mode 100644 libavdevice/decklink_dec.cpp create mode 100644 libavdevice/decklink_dec.h create mode 100644 libavdevice/decklink_dec_c.c
diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp new file mode 100644 index 0000000..7a9fed5 --- /dev/null +++ b/libavdevice/decklink_common.cpp @@ -0,0 +1,227 @@ +/* + * 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 + +#include <pthread.h> +#include <semaphore.h> + +extern "C" { +#include "libavformat/avformat.h" +#include "libavformat/internal.h" +#include "libavutil/imgutils.h" +} + +#include "decklink_common.h" + +#ifdef _WIN32 +IDeckLinkIterator *CreateDeckLinkIteratorInstance(void) +{ + IDeckLinkIterator *iter; + + if (CoInitialize(NULL) != S_OK) { + 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 + +/* free() is needed for a string returned by the DeckLink SDL. */ +#undef free + +#ifdef _WIN32 +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 +#else +#define DECKLINK_STR const char * +#define DECKLINK_STRDUP av_strdup +#endif + +HRESULT IDeckLink_GetDisplayName(IDeckLink *This, const char **displayName) +{ + DECKLINK_STR tmpDisplayName; + HRESULT hr = This->GetDisplayName(&tmpDisplayName); + if (hr != S_OK) + return hr; + *displayName = DECKLINK_STRDUP(tmpDisplayName); + free((void *) tmpDisplayName); + return hr; +} + +int decklink_set_format(struct decklink_ctx *ctx, + int width, int height, + int tb_num, int tb_den, + decklink_direction_t direction, int num) +{ + BMDDisplayModeSupport support; + IDeckLinkDisplayModeIterator *itermode; + IDeckLinkDisplayMode *mode; + int i = 1; + HRESULT res; + + if (direction == DIRECTION_IN) { + res = ctx->dli->GetDisplayModeIterator (&itermode); + } else { + res = ctx->dlo->GetDisplayModeIterator (&itermode); + } + + if (res!= S_OK) { + av_log(NULL, AV_LOG_ERROR, "Could not get Display Mode Iterator\n"); + return AVERROR(EIO); + } + + + if (tb_num == 1) { + tb_num *= 1000; + tb_den *= 1000; + } + 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(); + + mode->GetFrameRate(&bmd_tb_num, &bmd_tb_den); + + if ((bmd_width == width && bmd_height == height && + bmd_tb_num == tb_num && bmd_tb_den == tb_den) || i == num) { + ctx->bmd_mode = mode->GetDisplayMode(); + 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 = mode->GetFieldDominance(); + av_log(NULL, AV_LOG_INFO, "Found Decklink mode %d x %d with rate %.2f%s\n", bmd_width, bmd_height, (float)bmd_tb_den/(float)bmd_tb_num, (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 decklink_set_format(struct decklink_ctx *ctx, decklink_direction_t direction, int num) { + return decklink_set_format(ctx, 0, 0, 0, 0, direction, num); +} + +int 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; + IDeckLink_GetDisplayName(dl, &displayName); + av_log(avctx, AV_LOG_INFO, "\t'%s'\n", displayName); + av_free((void *) displayName); + dl->Release(); + } + iter->Release(); + return 0; +} + +int 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; + int i=0; + HRESULT res; + + if (direction == DIRECTION_IN) { + 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", + avctx->filename); + while (itermode->Next(&mode) == S_OK) { + BMDTimeValue tb_num, tb_den; + mode->GetFrameRate(&tb_num, &tb_den); + av_log(avctx, AV_LOG_INFO, "\t%d\t%ldx%ld at %d/%d fps", + ++i,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; + } + av_log(avctx, AV_LOG_INFO, "\n"); + mode->Release(); + } + + itermode->Release(); + + return 0; +} diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h new file mode 100644 index 0000000..dc53f23 --- /dev/null +++ b/libavdevice/decklink_common.h @@ -0,0 +1,98 @@ +/* + * Blackmagic DeckLink common code + * 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 "decklink_common_c.h" + +class decklink_output_callback; +class decklink_input_callback; + +typedef struct AVPacketQueue { + AVPacketList *first_pkt, *last_pkt; + int nb_packets; + unsigned long long size; + int abort_request; + pthread_mutex_t mutex; + pthread_cond_t cond; + AVFormatContext *avctx; +} AVPacketQueue; + +struct decklink_ctx { + /* DeckLink SDK interfaces */ + IDeckLink *dl; + IDeckLinkOutput *dlo; + IDeckLinkInput *dli; + decklink_output_callback *output_callback; + decklink_input_callback *input_callback; + + /* DeckLink mode information */ + BMDTimeValue bmd_tb_den; + BMDTimeValue bmd_tb_num; + BMDDisplayMode bmd_mode; + int bmd_width; + int bmd_height; + int bmd_field_dominance; + + /* Capture buffer queue */ + AVPacketQueue queue; + + /* Streams present */ + int audio; + int video; + + /* Status */ + int playback_started; + int capture_started; + int64_t last_pts; + unsigned long frameCount; + unsigned int dropped; + AVStream *audio_st; + AVStream *video_st; + + /* Options */ + int list_devices; + int list_formats; + double preroll; + + int frames_preroll; + int frames_buffer; + + sem_t semaphore; + + int channels; +}; + +typedef enum { DIRECTION_IN, DIRECTION_OUT} decklink_direction_t; + +#ifdef _WIN32 +typedef unsigned long buffercount_type; +IDeckLinkIterator *CreateDeckLinkIteratorInstance(void); +char *dup_wchar_to_utf8(wchar_t *w); +#else +typedef uint32_t buffercount_type; +#endif + + +HRESULT IDeckLink_GetDisplayName(IDeckLink *This, const char **displayName); +int decklink_set_format(struct decklink_ctx *ctx, int width, int height, int tb_num, int tb_den, decklink_direction_t direction = DIRECTION_OUT, int num = 0); +int decklink_set_format(struct decklink_ctx *ctx, decklink_direction_t direction, int num); +int decklink_list_devices(AVFormatContext *avctx); +int decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direction = DIRECTION_OUT); + diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h new file mode 100644 index 0000000..861a51a --- /dev/null +++ b/libavdevice/decklink_common_c.h @@ -0,0 +1,32 @@ +/* + * Blackmagic DeckLink common code + * Copyright (c) 2013-2014 Ramiro Polla + * + * 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 + */ + +struct decklink_cctx { + const AVClass *cclass; + + void *ctx; + + /* Options */ + int list_devices; + int list_formats; + double preroll; +}; + diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp new file mode 100644 index 0000000..ebf555f --- /dev/null +++ b/libavdevice/decklink_dec.cpp @@ -0,0 +1,520 @@ +/* + * Blackmagic DeckLink output + * Copyright (c) 2013-2014 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> + +#include <pthread.h> +#include <semaphore.h> + +extern "C" { +#include "libavformat/avformat.h" +#include "libavformat/internal.h" +#include "libavutil/imgutils.h" +} + +#include "decklink_common.h" +#include "decklink_dec.h" + +static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) +{ + memset(q, 0, sizeof(AVPacketQueue)); + pthread_mutex_init(&q->mutex, NULL); + pthread_cond_init(&q->cond, NULL); + q->avctx = avctx; +} + +static void avpacket_queue_flush(AVPacketQueue *q) +{ + AVPacketList *pkt, *pkt1; + + pthread_mutex_lock(&q->mutex); + for (pkt = q->first_pkt; pkt != NULL; pkt = pkt1) { + pkt1 = pkt->next; + av_free_packet(&pkt->pkt); + av_freep(&pkt); + } + q->last_pkt = NULL; + q->first_pkt = NULL; + q->nb_packets = 0; + q->size = 0; + pthread_mutex_unlock(&q->mutex); +} + +static void avpacket_queue_end(AVPacketQueue *q) +{ + avpacket_queue_flush(q); + pthread_mutex_destroy(&q->mutex); + pthread_cond_destroy(&q->cond); +} + +static unsigned long long avpacket_queue_size(AVPacketQueue *q) +{ + unsigned long long size; + pthread_mutex_lock(&q->mutex); + size = q->size; + pthread_mutex_unlock(&q->mutex); + return size; +} + +static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt) +{ + AVPacketList *pkt1; + + // Drop Packet if queue size is > 1GB + if (avpacket_queue_size(q) > 1024 * 1024 * 1024 ) { + av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); + return -1; + } + /* duplicate the packet */ + if (av_dup_packet(pkt) < 0) { + return -1; + } + + pkt1 = (AVPacketList *)av_malloc(sizeof(AVPacketList)); + if (!pkt1) { + return -1; + } + pkt1->pkt = *pkt; + pkt1->next = NULL; + + pthread_mutex_lock(&q->mutex); + + if (!q->last_pkt) { + q->first_pkt = pkt1; + } else { + q->last_pkt->next = pkt1; + } + + q->last_pkt = pkt1; + q->nb_packets++; + q->size += pkt1->pkt.size + sizeof(*pkt1); + + pthread_cond_signal(&q->cond); + + pthread_mutex_unlock(&q->mutex); + return 0; +} + +static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block) +{ + AVPacketList *pkt1; + int ret; + + pthread_mutex_lock(&q->mutex); + + for (;; ) { + pkt1 = q->first_pkt; + if (pkt1) { + q->first_pkt = pkt1->next; + if (!q->first_pkt) { + q->last_pkt = NULL; + } + q->nb_packets--; + q->size -= pkt1->pkt.size + sizeof(*pkt1); + *pkt = pkt1->pkt; + av_free(pkt1); + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + pthread_cond_wait(&q->cond, &q->mutex); + } + } + pthread_mutex_unlock(&q->mutex); + return ret; +} + +class decklink_input_callback : public IDeckLinkInputCallback +{ +public: + decklink_input_callback(AVFormatContext *_avctx); + ~decklink_input_callback(); + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } + virtual ULONG STDMETHODCALLTYPE AddRef(void); + virtual ULONG STDMETHODCALLTYPE Release(void); + virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags); + virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*); + +private: + ULONG m_refCount; + pthread_mutex_t m_mutex; + AVFormatContext *avctx; + decklink_ctx *ctx; + int no_video; + int64_t initial_video_pts; + int64_t initial_audio_pts; +}; + +decklink_input_callback::decklink_input_callback(AVFormatContext *_avctx) : m_refCount(0) +{ + avctx = _avctx; + decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + ctx = (struct decklink_ctx *) cctx->ctx; + 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; +} + +ULONG decklink_input_callback::Release(void) +{ + pthread_mutex_lock(&m_mutex); + m_refCount--; + pthread_mutex_unlock(&m_mutex); + + if (m_refCount == 0) { + delete this; + return 0; + } + + return (ULONG)m_refCount; +} + +HRESULT decklink_input_callback::VideoInputFrameArrived( + IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame) +{ + void *frameBytes; + void *audioFrameBytes; + BMDTimeValue frameTime; + BMDTimeValue frameDuration; + + ctx->frameCount++; + + // Handle Video Frame + if (videoFrame) { + AVPacket pkt; + AVCodecContext *c; + av_init_packet(&pkt); + c = ctx->video_st->codec; + if (ctx->frameCount % 25 == 0) { + unsigned long long qsize = avpacket_queue_size(&ctx->queue); + av_log(avctx, AV_LOG_DEBUG, + "Frame received (#%lu) - Valid (%liB) - QSize %fMB\n", + ctx->frameCount, + videoFrame->GetRowBytes() * videoFrame->GetHeight(), + (double)qsize / 1024 / 1024); + } + + videoFrame->GetBytes(&frameBytes); + videoFrame->GetStreamTime(&frameTime, &frameDuration, + ctx->video_st->time_base.den); + + if (videoFrame->GetFlags() & bmdFrameHasNoInputSource) { + unsigned bars[8] = { + 0xEA80EA80, 0xD292D210, 0xA910A9A5, 0x90229035, + 0x6ADD6ACA, 0x51EF515A, 0x286D28EF, 0x10801080 }; + int width = videoFrame->GetWidth(); + int height = videoFrame->GetHeight(); + unsigned *p = (unsigned *)frameBytes; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x += 2) + *p++ = bars[(x * 8) / width]; + } + + if (!no_video) { + av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - No input signal detected " + "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); + } + no_video = 1; + } else { + if (no_video) { + av_log(avctx, AV_LOG_WARNING, "Frame received (#%lu) - Input returned " + "- Frames dropped %u\n", ctx->frameCount, ++ctx->dropped); + } + no_video = 0; + } + + pkt.pts = frameTime / ctx->video_st->time_base.num; + + if (initial_video_pts == AV_NOPTS_VALUE) { + initial_video_pts = pkt.pts; + } + + pkt.pts -= initial_video_pts; + pkt.dts = pkt.pts; + + pkt.duration = frameDuration; + //To be made sure it still applies + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index = ctx->video_st->index; + pkt.data = (uint8_t *)frameBytes; + pkt.size = videoFrame->GetRowBytes() * + videoFrame->GetHeight(); + //fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts); + c->frame_number++; + if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + ++ctx->dropped; + } + } + + // Handle Audio Frame + if (audioFrame) { + AVCodecContext *c; + AVPacket pkt; + BMDTimeValue audio_pts; + av_init_packet(&pkt); + + c = ctx->audio_st->codec; + //hack among hacks + pkt.size = audioFrame->GetSampleFrameCount() * ctx->audio_st->codec->channels * (16 / 8); + audioFrame->GetBytes(&audioFrameBytes); + audioFrame->GetPacketTime(&audio_pts, ctx->audio_st->time_base.den); + pkt.pts = audio_pts / ctx->audio_st->time_base.num; + + if (initial_audio_pts == AV_NOPTS_VALUE) { + initial_audio_pts = pkt.pts; + } + + pkt.pts -= initial_audio_pts; + pkt.dts = pkt.pts; + + //fprintf(stderr,"Audio Frame size %d ts %d\n", pkt.size, pkt.pts); + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index = ctx->audio_st->index; + pkt.data = (uint8_t *)audioFrameBytes; + + c->frame_number++; + if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + ++ctx->dropped; + } + } + + return S_OK; +} + +HRESULT decklink_input_callback::VideoInputFormatChanged( + BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, + BMDDetectedVideoInputFormatFlags) +{ + return S_OK; +} + +static HRESULT decklink_start_input(AVFormatContext *avctx) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + + ctx->input_callback = new decklink_input_callback(avctx); + ctx->dli->SetCallback(ctx->input_callback); + return ctx->dli->StartStreams(); +} + +extern "C" { + +av_cold int ff_decklink_read_close(AVFormatContext *avctx) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + + if (ctx->capture_started) { + ctx->dli->StopStreams(); + ctx->dli->DisableVideoInput(); + ctx->dli->DisableAudioInput(); + } + + if (ctx->dli) + ctx->dli->Release(); + if (ctx->dl) + ctx->dl->Release(); + + avpacket_queue_end(&ctx->queue); + + av_freep(&cctx->ctx); + + return 0; +} + +av_cold int ff_decklink_read_header(AVFormatContext *avctx) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx; + IDeckLinkDisplayModeIterator *itermode; + IDeckLinkIterator *iter; + IDeckLink *dl = NULL; + AVStream *st; + HRESULT result; + char fname[1024]; + char *tmp; + int mode_num = 0; + + ctx = (struct decklink_ctx *) av_mallocz(sizeof(struct decklink_ctx)); + if (!ctx) + return AVERROR(ENOMEM); + ctx->list_devices = cctx->list_devices; + ctx->list_formats = cctx->list_formats; + ctx->preroll = cctx->preroll; + cctx->ctx = ctx; + + iter = CreateDeckLinkIteratorInstance(); + if (!iter) { + av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n"); + return AVERROR(EIO); + } + + /* List available devices. */ + if (ctx->list_devices) { + decklink_list_devices(avctx); + return AVERROR_EXIT; + } + + strcpy (fname, avctx->filename); + tmp=strchr (fname, '@'); + if (tmp != NULL) { + mode_num = atoi (tmp+1); + *tmp = 0; + } + + /* Open device. */ + while (iter->Next(&dl) == S_OK) { + const char *displayName; + IDeckLink_GetDisplayName(dl, &displayName); + if (!strcmp(fname, displayName)) { + av_free((void *) displayName); + ctx->dl = dl; + break; + } + av_free((void *) displayName); + dl->Release(); + } + iter->Release(); + if (!ctx->dl) { + av_log(avctx, AV_LOG_ERROR, "Could not open '%s'\n", fname); + return AVERROR(EIO); + } + + /* Get input device. */ + if (ctx->dl->QueryInterface(IID_IDeckLinkInput, (void **) &ctx->dli) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not open output device from '%s'\n", + avctx->filename); + ctx->dl->Release(); + return AVERROR(EIO); + } + + /* List supported formats. */ + if (ctx->list_formats) { + decklink_list_formats(avctx, DIRECTION_IN); + ctx->dli->Release(); + ctx->dl->Release(); + return AVERROR_EXIT; + } + + if (ctx->dli->GetDisplayModeIterator(&itermode) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n"); + ctx->dl->Release(); + return AVERROR(EIO); + } + + if (mode_num > 0) { + if (decklink_set_format(ctx, DIRECTION_IN, mode_num) < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not set mode %d for %s\n", mode_num, fname); + goto error; + } + } + + itermode->Release(); + + /* Setup streams. */ + st = avformat_new_stream(avctx, NULL); + if (!st) { + av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n"); + goto error; + } + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = AV_CODEC_ID_PCM_S16LE; + st->codec->sample_rate = bmdAudioSampleRate48kHz; + st->codec->channels = 2; + avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ + ctx->audio_st=st; + + st = avformat_new_stream(avctx, NULL); + if (!st) { + av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n"); + goto error; + } + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + st->codec->codec_id = AV_CODEC_ID_RAWVIDEO; + st->codec->width = 1920; + st->codec->height = 1080; + + st->codec->pix_fmt = AV_PIX_FMT_UYVY422; + st->codec->bit_rate = avpicture_get_size(st->codec->pix_fmt, 1920, 1080) * /*av_q2d(25)*/ 25 * 8; + st->codec->codec_tag = MKTAG('U', 'Y', 'V', 'Y'); + avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ + ctx->video_st=st; + + result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2); + + if (result != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Cannot enable audio input\n"); + goto error; + } + + result = ctx->dli->EnableVideoInput(ctx->bmd_mode, bmdFormat8BitYUV, bmdVideoInputFlagDefault); + + if (result != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Cannot enable video input\n"); + goto error; + } + + avpacket_queue_init (avctx, &ctx->queue); + + if (decklink_start_input (avctx) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n"); + goto error; + } + + return 0; + +error: + + ctx->dli->Release(); + ctx->dl->Release(); + + return AVERROR(EIO); +} + +int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + + avpacket_queue_get(&ctx->queue, pkt, 1); + + return 0; +} + +} /* extern "C" */ diff --git a/libavdevice/decklink_dec.h b/libavdevice/decklink_dec.h new file mode 100644 index 0000000..6bd9226 --- /dev/null +++ b/libavdevice/decklink_dec.h @@ -0,0 +1,32 @@ +/* + * Blackmagic DeckLink output + * Copyright (c) 2013-2014 Ramiro Polla + * + * 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 + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int ff_decklink_read_header(AVFormatContext *avctx); +int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt); +int ff_decklink_read_close(AVFormatContext *avctx); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c new file mode 100644 index 0000000..2aea277 --- /dev/null +++ b/libavdevice/decklink_dec_c.c @@ -0,0 +1,54 @@ +/* + * Blackmagic DeckLink output + * Copyright (c) 2014 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 "libavformat/avformat.h" +#include "libavutil/opt.h" + +#include "decklink_common_c.h" +#include "decklink_dec.h" + +#define OFFSET(x) offsetof(struct decklink_cctx, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "list_devices", "list available devices" , OFFSET(list_devices), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, + { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, + { NULL }, +}; + +static const AVClass decklink_demuxer_class = { + .class_name = "Blackmagic DeckLink demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, +}; + +AVInputFormat ff_decklink_demuxer = { + .name = "decklink", + .long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink input"), + .flags = AVFMT_NOFILE | AVFMT_RAWPICTURE, + .priv_class = &decklink_demuxer_class, + .priv_data_size = sizeof(struct decklink_cctx), + .read_header = ff_decklink_read_header, + .read_packet = ff_decklink_read_packet, + .read_close = ff_decklink_read_close, +}; -- 1.9.1
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel