early-access version 1680
This commit is contained in:
1
externals/ffmpeg/libavdevice/Makefile
vendored
1
externals/ffmpeg/libavdevice/Makefile
vendored
@@ -15,6 +15,7 @@ OBJS-$(CONFIG_SHARED) += reverse.o
|
||||
OBJS-$(CONFIG_ALSA_INDEV) += alsa_dec.o alsa.o timefilter.o
|
||||
OBJS-$(CONFIG_ALSA_OUTDEV) += alsa_enc.o alsa.o
|
||||
OBJS-$(CONFIG_ANDROID_CAMERA_INDEV) += android_camera.o
|
||||
OBJS-$(CONFIG_AUDIOTOOLBOX_OUTDEV) += audiotoolbox.o
|
||||
OBJS-$(CONFIG_AVFOUNDATION_INDEV) += avfoundation.o
|
||||
OBJS-$(CONFIG_BKTR_INDEV) += bktr.o
|
||||
OBJS-$(CONFIG_CACA_OUTDEV) += caca.o
|
||||
|
1
externals/ffmpeg/libavdevice/alldevices.c
vendored
1
externals/ffmpeg/libavdevice/alldevices.c
vendored
@@ -27,6 +27,7 @@
|
||||
extern AVInputFormat ff_alsa_demuxer;
|
||||
extern AVOutputFormat ff_alsa_muxer;
|
||||
extern AVInputFormat ff_android_camera_demuxer;
|
||||
extern AVOutputFormat ff_audiotoolbox_muxer;
|
||||
extern AVInputFormat ff_avfoundation_demuxer;
|
||||
extern AVInputFormat ff_bktr_demuxer;
|
||||
extern AVOutputFormat ff_caca_muxer;
|
||||
|
308
externals/ffmpeg/libavdevice/audiotoolbox.m
vendored
Executable file
308
externals/ffmpeg/libavdevice/audiotoolbox.m
vendored
Executable file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* AudioToolbox output device
|
||||
* Copyright (c) 2020 Thilo Borgmann <thilo.borgmann@mail.de>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* AudioToolbox output device
|
||||
* @author Thilo Borgmann <thilo.borgmann@mail.de>
|
||||
*/
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "libavutil/opt.h"
|
||||
#include "libavformat/internal.h"
|
||||
#include "libavutil/internal.h"
|
||||
#include "avdevice.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AVClass *class;
|
||||
|
||||
AudioQueueBufferRef buffer[2];
|
||||
pthread_mutex_t buffer_lock[2];
|
||||
int cur_buf;
|
||||
AudioQueueRef queue;
|
||||
|
||||
int list_devices;
|
||||
int audio_device_index;
|
||||
|
||||
} ATContext;
|
||||
|
||||
static int check_status(AVFormatContext *avctx, OSStatus *status, const char *msg)
|
||||
{
|
||||
if (*status != noErr) {
|
||||
av_log(avctx, AV_LOG_ERROR, "Error: %s (%i)\n", msg, *status);
|
||||
return 1;
|
||||
} else {
|
||||
av_log(avctx, AV_LOG_DEBUG, " OK : %s\n", msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_callback(void* atctx, AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
// unlock the buffer that has just been consumed
|
||||
ATContext *ctx = (ATContext*)atctx;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (inBuffer == ctx->buffer[i]) {
|
||||
pthread_mutex_unlock(&ctx->buffer_lock[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static av_cold int at_write_header(AVFormatContext *avctx)
|
||||
{
|
||||
ATContext *ctx = (ATContext*)avctx->priv_data;
|
||||
OSStatus err = noErr;
|
||||
CFStringRef device_UID = NULL;
|
||||
AudioDeviceID *devices;
|
||||
int num_devices;
|
||||
|
||||
|
||||
// get devices
|
||||
UInt32 data_size = 0;
|
||||
AudioObjectPropertyAddress prop;
|
||||
prop.mSelector = kAudioHardwarePropertyDevices;
|
||||
prop.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
prop.mElement = kAudioObjectPropertyElementMaster;
|
||||
err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size);
|
||||
if (check_status(avctx, &err, "AudioObjectGetPropertyDataSize devices"))
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
num_devices = data_size / sizeof(AudioDeviceID);
|
||||
|
||||
devices = (AudioDeviceID*)(av_malloc(data_size));
|
||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &data_size, devices);
|
||||
if (check_status(avctx, &err, "AudioObjectGetPropertyData devices")) {
|
||||
av_freep(&devices);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
// list devices
|
||||
if (ctx->list_devices) {
|
||||
CFStringRef device_name = NULL;
|
||||
prop.mScope = kAudioDevicePropertyScopeInput;
|
||||
|
||||
av_log(ctx, AV_LOG_INFO, "CoreAudio devices:\n");
|
||||
for(UInt32 i = 0; i < num_devices; ++i) {
|
||||
// UID
|
||||
data_size = sizeof(device_UID);
|
||||
prop.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_UID);
|
||||
if (check_status(avctx, &err, "AudioObjectGetPropertyData UID"))
|
||||
continue;
|
||||
|
||||
// name
|
||||
data_size = sizeof(device_name);
|
||||
prop.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||
err = AudioObjectGetPropertyData(devices[i], &prop, 0, NULL, &data_size, &device_name);
|
||||
if (check_status(avctx, &err, "AudioObjecTGetPropertyData name"))
|
||||
continue;
|
||||
|
||||
av_log(ctx, AV_LOG_INFO, "[%d] %30s, %s\n", i,
|
||||
CFStringGetCStringPtr(device_name, kCFStringEncodingMacRoman),
|
||||
CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
|
||||
}
|
||||
}
|
||||
|
||||
// get user-defined device UID or use default device
|
||||
// -audio_device_index overrides any URL given
|
||||
const char *stream_name = avctx->url;
|
||||
if (stream_name && ctx->audio_device_index == -1) {
|
||||
sscanf(stream_name, "%d", &ctx->audio_device_index);
|
||||
}
|
||||
|
||||
if (ctx->audio_device_index >= 0) {
|
||||
// get UID of selected device
|
||||
data_size = sizeof(device_UID);
|
||||
prop.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
err = AudioObjectGetPropertyData(devices[ctx->audio_device_index], &prop, 0, NULL, &data_size, &device_UID);
|
||||
if (check_status(avctx, &err, "AudioObjecTGetPropertyData UID")) {
|
||||
av_freep(&devices);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
} else {
|
||||
// use default device
|
||||
device_UID = NULL;
|
||||
}
|
||||
|
||||
av_log(ctx, AV_LOG_DEBUG, "stream_name: %s\n", stream_name);
|
||||
av_log(ctx, AV_LOG_DEBUG, "audio_device_idnex: %i\n", ctx->audio_device_index);
|
||||
av_log(ctx, AV_LOG_DEBUG, "UID: %s\n", CFStringGetCStringPtr(device_UID, kCFStringEncodingMacRoman));
|
||||
|
||||
// check input stream
|
||||
if (avctx->nb_streams != 1 || avctx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Only a single audio stream is supported.\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
av_freep(&devices);
|
||||
AVCodecParameters *codecpar = avctx->streams[0]->codecpar;
|
||||
|
||||
// audio format
|
||||
AudioStreamBasicDescription device_format = {0};
|
||||
device_format.mSampleRate = codecpar->sample_rate;
|
||||
device_format.mFormatID = kAudioFormatLinearPCM;
|
||||
device_format.mFormatFlags |= (codecpar->format == AV_SAMPLE_FMT_FLT) ? kLinearPCMFormatFlagIsFloat : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? kLinearPCMFormatFlagIsSignedInteger : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S32BE, AV_CODEC_ID_PCM_S32LE)) ? kLinearPCMFormatFlagIsSignedInteger : 0;
|
||||
device_format.mFormatFlags |= (av_sample_fmt_is_planar(codecpar->format)) ? kAudioFormatFlagIsNonInterleaved : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? kAudioFormatFlagIsBigEndian : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? kAudioFormatFlagIsBigEndian : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? kAudioFormatFlagIsBigEndian : 0;
|
||||
device_format.mFormatFlags |= (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? kAudioFormatFlagIsBigEndian : 0;
|
||||
device_format.mChannelsPerFrame = codecpar->channels;
|
||||
device_format.mBitsPerChannel = (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? 24 : (av_get_bytes_per_sample(codecpar->format) << 3);
|
||||
device_format.mBytesPerFrame = (device_format.mBitsPerChannel >> 3) * device_format.mChannelsPerFrame;
|
||||
device_format.mFramesPerPacket = 1;
|
||||
device_format.mBytesPerPacket = device_format.mBytesPerFrame * device_format.mFramesPerPacket;
|
||||
device_format.mReserved = 0;
|
||||
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mSampleRate = %i\n", codecpar->sample_rate);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatID = %s\n", "kAudioFormatLinearPCM");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->format == AV_SAMPLE_FMT_FLT) ? "kLinearPCMFormatFlagIsFloat" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S8) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S32BE, AV_CODEC_ID_PCM_S32LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_NE(AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S24LE)) ? "kLinearPCMFormatFlagIsSignedInteger" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (av_sample_fmt_is_planar(codecpar->format)) ? "kAudioFormatFlagIsNonInterleaved" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_F32BE) ? "kAudioFormatFlagIsBigEndian" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S16BE) ? "kAudioFormatFlagIsBigEndian" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S24BE) ? "kAudioFormatFlagIsBigEndian" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags |= %s\n", (codecpar->codec_id == AV_CODEC_ID_PCM_S32BE) ? "kAudioFormatFlagIsBigEndian" : "0");
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFormatFlags == %i\n", device_format.mFormatFlags);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mChannelsPerFrame = %i\n", codecpar->channels);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mBitsPerChannel = %i\n", av_get_bytes_per_sample(codecpar->format) << 3);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerFrame = %i\n", (device_format.mBitsPerChannel >> 3) * codecpar->channels);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mBytesPerPacket = %i\n", device_format.mBytesPerFrame);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mFramesPerPacket = %i\n", 1);
|
||||
av_log(ctx, AV_LOG_DEBUG, "device_format.mReserved = %i\n", 0);
|
||||
|
||||
// create new output queue for the device
|
||||
err = AudioQueueNewOutput(&device_format, queue_callback, ctx,
|
||||
NULL, kCFRunLoopCommonModes,
|
||||
0, &ctx->queue);
|
||||
if (check_status(avctx, &err, "AudioQueueNewOutput")) {
|
||||
if (err == kAudioFormatUnsupportedDataFormatError)
|
||||
av_log(ctx, AV_LOG_ERROR, "Unsupported output format.\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
// set user-defined device or leave untouched for default
|
||||
if (device_UID != NULL) {
|
||||
err = AudioQueueSetProperty(ctx->queue, kAudioQueueProperty_CurrentDevice, &device_UID, sizeof(device_UID));
|
||||
if (check_status(avctx, &err, "AudioQueueSetProperty output UID"))
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
// start the queue
|
||||
err = AudioQueueStart(ctx->queue, NULL);
|
||||
if (check_status(avctx, &err, "AudioQueueStart"))
|
||||
return AVERROR(EINVAL);
|
||||
|
||||
// init the mutexes for double-buffering
|
||||
pthread_mutex_init(&ctx->buffer_lock[0], NULL);
|
||||
pthread_mutex_init(&ctx->buffer_lock[1], NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at_write_packet(AVFormatContext *avctx, AVPacket *pkt)
|
||||
{
|
||||
ATContext *ctx = (ATContext*)avctx->priv_data;
|
||||
OSStatus err = noErr;
|
||||
|
||||
// use the other buffer
|
||||
ctx->cur_buf = !ctx->cur_buf;
|
||||
|
||||
// lock for writing or wait for the buffer to be available
|
||||
// will be unlocked by queue callback
|
||||
pthread_mutex_lock(&ctx->buffer_lock[ctx->cur_buf]);
|
||||
|
||||
// (re-)allocate the buffer if not existant or of different size
|
||||
if (!ctx->buffer[ctx->cur_buf] || ctx->buffer[ctx->cur_buf]->mAudioDataBytesCapacity != pkt->size) {
|
||||
err = AudioQueueAllocateBuffer(ctx->queue, pkt->size, &ctx->buffer[ctx->cur_buf]);
|
||||
if (check_status(avctx, &err, "AudioQueueAllocateBuffer")) {
|
||||
pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]);
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
AudioQueueBufferRef buf = ctx->buffer[ctx->cur_buf];
|
||||
|
||||
// copy audio data into buffer and enqueue the buffer
|
||||
memcpy(buf->mAudioData, pkt->data, buf->mAudioDataBytesCapacity);
|
||||
buf->mAudioDataByteSize = buf->mAudioDataBytesCapacity;
|
||||
err = AudioQueueEnqueueBuffer(ctx->queue, buf, 0, NULL);
|
||||
if (check_status(avctx, &err, "AudioQueueEnqueueBuffer")) {
|
||||
pthread_mutex_unlock(&ctx->buffer_lock[ctx->cur_buf]);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static av_cold int at_write_trailer(AVFormatContext *avctx)
|
||||
{
|
||||
ATContext *ctx = (ATContext*)avctx->priv_data;
|
||||
OSStatus err = noErr;
|
||||
|
||||
pthread_mutex_destroy(&ctx->buffer_lock[0]);
|
||||
pthread_mutex_destroy(&ctx->buffer_lock[1]);
|
||||
|
||||
err = AudioQueueFlush(ctx->queue);
|
||||
check_status(avctx, &err, "AudioQueueFlush");
|
||||
err = AudioQueueDispose(ctx->queue, true);
|
||||
check_status(avctx, &err, "AudioQueueDispose");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const AVOption options[] = {
|
||||
{ "list_devices", "list available audio devices", offsetof(ATContext, list_devices), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
|
||||
{ "audio_device_index", "select audio device by index (starts at 0)", offsetof(ATContext, audio_device_index), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const AVClass at_class = {
|
||||
.class_name = "AudioToolbox",
|
||||
.item_name = av_default_item_name,
|
||||
.option = options,
|
||||
.version = LIBAVUTIL_VERSION_INT,
|
||||
.category = AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
|
||||
};
|
||||
|
||||
AVOutputFormat ff_audiotoolbox_muxer = {
|
||||
.name = "audiotoolbox",
|
||||
.long_name = NULL_IF_CONFIG_SMALL("AudioToolbox output device"),
|
||||
.priv_data_size = sizeof(ATContext),
|
||||
.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE),
|
||||
.video_codec = AV_CODEC_ID_NONE,
|
||||
.write_header = at_write_header,
|
||||
.write_packet = at_write_packet,
|
||||
.write_trailer = at_write_trailer,
|
||||
.flags = AVFMT_NOFILE,
|
||||
.priv_class = &at_class,
|
||||
};
|
@@ -120,12 +120,14 @@ struct decklink_ctx {
|
||||
unsigned int dropped;
|
||||
AVStream *audio_st;
|
||||
AVStream *video_st;
|
||||
AVStream *klv_st;
|
||||
AVStream *teletext_st;
|
||||
uint16_t cdp_sequence_num;
|
||||
|
||||
/* Options */
|
||||
int list_devices;
|
||||
int list_formats;
|
||||
int enable_klv;
|
||||
int64_t teletext_lines;
|
||||
double preroll;
|
||||
int duplex_mode;
|
||||
|
@@ -40,6 +40,7 @@ struct decklink_cctx {
|
||||
/* Options */
|
||||
int list_devices;
|
||||
int list_formats;
|
||||
int enable_klv;
|
||||
int64_t teletext_lines;
|
||||
double preroll;
|
||||
int audio_channels;
|
||||
|
123
externals/ffmpeg/libavdevice/decklink_dec.cpp
vendored
123
externals/ffmpeg/libavdevice/decklink_dec.cpp
vendored
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
using std::atomic;
|
||||
|
||||
/* Include internal.h first to avoid conflict between winsock.h (used by
|
||||
@@ -583,6 +584,109 @@ static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideoInputFrame *videoFrame, int64_t pts)
|
||||
{
|
||||
const uint8_t KLV_DID = 0x44;
|
||||
const uint8_t KLV_IN_VANC_SDID = 0x04;
|
||||
|
||||
struct KLVPacket
|
||||
{
|
||||
uint16_t sequence_counter;
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
size_t total_size = 0;
|
||||
std::vector<std::vector<KLVPacket>> klv_packets(256);
|
||||
|
||||
IDeckLinkVideoFrameAncillaryPackets *packets = nullptr;
|
||||
if (videoFrame->QueryInterface(IID_IDeckLinkVideoFrameAncillaryPackets, (void**)&packets) != S_OK)
|
||||
return;
|
||||
|
||||
IDeckLinkAncillaryPacketIterator *it = nullptr;
|
||||
if (packets->GetPacketIterator(&it) != S_OK) {
|
||||
packets->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
IDeckLinkAncillaryPacket *packet = nullptr;
|
||||
while (it->Next(&packet) == S_OK) {
|
||||
uint8_t *data = nullptr;
|
||||
uint32_t size = 0;
|
||||
|
||||
if (packet->GetDID() == KLV_DID && packet->GetSDID() == KLV_IN_VANC_SDID) {
|
||||
av_log(avctx, AV_LOG_DEBUG, "Found KLV VANC packet on line: %d\n", packet->GetLineNumber());
|
||||
|
||||
if (packet->GetBytes(bmdAncillaryPacketFormatUInt8, (const void**) &data, &size) == S_OK) {
|
||||
// MID and PSC
|
||||
if (size > 3) {
|
||||
uint8_t mid = data[0];
|
||||
uint16_t psc = data[1] << 8 | data[2];
|
||||
|
||||
av_log(avctx, AV_LOG_DEBUG, "KLV with MID: %d and PSC: %d\n", mid, psc);
|
||||
|
||||
auto& list = klv_packets[mid];
|
||||
uint16_t expected_psc = list.size() + 1;
|
||||
|
||||
if (psc == expected_psc) {
|
||||
uint32_t data_len = size - 3;
|
||||
total_size += data_len;
|
||||
|
||||
KLVPacket packet{ psc };
|
||||
packet.data.resize(data_len);
|
||||
memcpy(packet.data.data(), data + 3, data_len);
|
||||
|
||||
list.push_back(std::move(packet));
|
||||
} else {
|
||||
av_log(avctx, AV_LOG_WARNING, "Out of order PSC: %d for MID: %d\n", psc, mid);
|
||||
|
||||
if (!list.empty()) {
|
||||
for (auto& klv : list)
|
||||
total_size -= klv.data.size();
|
||||
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet->Release();
|
||||
}
|
||||
|
||||
it->Release();
|
||||
packets->Release();
|
||||
|
||||
if (total_size > 0) {
|
||||
std::vector<uint8_t> klv;
|
||||
klv.reserve(total_size);
|
||||
|
||||
for (size_t i = 0; i < klv_packets.size(); ++i) {
|
||||
auto& list = klv_packets[i];
|
||||
|
||||
if (list.empty())
|
||||
continue;
|
||||
|
||||
av_log(avctx, AV_LOG_DEBUG, "Joining MID: %d\n", (int)i);
|
||||
|
||||
for (auto& packet : list)
|
||||
klv.insert(klv.end(), packet.data.begin(), packet.data.end());
|
||||
}
|
||||
|
||||
AVPacket klv_packet;
|
||||
av_init_packet(&klv_packet);
|
||||
klv_packet.pts = pts;
|
||||
klv_packet.dts = pts;
|
||||
klv_packet.flags |= AV_PKT_FLAG_KEY;
|
||||
klv_packet.stream_index = ctx->klv_st->index;
|
||||
klv_packet.data = klv.data();
|
||||
klv_packet.size = klv.size();
|
||||
|
||||
if (avpacket_queue_put(&ctx->queue, &klv_packet) < 0) {
|
||||
++ctx->dropped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class decklink_input_callback : public IDeckLinkInputCallback
|
||||
{
|
||||
public:
|
||||
@@ -821,6 +925,10 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
|
||||
uint8_t txt_buf0[3531]; // 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + 1920 bytes OP47 decode buffer
|
||||
uint8_t *txt_buf = txt_buf0;
|
||||
|
||||
if (ctx->enable_klv) {
|
||||
handle_klv(avctx, ctx, videoFrame, pkt.pts);
|
||||
}
|
||||
|
||||
if (videoFrame->GetAncillaryData(&vanc) == S_OK) {
|
||||
int i;
|
||||
int64_t line_mask = 1;
|
||||
@@ -1012,6 +1120,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
|
||||
return AVERROR(ENOMEM);
|
||||
ctx->list_devices = cctx->list_devices;
|
||||
ctx->list_formats = cctx->list_formats;
|
||||
ctx->enable_klv = cctx->enable_klv;
|
||||
ctx->teletext_lines = cctx->teletext_lines;
|
||||
ctx->preroll = cctx->preroll;
|
||||
ctx->duplex_mode = cctx->duplex_mode;
|
||||
@@ -1202,6 +1311,20 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
|
||||
|
||||
ctx->video_st=st;
|
||||
|
||||
if (ctx->enable_klv) {
|
||||
st = avformat_new_stream(avctx, NULL);
|
||||
if (!st) {
|
||||
ret = AVERROR(ENOMEM);
|
||||
goto error;
|
||||
}
|
||||
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
|
||||
st->time_base.den = ctx->bmd_tb_den;
|
||||
st->time_base.num = ctx->bmd_tb_num;
|
||||
st->codecpar->codec_id = AV_CODEC_ID_SMPTE_KLV;
|
||||
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
|
||||
ctx->klv_st = st;
|
||||
}
|
||||
|
||||
if (ctx->teletext_lines) {
|
||||
st = avformat_new_stream(avctx, NULL);
|
||||
if (!st) {
|
||||
|
@@ -39,6 +39,7 @@ static const AVOption options[] = {
|
||||
{ "argb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, DEC, "raw_format"},
|
||||
{ "bgra", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('B','G','R','A') }, 0, 0, DEC, "raw_format"},
|
||||
{ "rgb10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MKBETAG('r','2','1','0') }, 0, 0, DEC, "raw_format"},
|
||||
{ "enable_klv", "output klv if present in vanc", OFFSET(enable_klv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC },
|
||||
{ "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, "teletext_lines"},
|
||||
{ "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"},
|
||||
|
4
externals/ffmpeg/libavdevice/version.h
vendored
4
externals/ffmpeg/libavdevice/version.h
vendored
@@ -28,8 +28,8 @@
|
||||
#include "libavutil/version.h"
|
||||
|
||||
#define LIBAVDEVICE_VERSION_MAJOR 58
|
||||
#define LIBAVDEVICE_VERSION_MINOR 10
|
||||
#define LIBAVDEVICE_VERSION_MICRO 100
|
||||
#define LIBAVDEVICE_VERSION_MINOR 11
|
||||
#define LIBAVDEVICE_VERSION_MICRO 101
|
||||
|
||||
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
|
||||
LIBAVDEVICE_VERSION_MINOR, \
|
||||
|
Reference in New Issue
Block a user