+#define DECKLINK_MAX_AUDIO_CHANNELS 32
+
class decklink_output_callback;
class decklink_input_callback;
@@ -71,6 +75,7 @@ struct decklink_ctx {
int bmd_height;
int bmd_field_dominance;
int supports_vanc;
+ int max_audio_channels;
/* Capture buffer queue */
AVPacketQueue queue;
@@ -85,7 +90,8 @@ struct decklink_ctx {
int64_t last_pts;
unsigned long frameCount;
unsigned int dropped;
- AVStream *audio_st;
+ AVStream *audio_st[DECKLINK_MAX_AUDIO_CHANNELS];
+ int num_audio_streams;
AVStream *video_st;
AVStream *teletext_st;
uint16_t cdp_sequence_num;
diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h
index 368ac25..3a21bae 100644
--- a/libavdevice/decklink_common_c.h
+++ b/libavdevice/decklink_common_c.h
@@ -30,6 +30,11 @@ typedef enum DecklinkPtsSource {
PTS_SRC_WALLCLOCK = 4,
} DecklinkPtsSource;
+typedef enum DecklinkAudioMode {
+ AUDIO_MODE_BUNDLED = 0,
+ AUDIO_MODE_PAIRS = 1,
+} DecklinkAudioMode;
+
struct decklink_cctx {
const AVClass *cclass;
@@ -42,6 +47,7 @@ struct decklink_cctx {
double preroll;
int v210;
int audio_channels;
+ int audio_mode;
int audio_depth;
int duplex_mode;
DecklinkPtsSource audio_pts_source;
diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp
index 94dae26..6c1ff82 100644
--- a/libavdevice/decklink_dec.cpp
+++ b/libavdevice/decklink_dec.cpp
@@ -627,9 +627,56 @@ static int64_t get_pkt_pts(IDeckLinkVideoInputFrame
*videoFrame,
return pts;
}
+static int setup_audio(AVFormatContext *avctx)
+{
+ struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
+ struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx;
+ AVStream *st;
+ int ret = 0;
+
+ if (cctx->audio_mode == AUDIO_MODE_BUNDLED) {
+ st = avformat_new_stream(avctx, NULL);
+ if (!st) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
+ ret = AVERROR(ENOMEM);
+ goto error;
+ }
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = ctx->audio_depth == 32 ?
AV_CODEC_ID_PCM_S32LE : AV_CODEC_ID_PCM_S16LE;
+ st->codecpar->sample_rate = bmdAudioSampleRate48kHz;
+ st->codecpar->channels = cctx->audio_channels;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
+ ctx->audio_st[0] = st;
+ ctx->num_audio_streams++;
+ } else {
+ for (int i = 0; i < ctx->max_audio_channels / 2; i++) {
+ st = avformat_new_stream(avctx, NULL);
+ if (!st) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot add stream %d\n", i);
+ ret = AVERROR(ENOMEM);
+ goto error;
+ }
+ st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = ctx->audio_depth == 32 ?
AV_CODEC_ID_PCM_S32LE : AV_CODEC_ID_PCM_S16LE;
+ st->codecpar->sample_rate = bmdAudioSampleRate48kHz;
+ st->codecpar->channels = 2;
+ st->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+ avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
+ ctx->audio_st[i] = st;
+ ctx->num_audio_streams++;
+ }
+ cctx->audio_channels = ctx->max_audio_channels;
+ }
+
+error:
+ return ret;
+}
+
HRESULT decklink_input_callback::VideoInputFrameArrived(
IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioFrame)
{
+ decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data;
void *frameBytes;
void *audioFrameBytes;
BMDTimeValue frameTime;
@@ -777,24 +824,57 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
// Handle Audio Frame
if (audioFrame) {
- AVPacket pkt;
- BMDTimeValue audio_pts;
- av_init_packet(&pkt);
-
- //hack among hacks
- 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.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;
+ if (cctx->audio_mode == AUDIO_MODE_BUNDLED) {
+ AVPacket pkt;
+ BMDTimeValue audio_pts;
+ av_init_packet(&pkt);
- if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
- ++ctx->dropped;
+ //hack among hacks
+ pkt.size = audioFrame->GetSampleFrameCount() *
ctx->audio_st[0]->codecpar->channels * (ctx->audio_depth / 8);
+ audioFrame->GetBytes(&audioFrameBytes);
+ audioFrame->GetPacketTime(&audio_pts,
ctx->audio_st[0]->time_base.den);
+ pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock,
ctx->audio_pts_source, ctx->audio_st[0]->time_base, &initial_audio_pts);
+ pkt.dts = pkt.pts;
+
+ pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt.stream_index = ctx->audio_st[0]->index;
+ pkt.data = (uint8_t *)audioFrameBytes;
+
+ if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
+ ++ctx->dropped;
+ }
+ } else {
+ /* Need to deinterleave audio */
+ int audio_offset = 0;
+ int audio_stride = cctx->audio_channels * ctx->audio_depth / 8;
+ for (int i = 0; i < ctx->num_audio_streams; i++) {
+ int sample_size = ctx->audio_st[i]->codecpar->channels *
+
ctx->audio_st[i]->codecpar->bits_per_coded_sample / 8;
+ AVPacket pkt;
+ int ret = av_new_packet(&pkt,
audioFrame->GetSampleFrameCount() * sample_size);
+ if (ret != 0)
+ continue;
+
+ pkt.pts = get_pkt_pts(videoFrame, audioFrame, wallclock,
ctx->audio_pts_source,
+ ctx->audio_st[i]->time_base,
&initial_audio_pts);
+ pkt.dts = pkt.pts;
+ pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt.stream_index = ctx->audio_st[i]->index;
+
+ uint8_t *audio_in = ((uint8_t *) audioFrameBytes) +
audio_offset;
+ for (int x = 0; x < pkt.size; x += sample_size) {
+ memcpy(&pkt.data[x], audio_in, sample_size);
+ audio_in += audio_stride;
+ }
+
+ if (avpacket_queue_put(&ctx->queue, &pkt) < 0)
+ ++ctx->dropped;
+
+ av_packet_unref(&pkt);
+ audio_offset += sample_size;
+ }
}
}
@@ -999,18 +1079,7 @@ av_cold int ff_decklink_read_header(AVFormatContext
*avctx)
#endif
/* Setup streams. */
- st = avformat_new_stream(avctx, NULL);
- if (!st) {
- av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
- ret = AVERROR(ENOMEM);
- goto error;
- }
- st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
- st->codecpar->codec_id = cctx->audio_depth == 32 ?
AV_CODEC_ID_PCM_S32LE : AV_CODEC_ID_PCM_S16LE;
- st->codecpar->sample_rate = bmdAudioSampleRate48kHz;
- st->codecpar->channels = cctx->audio_channels;
- avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
- ctx->audio_st=st;
+ setup_audio(avctx);
st = avformat_new_stream(avctx, NULL);
if (!st) {
@@ -1096,8 +1165,17 @@ av_cold int ff_decklink_read_header(AVFormatContext
*avctx)
ctx->teletext_st = st;
}
- av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n",
ctx->audio_st->codecpar->channels);
- result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, cctx->audio_depth ==
32 ? bmdAudioSampleType32bitInteger : bmdAudioSampleType16bitInteger,
ctx->audio_st->codecpar->channels);
+ if (cctx->audio_mode == AUDIO_MODE_BUNDLED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n",
ctx->audio_st[0]->codecpar->channels);
+ result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz,
+ ctx->audio_depth == 32 ?
bmdAudioSampleType32bitInteger : bmdAudioSampleType16bitInteger,
+
ctx->audio_st[0]->codecpar->channels);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using %d input audio channels\n",
ctx->max_audio_channels);
+ result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz,
+ ctx->audio_depth == 32 ?
bmdAudioSampleType32bitInteger : bmdAudioSampleType16bitInteger,
+ ctx->max_audio_channels);
+ }
if (result != S_OK) {
av_log(avctx, AV_LOG_ERROR, "Cannot enable audio input\n");
diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c
index 1c6d826..fe8e9fd 100644
--- a/libavdevice/decklink_dec_c.c
+++ b/libavdevice/decklink_dec_c.c
@@ -44,6 +44,9 @@ static const AVOption options[] = {
{ "standard", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"},
{ "all", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"},
{ "channels", "number of audio channels", OFFSET(audio_channels),
AV_OPT_TYPE_INT , { .i64 = 2 }, 2, 16, DEC },
+ { "audio_mode", "audio mode", OFFSET(audio_mode),
AV_OPT_TYPE_INT, { .i64 = AUDIO_MODE_BUNDLED}, 0, 1, DEC, "audio_mode"},
+ { "bundled", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = AUDIO_MODE_BUNDLED}, 0, 0, DEC, "audio_mode"},
+ { "pairs", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = AUDIO_MODE_PAIRS}, 0, 0, DEC, "audio_mode"},
{ "duplex_mode", "duplex mode", OFFSET(duplex_mode),
AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, DEC, "duplex_mode"},
{ "unset", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "duplex_mode"},
{ "half", NULL, 0,
AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "duplex_mode"},