Skip to content

Commit

Permalink
Use AJA code for HSDK 1.0 for LWS2 branch (#670)
Browse files Browse the repository at this point in the history
* Missing AJA dependency for colonoscopy segmentation
* Use AJA source from Holoscan SDK 1.0 for compatibility

---------

Signed-off-by: Julien Jomier <jjomier@nvidia.com>
  • Loading branch information
jjomier authored Jan 30, 2025
1 parent d14a141 commit d125975
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 147 deletions.
2 changes: 1 addition & 1 deletion applications/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ add_holohub_application(basic_networking_ping DEPENDS
add_holohub_application(body_pose_estimation DEPENDS
OPERATORS OPTIONAL dds_video_subscriber dds_video_publisher)

add_holohub_application(colonoscopy_segmentation)
add_holohub_application(colonoscopy_segmentation DEPENDS OPERATORS aja_source)

add_holohub_application(cvcuda_basic DEPENDS OPERATORS cvcuda_holoscan_interop)

Expand Down
120 changes: 16 additions & 104 deletions operators/aja_source/aja_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ void AJASourceOp::setup(OperatorSpec& spec) {
constexpr uint32_t kDefaultWidth = 1920;
constexpr uint32_t kDefaultHeight = 1080;
constexpr uint32_t kDefaultFramerate = 60;
constexpr bool kDefaultInterlaced = false;
constexpr bool kDefaultRDMA = false;
constexpr bool kDefaultEnableOverlay = false;
constexpr bool kDefaultOverlayRDMA = false;
Expand All @@ -66,7 +65,6 @@ void AJASourceOp::setup(OperatorSpec& spec) {
spec.param(width_, "width", "Width", "Width of the stream.", kDefaultWidth);
spec.param(height_, "height", "Height", "Height of the stream.", kDefaultHeight);
spec.param(framerate_, "framerate", "Framerate", "Framerate of the stream.", kDefaultFramerate);
spec.param(interlaced_, "interlaced", "Interlaced", "Interlaced or not.", kDefaultInterlaced);
spec.param(use_rdma_, "rdma", "RDMA", "Enable RDMA.", kDefaultRDMA);
spec.param(
enable_overlay_, "enable_overlay", "EnableOverlay", "Enable overlay.", kDefaultEnableOverlay);
Expand All @@ -90,85 +88,15 @@ void AJASourceOp::setup(OperatorSpec& spec) {
}

AJAStatus AJASourceOp::DetermineVideoFormat() {
video_format_ = NTV2_FORMAT_UNKNOWN;

if (interlaced_) {
if (width_ == 1920 && height_ == 1080) {
if (framerate_ == 50) {
video_format_ = NTV2_FORMAT_1080i_5000;
} else if (framerate_ == 59) {
video_format_ = NTV2_FORMAT_1080i_5994;
} else if (framerate_ == 60) {
video_format_ = NTV2_FORMAT_1080i_6000;
}
}
if (width_ == 1920 && height_ == 1080 && framerate_ == 60) {
video_format_ = NTV2_FORMAT_1080p_6000_A;
} else if (width_ == 3840 && height_ == 2160 && framerate_ == 60) {
video_format_ = NTV2_FORMAT_3840x2160p_6000;
} else {
if (width_ == 1280 && height_ == 720) {
if (framerate_ == 50) {
video_format_ = NTV2_FORMAT_720p_5000;
} else if (framerate_ == 59) {
video_format_ = NTV2_FORMAT_720p_5994;
} else if (framerate_ == 60) {
video_format_ = NTV2_FORMAT_720p_6000;
}
} else if (width_ == 1920 && height_ == 1080) {
if (framerate_ == 23) {
video_format_ = NTV2_FORMAT_1080p_2398;
} else if (framerate_ == 24) {
video_format_ = NTV2_FORMAT_1080p_2400;
} else if (framerate_ == 25) {
video_format_ = NTV2_FORMAT_1080p_2500;
} else if (framerate_ == 29) {
video_format_ = NTV2_FORMAT_1080p_2997;
} else if (framerate_ == 30) {
video_format_ = NTV2_FORMAT_1080p_3000;
} else if (framerate_ == 50) {
video_format_ = NTV2_FORMAT_1080p_5000_A;
} else if (framerate_ == 59) {
video_format_ = NTV2_FORMAT_1080p_5994_A;
} else if (framerate_ == 60) {
video_format_ = NTV2_FORMAT_1080p_6000_A;
}
} else if (width_ == 3840 && height_ == 2160) {
if (framerate_ == 23) {
video_format_ = NTV2_FORMAT_3840x2160p_2398;
} else if (framerate_ == 24) {
video_format_ = NTV2_FORMAT_3840x2160p_2400;
} else if (framerate_ == 25) {
video_format_ = NTV2_FORMAT_3840x2160p_2500;
} else if (framerate_ == 29) {
video_format_ = NTV2_FORMAT_3840x2160p_2997;
} else if (framerate_ == 30) {
video_format_ = NTV2_FORMAT_3840x2160p_3000;
} else if (framerate_ == 50) {
video_format_ = NTV2_FORMAT_3840x2160p_5000;
} else if (framerate_ == 59) {
video_format_ = NTV2_FORMAT_3840x2160p_5994;
} else if (framerate_ == 60) {
video_format_ = NTV2_FORMAT_3840x2160p_6000;
}
} else if (width_ == 4096 && height_ == 2160) {
if (framerate_ == 23) {
video_format_ = NTV2_FORMAT_4096x2160p_2398;
} else if (framerate_ == 24) {
video_format_ = NTV2_FORMAT_4096x2160p_2400;
} else if (framerate_ == 25) {
video_format_ = NTV2_FORMAT_4096x2160p_2500;
} else if (framerate_ == 29) {
video_format_ = NTV2_FORMAT_4096x2160p_2997;
} else if (framerate_ == 30) {
video_format_ = NTV2_FORMAT_4096x2160p_3000;
} else if (framerate_ == 50) {
video_format_ = NTV2_FORMAT_4096x2160p_5000;
} else if (framerate_ == 59) {
video_format_ = NTV2_FORMAT_4096x2160p_5994;
} else if (framerate_ == 60) {
video_format_ = NTV2_FORMAT_4096x2160p_6000;
}
}
return AJA_STATUS_UNSUPPORTED;
}

return (video_format_ == NTV2_FORMAT_UNKNOWN) ? AJA_STATUS_UNSUPPORTED : AJA_STATUS_SUCCESS;
return AJA_STATUS_SUCCESS;
}

AJAStatus AJASourceOp::OpenDevice() {
Expand Down Expand Up @@ -207,14 +135,14 @@ AJAStatus AJASourceOp::OpenDevice() {
return AJA_STATUS_UNSUPPORTED;
}
if (!NTV2_IS_VALID_CHANNEL(channel_)) {
HOLOSCAN_LOG_ERROR("Invalid AJA channel: {}", static_cast<int>(channel_.get()));
HOLOSCAN_LOG_ERROR("Invalid AJA channel: {}", channel_.get());
return AJA_STATUS_UNSUPPORTED;
}

// Check overlay capabilities.
if (enable_overlay_) {
if (!NTV2_IS_VALID_CHANNEL(overlay_channel_)) {
HOLOSCAN_LOG_ERROR("Invalid overlay channel: {}", static_cast<int>(overlay_channel_.get()));
HOLOSCAN_LOG_ERROR("Invalid overlay channel: {}", overlay_channel_.get());
return AJA_STATUS_UNSUPPORTED;
}

Expand Down Expand Up @@ -312,7 +240,7 @@ AJAStatus AJASourceOp::SetupVideo() {
}
} else if (!is_input_rgb) {
if (NTV2DeviceGetNumCSCs(device_id_) <= static_cast<int>(channel_)) {
HOLOSCAN_LOG_ERROR("No CSC available for NTV2_CHANNEL{}", static_cast<int>(channel_) + 1);
HOLOSCAN_LOG_ERROR("No CSC available for NTV2_CHANNEL{}", channel_ + 1);
return AJA_STATUS_UNSUPPORTED;
}
NTV2InputXptID csc_input = GetCSCInputXptFromChannel(channel_);
Expand Down Expand Up @@ -450,28 +378,12 @@ void AJASourceOp::initialize() {

void AJASourceOp::start() {
// Determine whether or not we're using the iGPU.
// TODO(unknown): This assumes we're using the first GPU device (as does the rest of the
// operator).
// TODO: This assumes we're using the first GPU device (as does the rest of the operator).
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, 0);
is_igpu_ = prop.integrated;

float framerate;
if (framerate_ == 23) {
framerate = 23.98F;
} else if (framerate_ == 29) {
framerate = 29.97F;
} else if (framerate_ == 59) {
framerate = 59.94F;
} else {
framerate = framerate_;
}
HOLOSCAN_LOG_INFO("AJA Source: Capturing {}x{}@{}Hz {}from NTV2_CHANNEL{}",
width_,
height_,
framerate,
(interlaced_ ? "(interlaced) " : ""),
(channel_.get() + 1));
HOLOSCAN_LOG_INFO("AJA Source: Capturing from NTV2_CHANNEL{}", (channel_.get() + 1));
HOLOSCAN_LOG_INFO("AJA Source: RDMA is {}", use_rdma_ ? "enabled" : "disabled");
if (enable_overlay_) {
HOLOSCAN_LOG_INFO("AJA Source: Outputting overlay to NTV2_CHANNEL{}",
Expand All @@ -483,7 +395,7 @@ void AJASourceOp::start() {

AJAStatus status = DetermineVideoFormat();
if (AJA_FAILURE(status)) {
throw std::runtime_error("Video format could not be determined or is not supported.");
throw std::runtime_error("Video format could not be determined based on parameters.");
}

status = OpenDevice();
Expand All @@ -506,11 +418,11 @@ void AJASourceOp::compute(InputContext& op_input, OutputContext& op_output,
bool have_overlay_in = false;
holoscan::gxf::Entity overlay_in_message;
auto maybe_overlay_message = op_input.receive<gxf::Entity>("overlay_buffer_input");
if (!maybe_overlay_message || maybe_overlay_message.value().is_null()) {
HOLOSCAN_LOG_TRACE("Operator '{}' failed to find overlay_buffer_input", name_);
} else {
if (maybe_overlay_message) {
overlay_in_message = maybe_overlay_message.value();
have_overlay_in = true;
} else {
HOLOSCAN_LOG_TRACE("Failed to find overlay_buffer_input");
}

if (enable_overlay_ && have_overlay_in) {
Expand All @@ -532,7 +444,7 @@ void AJASourceOp::compute(InputContext& op_input, OutputContext& op_output,
// Update the next input frame and wait until it starts.
uint32_t next_hw_frame = (current_hw_frame_ + 1) % 2;
device_.SetInputFrame(channel_, next_hw_frame);
device_.WaitForInputFieldID(NTV2_FIELD0, channel_);
device_.WaitForInputVerticalInterrupt(channel_);

// Read the last completed frame.
auto size = GetVideoWriteSize(video_format_, pixel_format_);
Expand Down
57 changes: 15 additions & 42 deletions operators/aja_source/aja_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@
#ifndef HOLOSCAN_OPERATORS_AJA_SOURCE_AJA_SOURCE_HPP
#define HOLOSCAN_OPERATORS_AJA_SOURCE_AJA_SOURCE_HPP

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
// AJA headers are not clean C++ and generate warnings
// when compiled with -Werror.
// Since ajantv2 is 3rd party code, we disable the warning for now.
#include <ajantv2/includes/ntv2card.h>
#include <ajantv2/includes/ntv2devicescanner.h>
#pragma GCC diagnostic pop
#include <ajantv2/includes/ntv2enums.h>

#include <string>
Expand All @@ -43,41 +37,21 @@ namespace holoscan::ops {
/**
* @brief Operator class to get the video stream from AJA capture card.
*
* ==Named Inputs==
* **Named inputs:**
* - *overlay_buffer_input*: `nvidia::gxf::VideoBuffer` (optional)
* - The operator does not require a message on this input port in order for ``compute`` to
* be called. If a message is found, and `enable_overlay` is true, the image will be
* mixed with the image captured by the AJA card. If `enable_overlay` is false, any message
* on this port will be ignored.
*
* - **overlay_buffer_input** : `nvidia::gxf::VideoBuffer` (optional)
* - The operator does not require a message on this input port in order for `compute` to
* be called. If a message is found, and `enable_overlay` is true, the image will be
* mixed with the image captured by the AJA card. If `enable_overlay` is false, any message
* on this port will be ignored.
*
* ==Named Outputs==
*
* - **video_buffer_output** : `nvidia::gxf::VideoBuffer`
* - The output video frame from the AJA capture card. If `overlay_rdma` is true, this
* video buffer will be on the device, otherwise it will be in pinned host memory.
* - **overlay_buffer_output** : `nvidia::gxf::VideoBuffer` (optional)
* - This output port will only emit a video buffer when `enable_overlay` is true. If
* `overlay_rdma` is true, this video buffer will be on the device, otherwise it will be
* in pinned host memory.
*
* ==Parameters==
*
* - **device**: The device to target (e.g., "0" for device 0). Optional (default: "0").
* - **channel**: The camera `NTV2Channel` to use for output (e.g., `NTV2Channel::NTV2_CHANNEL1`
* (`0`) or "NTV2_CHANNEL1" (in YAML) for the first channel). Optional (default:
* `NTV2Channel::NTV2_CHANNEL1` in C++ or `"NTV2_CHANNEL1"` in YAML).
* - **width**: Width of the video stream. Optional (default: `1920`).
* - **height**: Height of the video stream. Optional (default: `1080`).
* - **framerate**: Frame rate of the video stream. Optional (default: `60`).
* - **interlaced**: Whether the frame is interlaced (true) or progressive (false). Optional (default: `false`).
* - **rdma**: Boolean indicating whether RDMA is enabled. Optional (default: `false`).
* - **enable_overlay**: Boolean indicating whether a separate overlay channel is enabled. Optional
* (default: `false`).
* - **overlay_channel**: The camera `NTV2Channel` to use for overlay output. Optional (default:
* `NTV2Channel::NTV2_CHANNEL2` in C++ or `"NTV2_CHANNEL2"` in YAML).
* - **overlay_rdma**: Boolean indicating whether RDMA is enabled for the overlay. Optional
* (default: `true`).
* **Named outputs:**
* - *video_buffer_output*: `nvidia::gxf::VideoBuffer`
* - The output video frame from the AJA capture card. If ``overlay_rdma`` is true, this
* video buffer will be on the device, otherwise it will be in pinned host memory.
* - *overlay_buffer_output*: `nvidia::gxf::VideoBuffer` (optional)
* - This output port will only emit a video buffer when ``enable_overlay`` is true. If
* ``overlay_rdma`` is true, this video buffer will be on the device, otherwise it will be
* in pinned host memory.
*/
class AJASourceOp : public holoscan::Operator {
public:
Expand Down Expand Up @@ -110,7 +84,6 @@ class AJASourceOp : public holoscan::Operator {
Parameter<uint32_t> width_;
Parameter<uint32_t> height_;
Parameter<uint32_t> framerate_;
Parameter<bool> interlaced_;
Parameter<bool> use_rdma_;
Parameter<bool> enable_overlay_;
Parameter<NTV2Channel> overlay_channel_;
Expand All @@ -121,7 +94,7 @@ class AJASourceOp : public holoscan::Operator {
// internal state
CNTV2Card device_;
NTV2DeviceID device_id_ = DEVICE_ID_NOTFOUND;
NTV2VideoFormat video_format_ = NTV2_FORMAT_UNKNOWN;
NTV2VideoFormat video_format_ = NTV2_FORMAT_1080p_6000_A;
NTV2PixelFormat pixel_format_ = NTV2_FBF_ABGR;
bool use_tsi_ = false;
bool is_kona_hdmi_ = false;
Expand Down

0 comments on commit d125975

Please sign in to comment.