libavdevice/decklink: add support for 10-bit output for Decklink SDI

Can be tested via the following command:

./ffmpeg -i foo.ts -f decklink -vcodec v210 'DeckLink Duo (1)'

Note that the 8-bit support works as it did before, and setting
the pix_fmt isn't required for 10-bit mode.  The code defaults to
operating in 8-bit mode when no vcodec is specified, for backward
compatibility.

Updated to reflect feedback from Marton Balint <cus@passwd.hu>

Signed-off-by: Devin Heitmueller <dheitmueller@ltnglobal.com>
Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
Devin Heitmueller 2017-10-06 08:55:44 -04:00 committed by Marton Balint
parent 278588cd0b
commit 77f7d710e0
1 changed files with 82 additions and 28 deletions

View File

@ -44,20 +44,45 @@ extern "C" {
class decklink_frame : public IDeckLinkVideoFrame class decklink_frame : public IDeckLinkVideoFrame
{ {
public: public:
decklink_frame(struct decklink_ctx *ctx, AVFrame *avframe) : decklink_frame(struct decklink_ctx *ctx, AVFrame *avframe, AVCodecID codec_id, int height, int width) :
_ctx(ctx), _avframe(avframe), _refs(1) { } _ctx(ctx), _avframe(avframe), _avpacket(NULL), _codec_id(codec_id), _height(height), _width(width), _refs(1) { }
decklink_frame(struct decklink_ctx *ctx, AVPacket *avpacket, AVCodecID codec_id, int height, int width) :
_ctx(ctx), _avframe(NULL), _avpacket(avpacket), _codec_id(codec_id), _height(height), _width(width), _refs(1) { }
virtual long STDMETHODCALLTYPE GetWidth (void) { return _width; }
virtual long STDMETHODCALLTYPE GetHeight (void) { return _height; }
virtual long STDMETHODCALLTYPE GetRowBytes (void)
{
if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
return _avframe->linesize[0] < 0 ? -_avframe->linesize[0] : _avframe->linesize[0];
else
return ((GetWidth() + 47) / 48) * 128;
}
virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void)
{
if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
return bmdFormat8BitYUV;
else
return bmdFormat10BitYUV;
}
virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags (void)
{
if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME)
return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault;
else
return bmdFrameFlagDefault;
}
virtual long STDMETHODCALLTYPE GetWidth (void) { return _avframe->width; }
virtual long STDMETHODCALLTYPE GetHeight (void) { return _avframe->height; }
virtual long STDMETHODCALLTYPE GetRowBytes (void) { return _avframe->linesize[0] < 0 ? -_avframe->linesize[0] : _avframe->linesize[0]; }
virtual BMDPixelFormat STDMETHODCALLTYPE GetPixelFormat(void) { return bmdFormat8BitYUV; }
virtual BMDFrameFlags STDMETHODCALLTYPE GetFlags (void) { return _avframe->linesize[0] < 0 ? bmdFrameFlagFlipVertical : bmdFrameFlagDefault; }
virtual HRESULT STDMETHODCALLTYPE GetBytes (void **buffer) virtual HRESULT STDMETHODCALLTYPE GetBytes (void **buffer)
{ {
if (_avframe->linesize[0] < 0) if (_codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
*buffer = (void *)(_avframe->data[0] + _avframe->linesize[0] * (_avframe->height - 1)); if (_avframe->linesize[0] < 0)
else *buffer = (void *)(_avframe->data[0] + _avframe->linesize[0] * (_avframe->height - 1));
*buffer = (void *)(_avframe->data[0]); else
*buffer = (void *)(_avframe->data[0]);
} else {
*buffer = (void *)(_avpacket->data);
}
return S_OK; return S_OK;
} }
@ -71,6 +96,7 @@ public:
int ret = --_refs; int ret = --_refs;
if (!ret) { if (!ret) {
av_frame_free(&_avframe); av_frame_free(&_avframe);
av_packet_free(&_avpacket);
delete this; delete this;
} }
return ret; return ret;
@ -78,6 +104,10 @@ public:
struct decklink_ctx *_ctx; struct decklink_ctx *_ctx;
AVFrame *_avframe; AVFrame *_avframe;
AVPacket *_avpacket;
AVCodecID _codec_id;
int _height;
int _width;
private: private:
std::atomic<int> _refs; std::atomic<int> _refs;
@ -90,9 +120,11 @@ public:
{ {
decklink_frame *frame = static_cast<decklink_frame *>(_frame); decklink_frame *frame = static_cast<decklink_frame *>(_frame);
struct decklink_ctx *ctx = frame->_ctx; struct decklink_ctx *ctx = frame->_ctx;
AVFrame *avframe = frame->_avframe;
av_frame_unref(avframe); if (frame->_avframe)
av_frame_unref(frame->_avframe);
if (frame->_avpacket)
av_packet_unref(frame->_avpacket);
pthread_mutex_lock(&ctx->mutex); pthread_mutex_lock(&ctx->mutex);
ctx->frames_buffer_available_spots++; ctx->frames_buffer_available_spots++;
@ -118,11 +150,18 @@ static int decklink_setup_video(AVFormatContext *avctx, AVStream *st)
return -1; return -1;
} }
if (c->format != AV_PIX_FMT_UYVY422) { if (c->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!" if (c->format != AV_PIX_FMT_UYVY422) {
" Only AV_PIX_FMT_UYVY422 is supported.\n"); av_log(avctx, AV_LOG_ERROR, "Unsupported pixel format!"
" Only AV_PIX_FMT_UYVY422 is supported.\n");
return -1;
}
} else if (c->codec_id != AV_CODEC_ID_V210) {
av_log(avctx, AV_LOG_ERROR, "Unsupported codec type!"
" Only V210 and wrapped frame with AV_PIX_FMT_UYVY422 are supported.\n");
return -1; return -1;
} }
if (ff_decklink_set_format(avctx, c->width, c->height, if (ff_decklink_set_format(avctx, c->width, c->height,
st->time_base.num, st->time_base.den, c->field_order)) { st->time_base.num, st->time_base.den, c->field_order)) {
av_log(avctx, AV_LOG_ERROR, "Unsupported video size, framerate or field order!" av_log(avctx, AV_LOG_ERROR, "Unsupported video size, framerate or field order!"
@ -230,27 +269,42 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt)
{ {
struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
AVFrame *avframe, *tmp = (AVFrame *)pkt->data; AVStream *st = avctx->streams[pkt->stream_index];
AVFrame *avframe = NULL, *tmp = (AVFrame *)pkt->data;
AVPacket *avpacket = NULL;
decklink_frame *frame; decklink_frame *frame;
buffercount_type buffered; buffercount_type buffered;
HRESULT hr; HRESULT hr;
if (tmp->format != AV_PIX_FMT_UYVY422 || if (st->codecpar->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) {
tmp->width != ctx->bmd_width || if (tmp->format != AV_PIX_FMT_UYVY422 ||
tmp->height != ctx->bmd_height) { tmp->width != ctx->bmd_width ||
av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format or dimension.\n"); tmp->height != ctx->bmd_height) {
return AVERROR(EINVAL); av_log(avctx, AV_LOG_ERROR, "Got a frame with invalid pixel format or dimension.\n");
} return AVERROR(EINVAL);
avframe = av_frame_clone(tmp); }
if (!avframe) {
av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n"); avframe = av_frame_clone(tmp);
return AVERROR(EIO); if (!avframe) {
av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n");
return AVERROR(EIO);
}
frame = new decklink_frame(ctx, avframe, st->codecpar->codec_id, avframe->height, avframe->width);
} else {
avpacket = av_packet_clone(pkt);
if (!avpacket) {
av_log(avctx, AV_LOG_ERROR, "Could not clone video frame.\n");
return AVERROR(EIO);
}
frame = new decklink_frame(ctx, avpacket, st->codecpar->codec_id, ctx->bmd_height, ctx->bmd_width);
} }
frame = new decklink_frame(ctx, avframe);
if (!frame) { if (!frame) {
av_log(avctx, AV_LOG_ERROR, "Could not create new frame.\n"); av_log(avctx, AV_LOG_ERROR, "Could not create new frame.\n");
av_frame_free(&avframe); av_frame_free(&avframe);
av_packet_free(&avpacket);
return AVERROR(EIO); return AVERROR(EIO);
} }