diff --git a/configure b/configure index a211975cc3..6f572f6acf 100755 --- a/configure +++ b/configure @@ -249,6 +249,7 @@ External library support: native MPEG-4/Xvid encoder exists [no] --enable-libzmq enable message passing via libzmq [no] --enable-libzvbi enable teletext support via libzvbi [no] + --enable-decklink enable Blackmagick DeckLink output [no] --enable-openal enable OpenAL 1.1 capture support [no] --enable-opencl enable OpenCL code --enable-opengl enable OpenGL rendering [no] @@ -1258,6 +1259,7 @@ EXTERNAL_LIBRARY_LIST=" avisynth bzlib crystalhd + decklink fontconfig frei0r gnutls @@ -2258,6 +2260,8 @@ alsa_indev_deps="alsa_asoundlib_h snd_pcm_htimestamp" alsa_outdev_deps="alsa_asoundlib_h" bktr_indev_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h dev_video_bktr_ioctl_bt848_h dev_ic_bt8xx_h" caca_outdev_deps="libcaca" +decklink_outdev_deps="decklink pthreads" +decklink_outdev_extralibs="-lstdc++" dshow_indev_deps="IBaseFilter" dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid" dv1394_indev_deps="dv1394" @@ -4426,6 +4430,7 @@ done enabled avisynth && { { check_lib2 "windows.h" LoadLibrary; } || { check_lib2 "dlfcn.h" dlopen -ldl; } || die "ERROR: LoadLibrary/dlopen not found for avisynth"; } +enabled decklink && { check_header DeckLinkAPI.h || die "ERROR: DeckLinkAPI.h header not found"; } enabled fontconfig && require_pkg_config fontconfig "fontconfig/fontconfig.h" FcInit enabled frei0r && { check_header frei0r.h || die "ERROR: frei0r.h header not found"; } enabled gnutls && require_pkg_config gnutls gnutls/gnutls.h gnutls_global_init diff --git a/doc/outdevs.texi b/doc/outdevs.texi index d2ccef216a..5eba26fab0 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -120,6 +120,68 @@ ffmpeg -i INPUT -pix_fmt rgb24 -f caca -list_dither colors - @end example @end itemize +@section decklink + +The decklink output device provides playback capabilities for Blackmagic +DeckLink devices. + +To enable this output device, you need the Blackmagic DeckLink SDK and you +need to configure with the appropriate @code{--extra-cflags} +and @code{--extra-ldflags}. +On Windows, you need to run the IDL files through @command{widl}. + +DeckLink is very picky about the formats it supports. Pixel format is always +uyvy422, framerate and video size must be determined for your device with +@command{-list_formats 1}. Audio sample rate is always 48 kHz. + +@subsection Options + +@table @option + +@item list_devices +If set to @option{true}, print a list of devices and exit. +Defaults to @option{false}. + +@item list_formats +If set to @option{true}, print a list of supported formats and exit. +Defaults to @option{false}. + +@item preroll +Amount of time to preroll video in seconds. +Defaults to @option{0.5}. + +@end table + +@subsection Examples + +@itemize + +@item +List output devices: +@example +ffmpeg -i test.avi -f decklink -list_devices 1 dummy +@end example + +@item +List supported formats: +@example +ffmpeg -i test.avi -f decklink -list_formats 1 'DeckLink Mini Monitor' +@end example + +@item +Play video clip: +@example +ffmpeg -i test.avi -f decklink -pix_fmt uyvy422 'DeckLink Mini Monitor' +@end example + +@item +Play video clip with non-standard framerate or video size: +@example +ffmpeg -i test.avi -f decklink -pix_fmt uyvy422 -s 720x486 -r 24000/1001 'DeckLink Mini Monitor' +@end example + +@end itemize + @section fbdev Linux framebuffer output device. diff --git a/libavdevice/Makefile b/libavdevice/Makefile index fb57c33a80..56db451025 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -17,6 +17,7 @@ OBJS-$(CONFIG_ALSA_OUTDEV) += alsa-audio-common.o \ alsa-audio-enc.o OBJS-$(CONFIG_BKTR_INDEV) += bktr.o OBJS-$(CONFIG_CACA_OUTDEV) += caca.o +OBJS-$(CONFIG_DECKLINK_OUTDEV) += decklink_enc.o decklink_enc_c.o OBJS-$(CONFIG_DSHOW_INDEV) += dshow.o dshow_enummediatypes.o \ dshow_enumpins.o dshow_filter.o \ dshow_pin.o dshow_common.o diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index 63bff11b8d..005bbc8e0d 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -49,6 +49,7 @@ void avdevice_register_all(void) REGISTER_INOUTDEV(ALSA, alsa); REGISTER_INDEV (BKTR, bktr); REGISTER_OUTDEV (CACA, caca); + REGISTER_OUTDEV (DECKLINK, decklink); REGISTER_INDEV (DSHOW, dshow); REGISTER_INDEV (DV1394, dv1394); REGISTER_INOUTDEV(FBDEV, fbdev); diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp new file mode 100644 index 0000000000..3d7cbe237c --- /dev/null +++ b/libavdevice/decklink_enc.cpp @@ -0,0 +1,588 @@ +/* + * 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 + */ + +#include +#ifdef _WIN32 +#include +typedef unsigned long buffercount_type; +#else +#include +typedef uint32_t buffercount_type; +#endif + +#include +#include + +extern "C" { +#include "libavformat/avformat.h" +#include "libavformat/internal.h" +#include "libavutil/imgutils.h" +} + +#include "decklink_enc.h" + +class decklink_callback; + +struct decklink_ctx { + /* DeckLink SDK interfaces */ + IDeckLink *dl; + IDeckLinkOutput *dlo; + decklink_callback *callback; + + /* DeckLink mode information */ + IDeckLinkDisplayModeIterator *itermode; + BMDTimeValue bmd_tb_den; + BMDTimeValue bmd_tb_num; + BMDDisplayMode bmd_mode; + int bmd_width; + int bmd_height; + + /* Streams present */ + int audio; + int video; + + /* Status */ + int playback_started; + int64_t last_pts; + + /* Options */ + int list_devices; + int list_formats; + double preroll; + + int frames_preroll; + int frames_buffer; + + sem_t semaphore; + + int channels; +}; + +/* DeckLink callback class declaration */ +class decklink_frame : public IDeckLinkVideoFrame +{ +public: + decklink_frame(struct decklink_ctx *ctx, AVFrame *avframe, long width, + long height, void *buffer) : + _ctx(ctx), _avframe(avframe), _width(width), + _height(height), _buffer(buffer), _refs(0) { } + + virtual long STDMETHODCALLTYPE GetWidth (void) { return _width; } + virtual long STDMETHODCALLTYPE GetHeight (void) { return _height; } + virtual long STDMETHODCALLTYPE GetRowBytes (void) { return _width<<1; } + virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void) { return bmdFormat8BitYUV; } + virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags (void) { return bmdVideoOutputFlagDefault; } + virtual HRESULT STDMETHODCALLTYPE GetBytes (void **buffer) { *buffer = _buffer; return S_OK; } + + virtual HRESULT STDMETHODCALLTYPE GetTimecode (BMDTimecodeFormat format, IDeckLinkTimecode **timecode) { return S_FALSE; } + virtual HRESULT STDMETHODCALLTYPE GetAncillaryData(IDeckLinkVideoFrameAncillary **ancillary) { return S_FALSE; } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } + virtual ULONG STDMETHODCALLTYPE AddRef(void) { return ++_refs; } + virtual ULONG STDMETHODCALLTYPE Release(void) { if (!--_refs) delete this; return _refs; } + + struct decklink_ctx *_ctx; + AVFrame *_avframe; + +private: + long _width; + long _height; + void *_buffer; + int _refs; +}; + +class decklink_callback : public IDeckLinkVideoOutputCallback +{ +public: + virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame *_frame, BMDOutputFrameCompletionResult result) + { + decklink_frame *frame = static_cast(_frame); + struct decklink_ctx *ctx = frame->_ctx; + AVFrame *avframe = frame->_avframe; + + av_frame_free(&avframe); + + sem_post(&ctx->semaphore); + + return S_OK; + } + virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped(void) { return S_OK; } + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } + virtual ULONG STDMETHODCALLTYPE AddRef(void) { return 1; } + virtual ULONG STDMETHODCALLTYPE Release(void) { return 1; } +}; + +#ifdef _WIN32 +static 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 +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 +#else +#define DECKLINK_STR const char * +#define DECKLINK_STRDUP av_strdup +#endif + +static 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; +} + +static int decklink_set_format(struct decklink_ctx *ctx, + int width, int height, + int tb_num, int tb_den) +{ + BMDDisplayModeSupport support; + IDeckLinkDisplayMode *mode; + + if (tb_num == 1) { + tb_num *= 1000; + tb_den *= 1000; + } + ctx->bmd_mode = bmdModeUnknown; + while ((ctx->bmd_mode == bmdModeUnknown) && ctx->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) { + 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; + } + + mode->Release(); + } + if (ctx->bmd_mode == bmdModeUnknown) + return -1; + if (ctx->dlo->DoesSupportVideoMode(ctx->bmd_mode, bmdFormat8BitYUV, + bmdVideoOutputFlagDefault, + &support, NULL) != S_OK) + return -1; + if (support == bmdDisplayModeSupported) + return 0; + + return -1; +} + +static int decklink_setup_video(AVFormatContext *avctx, AVStream *st) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + AVCodecContext *c = st->codec; + + if (ctx->video) { + av_log(avctx, AV_LOG_ERROR, "Only one video stream is supported!\n"); + return -1; + } + + if (c->pix_fmt != AV_PIX_FMT_UYVY422) { + av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!" + " Only AV_PIX_FMT_UYVY422 is supported.\n"); + return -1; + } + if (decklink_set_format(ctx, c->width, c->height, + c->time_base.num, c->time_base.den)) { + av_log(avctx, AV_LOG_ERROR, "Unsupported video size or framerate!" + " Check available formats with -list_formats 1.\n"); + return -1; + } + if (ctx->dlo->EnableVideoOutput(ctx->bmd_mode, + bmdVideoOutputFlagDefault) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not enable video output!\n"); + return -1; + } + + /* Set callback. */ + ctx->callback = new decklink_callback(); + ctx->dlo->SetScheduledFrameCompletionCallback(ctx->callback); + + /* Start video semaphore. */ + ctx->frames_preroll = c->time_base.den * ctx->preroll; + if (c->time_base.den > 1000) + ctx->frames_preroll /= 1000; + + /* Buffer twice as many frames as the preroll. */ + ctx->frames_buffer = ctx->frames_preroll * 2; + ctx->frames_buffer = FFMIN(ctx->frames_buffer, 60); + sem_init(&ctx->semaphore, 0, ctx->frames_buffer); + + /* The device expects the framerate to be fixed. */ + avpriv_set_pts_info(st, 64, c->time_base.num, c->time_base.den); + + ctx->video = 1; + + return 0; +} + +static int decklink_setup_audio(AVFormatContext *avctx, AVStream *st) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + AVCodecContext *c = st->codec; + + if (ctx->audio) { + av_log(avctx, AV_LOG_ERROR, "Only one audio stream is supported!\n"); + return -1; + } + if (c->sample_rate != 48000) { + av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate!" + " Only 48kHz is supported.\n"); + return -1; + } + if (c->channels != 2 && c->channels != 8) { + av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels!" + " Only stereo and 7.1 are supported.\n"); + return -1; + } + if (ctx->dlo->EnableAudioOutput(bmdAudioSampleRate48kHz, + bmdAudioSampleType16bitInteger, + c->channels, + bmdAudioOutputStreamTimestamped) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not enable audio output!\n"); + return -1; + } + if (ctx->dlo->BeginAudioPreroll() != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not begin audio preroll!\n"); + return -1; + } + + /* The device expects the sample rate to be fixed. */ + avpriv_set_pts_info(st, 64, 1, c->sample_rate); + ctx->channels = c->channels; + + ctx->audio = 1; + + return 0; +} + +av_cold int ff_decklink_write_trailer(AVFormatContext *avctx) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + + if (ctx->playback_started) { + BMDTimeValue actual; + ctx->dlo->StopScheduledPlayback(ctx->last_pts * ctx->bmd_tb_num, + &actual, ctx->bmd_tb_den); + ctx->dlo->DisableVideoOutput(); + if (ctx->audio) + ctx->dlo->DisableAudioOutput(); + } + + if (ctx->dlo) + ctx->dlo->Release(); + if (ctx->dl) + ctx->dl->Release(); + + if (ctx->callback) + delete ctx->callback; + + sem_destroy(&ctx->semaphore); + + av_freep(&cctx->ctx); + + return 0; +} + +static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + AVPicture *avpicture = (AVPicture *) pkt->data; + AVFrame *avframe, *tmp; + decklink_frame *frame; + buffercount_type buffered; + HRESULT hr; + + /* HACK while av_uncoded_frame() isn't implemented */ + int ret; + + tmp = av_frame_alloc(); + if (!tmp) + return AVERROR(ENOMEM); + tmp->format = AV_PIX_FMT_UYVY422; + tmp->width = ctx->bmd_width; + tmp->height = ctx->bmd_height; + ret = av_frame_get_buffer(tmp, 32); + if (ret < 0) { + av_frame_free(&tmp); + return ret; + } + av_image_copy(tmp->data, tmp->linesize, (const uint8_t **) avpicture->data, + avpicture->linesize, (AVPixelFormat) tmp->format, tmp->width, + tmp->height); + avframe = av_frame_clone(tmp); + av_frame_free(&tmp); + if (!avframe) { + av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n"); + return AVERROR(EIO); + } + /* end HACK */ + + frame = new decklink_frame(ctx, avframe, ctx->bmd_width, ctx->bmd_height, + (void *) avframe->data[0]); + if (!frame) { + av_log(avctx, AV_LOG_ERROR, "Could not create new frame.\n"); + return AVERROR(EIO); + } + + /* Always keep at most one second of frames buffered. */ + sem_wait(&ctx->semaphore); + + /* Schedule frame for playback. */ + hr = ctx->dlo->ScheduleVideoFrame((struct IDeckLinkVideoFrame *) frame, + pkt->pts * ctx->bmd_tb_num, + ctx->bmd_tb_num, ctx->bmd_tb_den); + if (hr != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not schedule video frame." + " error %08x.\n", (uint32_t) hr); + frame->Release(); + return AVERROR(EIO); + } + + ctx->dlo->GetBufferedVideoFrameCount(&buffered); + av_log(avctx, AV_LOG_DEBUG, "Buffered video frames: %d.\n", (int) buffered); + if (pkt->pts > 2 && buffered <= 2) + av_log(avctx, AV_LOG_WARNING, "There are not enough buffered video frames." + " Video may misbehave!\n"); + + /* Preroll video frames. */ + if (!ctx->playback_started && pkt->pts > ctx->frames_preroll) { + av_log(avctx, AV_LOG_DEBUG, "Ending audio preroll.\n"); + if (ctx->audio && ctx->dlo->EndAudioPreroll() != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not end audio preroll!\n"); + return AVERROR(EIO); + } + av_log(avctx, AV_LOG_DEBUG, "Starting scheduled playback.\n"); + if (ctx->dlo->StartScheduledPlayback(0, ctx->bmd_tb_den, 1.0) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not start scheduled playback!\n"); + return AVERROR(EIO); + } + ctx->playback_started = 1; + } + + return 0; +} + +static int decklink_write_audio_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + int sample_count = pkt->size / (ctx->channels << 1); + buffercount_type buffered; + + ctx->dlo->GetBufferedAudioSampleFrameCount(&buffered); + if (pkt->pts > 1 && !buffered) + av_log(avctx, AV_LOG_WARNING, "There's no buffered audio." + " Audio will misbehave!\n"); + + if (ctx->dlo->ScheduleAudioSamples(pkt->data, sample_count, pkt->pts, + bmdAudioSampleRate48kHz, NULL) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not schedule audio samples.\n"); + return AVERROR(EIO); + } + + return 0; +} + +extern "C" { + +av_cold int ff_decklink_write_header(AVFormatContext *avctx) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx; + IDeckLinkIterator *iter; + IDeckLink *dl = NULL; + unsigned int n; + + 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) { + 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 AVERROR_EXIT; + } + + /* Open device. */ + while (iter->Next(&dl) == S_OK) { + const char *displayName; + IDeckLink_GetDisplayName(dl, &displayName); + if (!strcmp(avctx->filename, 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", avctx->filename); + return AVERROR(EIO); + } + + /* Get output device. */ + if (ctx->dl->QueryInterface(IID_IDeckLinkOutput, (void **) &ctx->dlo) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not open output device from '%s'\n", + avctx->filename); + ctx->dl->Release(); + return AVERROR(EIO); + } + + if (ctx->dlo->GetDisplayModeIterator(&ctx->itermode) != S_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not get Display Mode Iterator\n"); + ctx->dl->Release(); + return AVERROR(EIO); + } + + /* List supported formats. */ + if (ctx->list_formats) { + IDeckLinkDisplayMode *mode; + + av_log(avctx, AV_LOG_INFO, "Supported formats for '%s':\n", + avctx->filename); + while (ctx->itermode->Next(&mode) == S_OK) { + BMDTimeValue tb_num, tb_den; + mode->GetFrameRate(&tb_num, &tb_den); + av_log(avctx, AV_LOG_INFO, "\t%ldx%ld at %d/%d fps", + 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(); + } + + ctx->itermode->Release(); + ctx->dlo->Release(); + ctx->dl->Release(); + return AVERROR_EXIT; + } + + /* Setup streams. */ + for (n = 0; n < avctx->nb_streams; n++) { + AVStream *st = avctx->streams[n]; + AVCodecContext *c = st->codec; + if (c->codec_type == AVMEDIA_TYPE_AUDIO) { + if (decklink_setup_audio(avctx, st)) + goto error; + } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) { + if (decklink_setup_video(avctx, st)) + goto error; + } else { + av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n"); + goto error; + } + } + ctx->itermode->Release(); + + return 0; + +error: + + ctx->dlo->Release(); + ctx->dl->Release(); + + return AVERROR(EIO); +} + +int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *) avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *) cctx->ctx; + AVStream *st = avctx->streams[pkt->stream_index]; + + ctx->last_pts = FFMAX(ctx->last_pts, pkt->pts); + + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + return decklink_write_video_packet(avctx, pkt); + else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + return decklink_write_audio_packet(avctx, pkt); + + return AVERROR(EIO); +} + +} /* extern "C" */ diff --git a/libavdevice/decklink_enc.h b/libavdevice/decklink_enc.h new file mode 100644 index 0000000000..0b9ad8f79e --- /dev/null +++ b/libavdevice/decklink_enc.h @@ -0,0 +1,43 @@ +/* + * 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 + */ + +struct decklink_cctx { + const AVClass *cclass; + + void *ctx; + + /* Options */ + int list_devices; + int list_formats; + double preroll; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int ff_decklink_write_header(AVFormatContext *avctx); +int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt); +int ff_decklink_write_trailer(AVFormatContext *avctx); + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/libavdevice/decklink_enc_c.c b/libavdevice/decklink_enc_c.c new file mode 100644 index 0000000000..b98e0b4fa4 --- /dev/null +++ b/libavdevice/decklink_enc_c.c @@ -0,0 +1,55 @@ +/* + * 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 + */ + +#include "libavformat/avformat.h" +#include "libavutil/opt.h" + +#include "decklink_enc.h" + +#define OFFSET(x) offsetof(struct decklink_cctx, x) +#define ENC AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "list_devices", "list available devices" , OFFSET(list_devices), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, ENC }, + { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, ENC }, + { "preroll" , "video preroll in seconds", OFFSET(preroll ), AV_OPT_TYPE_DOUBLE, { .dbl = 0.5 }, 0, 5, ENC }, + { NULL }, +}; + +static const AVClass decklink_muxer_class = { + .class_name = "Blackmagic DeckLink muxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVOutputFormat ff_decklink_muxer = { + .name = "decklink", + .long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink output"), + .audio_codec = AV_CODEC_ID_PCM_S16LE, + .video_codec = AV_CODEC_ID_RAWVIDEO, + .subtitle_codec = AV_CODEC_ID_NONE, + .flags = AVFMT_NOFILE | AVFMT_RAWPICTURE, + .priv_class = &decklink_muxer_class, + .priv_data_size = sizeof(struct decklink_cctx), + .write_header = ff_decklink_write_header, + .write_packet = ff_decklink_write_packet, + .write_trailer = ff_decklink_write_trailer, +}; diff --git a/libavdevice/version.h b/libavdevice/version.h index a62177518f..0dedc73551 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVDEVICE_VERSION_MAJOR 55 -#define LIBAVDEVICE_VERSION_MINOR 7 +#define LIBAVDEVICE_VERSION_MINOR 8 #define LIBAVDEVICE_VERSION_MICRO 100 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \