Implement hopefully working dynamic framebuffer assignments

We try to allocate as many frames as were configured by looking for a
big enough range of unused contiguous frames while taking the configured
modes into account.
This commit is contained in:
Sebastian Dröge 2021-11-10 17:22:30 +02:00
parent bfdf93ecd1
commit 484cb51445
4 changed files with 164 additions and 15 deletions

View file

@ -684,6 +684,134 @@ ShmMutexLocker::~ShmMutexLocker() {
if (s != SEM_FAILED) sem_post(s);
}
static guint gst_aja_device_get_frame_multiplier(GstAjaNtv2Device *device,
NTV2Channel channel) {
// quad formats use 4x as many frames, quad-quad formats 8x
bool quad_enabled = false;
device->device->GetQuadFrameEnable(quad_enabled, channel);
bool quad_quad_enabled = false;
device->device->GetQuadQuadFrameEnable(quad_quad_enabled, channel);
NTV2VideoFormat format = NTV2_FORMAT_UNKNOWN;
device->device->GetVideoFormat(format, channel);
GST_TRACE("Channel %d uses mode %d (quad: %d, quad quad: %d)", (gint)channel,
(gint)format, quad_enabled, quad_quad_enabled);
// Similarly, 2k/UHD use 4x as many frames and 4k/UHD2 use 8x as many
// frames
if (format != NTV2_FORMAT_UNKNOWN) {
guint width = ::GetDisplayWidth(format);
guint height = ::GetDisplayHeight(format);
if (height <= 1080 && width <= 1920) {
// SD and HD but not 2k!
} else if (height <= 2160 && width <= 3840) {
// 2k and UHD but not 4k
quad_enabled = true;
} else if (height <= 4320 && width <= 7680) {
// 4k and UHD2 but not 8k
quad_quad_enabled = true;
} else {
// 8k FIXME
quad_quad_enabled = true;
}
}
if (quad_enabled) {
g_assert(!quad_quad_enabled);
return 4;
} else if (quad_quad_enabled) {
g_assert(!quad_enabled);
return 8;
}
return 1;
}
// Returns -1 on failure or otherwise the start_frame.
// end_frame would be start_frame + frame_count - 1
gint gst_aja_ntv2_device_find_unallocated_frames(GstAjaNtv2Device *device,
NTV2Channel channel,
guint frame_count) {
g_assert(frame_count != 0);
g_assert(device != NULL);
g_assert(device->device->IsOpen());
// Adapted from CNTV2Card::FindUnallocatedFrames() with
// quad/quad-quad/UHD/UHD2 support
std::set<guint16> used_frames;
for (NTV2Channel c = ::NTV2_CHANNEL1; c < NTV2_MAX_NUM_CHANNELS;
c = (NTV2Channel)(c + 1)) {
AUTOCIRCULATE_STATUS ac_status;
if (device->device->AutoCirculateGetStatus(c, ac_status) &&
!ac_status.IsStopped()) {
guint16 start_frame = ac_status.GetStartFrame();
guint16 end_frame = ac_status.GetEndFrame();
guint multiplier = gst_aja_device_get_frame_multiplier(device, c);
GST_TRACE("Channel %d uses frames %u-%u (multiplier: %u)", c, start_frame,
end_frame, multiplier);
start_frame *= multiplier;
end_frame *= multiplier;
end_frame += (multiplier - 1);
GST_TRACE("Channel %d uses HD frames %u-%u", c, start_frame, end_frame);
for (guint16 i = start_frame; i <= end_frame; i++) {
used_frames.insert(i);
}
}
}
guint multiplier = gst_aja_device_get_frame_multiplier(device, channel);
frame_count *= multiplier;
const guint16 last_frame =
::NTV2DeviceGetNumberFrameBuffers(device->device->GetDeviceID()) - 1;
guint16 start_frame = 0;
guint16 end_frame = start_frame + frame_count - 1;
auto iter = used_frames.cbegin();
while (iter != used_frames.cend()) {
guint16 allocated_start_frame = *iter;
guint16 allocated_end_frame = allocated_start_frame;
// Find end of the allocation
while (++iter != used_frames.cend() && *iter == (allocated_end_frame + 1))
allocated_end_frame++;
// Free block before this allocation
if (start_frame < allocated_start_frame &&
end_frame < allocated_start_frame)
break;
// Move after this allocation and check if there is enough space before
// the next allocation
start_frame = GST_ROUND_UP_N(allocated_end_frame + 1, multiplier);
end_frame = start_frame + frame_count - 1;
}
// If above we moved after the end of the available frames error out
if (start_frame > last_frame || end_frame > last_frame) {
GST_WARNING("Did not find a contiguous unused range of %u frames",
frame_count);
return -1;
}
// Otherwise we have enough space after the last allocation
GST_INFO("Using HD frames %u-%u", start_frame, end_frame);
GST_INFO("Using frames %u-%u", start_frame / multiplier,
start_frame / multiplier + frame_count / multiplier - 1);
return start_frame / multiplier;
}
GType gst_aja_audio_system_get_type(void) {
static gsize id = 0;
static const GEnumValue modes[] = {

View file

@ -65,6 +65,11 @@ GstAjaNtv2Device *gst_aja_ntv2_device_ref(GstAjaNtv2Device *device);
G_GNUC_INTERNAL
void gst_aja_ntv2_device_unref(GstAjaNtv2Device *device);
G_GNUC_INTERNAL
gint gst_aja_ntv2_device_find_unallocated_frames(GstAjaNtv2Device *device,
NTV2Channel channel,
guint frame_count);
#define GST_AJA_ALLOCATOR_MEMTYPE "aja"
#define GST_TYPE_AJA_ALLOCATOR (gst_aja_allocator_get_type())

View file

@ -1737,13 +1737,22 @@ restart:
guint16 start_frame = self->start_frame;
guint16 end_frame = self->end_frame;
// If nothing was configured, work with a number of frames that is half
// the queue size and assume that all other channels work the same.
// If both are the same, try to find queue_size/2 unallocated frames and
// use those.
if (start_frame == end_frame) {
guint16 num_frames = self->queue_size / 2;
start_frame = self->channel * num_frames;
end_frame = (self->channel + 1) * num_frames - 1;
gint assigned_start_frame = gst_aja_ntv2_device_find_unallocated_frames(
self->device, self->channel, num_frames);
if (assigned_start_frame == -1) {
GST_ELEMENT_ERROR(self, STREAM, FAILED, (NULL),
("Failed to allocate %u frames", num_frames));
goto out;
}
start_frame = assigned_start_frame;
end_frame = start_frame + num_frames - 1;
}
GST_DEBUG_OBJECT(

View file

@ -42,8 +42,8 @@ GST_DEBUG_CATEGORY_STATIC(gst_aja_src_debug);
#define DEFAULT_TIMECODE_INDEX (GST_AJA_TIMECODE_INDEX_VITC)
#define DEFAULT_REFERENCE_SOURCE (GST_AJA_REFERENCE_SOURCE_FREERUN)
#define DEFAULT_QUEUE_SIZE (16)
#define DEFAULT_START_FRAME (0)
#define DEFAULT_END_FRAME (0)
#define DEFAULT_START_FRAME (8)
#define DEFAULT_END_FRAME (8)
#define DEFAULT_CAPTURE_CPU_CORE (G_MAXUINT)
enum {
@ -156,8 +156,8 @@ static void gst_aja_src_class_init(GstAjaSrcClass *klass) {
gobject_class, PROP_START_FRAME,
g_param_spec_uint(
"start-frame", "Start Frame",
"Start frame buffer to be used for capturing (auto if same number as "
"end-frame).",
"Start frame buffer to be used for capturing (automatically assign "
"that many frames if same number as end-frame).",
0, G_MAXINT, DEFAULT_START_FRAME,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
@ -165,8 +165,8 @@ static void gst_aja_src_class_init(GstAjaSrcClass *klass) {
gobject_class, PROP_END_FRAME,
g_param_spec_uint(
"end-frame", "End Frame",
"End frame buffer to be used for capturing (auto if same number as "
"start-frame).",
"End frame buffer to be used for capturing (automatically assign "
"that many frames if same number as start-frame).",
0, G_MAXINT, DEFAULT_END_FRAME,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
@ -1781,13 +1781,20 @@ restart:
guint16 start_frame = self->start_frame;
guint16 end_frame = self->end_frame;
// If nothing was configured, work with 8 frames and assume that all
// other channels work the same.
// If both are set to the same value, try to find that many unallocated
// frames and use those.
if (start_frame == end_frame) {
const guint16 num_frames = 8;
gint assigned_start_frame = gst_aja_ntv2_device_find_unallocated_frames(
self->device, self->channel, self->start_frame);
start_frame = self->channel * num_frames;
end_frame = (self->channel + 1) * num_frames - 1;
if (assigned_start_frame == -1) {
GST_ELEMENT_ERROR(self, STREAM, FAILED, (NULL),
("Failed to allocate %u frames", start_frame));
goto out;
}
start_frame = assigned_start_frame;
end_frame = start_frame + self->start_frame - 1;
}
GST_DEBUG_OBJECT(