|
@@ -6,25 +6,19 @@
|
|
|
|
|
|
namespace GNode
|
|
|
{
|
|
|
-
|
|
|
-// --- Private Helper Methods ---
|
|
|
-
|
|
|
void StreamNode::close_stream() {
|
|
|
PLOGI.printf("StreamNode [%s]: Closing stream...", name_.c_str());
|
|
|
- // Reset pointers, which will call destructors if they are unique owners
|
|
|
cap_.reset();
|
|
|
- decoder_.reset(); // Decoder depends on demuxer info, close it first potentially
|
|
|
+ decoder_.reset();
|
|
|
demuxer_.reset();
|
|
|
status_ = StreamStatus::CLOSED;
|
|
|
- frame_count_ = -1; // Reset frame count for the next potential stream
|
|
|
+ frame_count_ = -1;
|
|
|
}
|
|
|
|
|
|
bool StreamNode::open_stream() {
|
|
|
- // Ensure any previous stream is closed before opening a new one
|
|
|
close_stream();
|
|
|
-
|
|
|
PLOGI.printf("StreamNode [%s]: Attempting to open stream: %s", name_.c_str(), stream_url_.c_str());
|
|
|
- status_ = StreamStatus::CLOSED; // Start as closed before trying
|
|
|
+ status_ = StreamStatus::CLOSED;
|
|
|
|
|
|
if (decode_type_ == DecodeType::GPU)
|
|
|
{
|
|
@@ -52,12 +46,10 @@ bool StreamNode::open_stream() {
|
|
|
printf("StreamNode [%s]: GPU Demuxer and Decoder created successfully.", name_.c_str());
|
|
|
status_ = StreamStatus::OPENED;
|
|
|
}
|
|
|
- else // DecodeType::CPU
|
|
|
+ else
|
|
|
{
|
|
|
cap_ = std::make_shared<cv::VideoCapture>();
|
|
|
- // Optionally set backend preference if needed
|
|
|
- // cap_->open(stream_url_, cv::CAP_FFMPEG);
|
|
|
- if (!cap_->open(stream_url_)) // Check return value of open
|
|
|
+ if (!cap_->open(stream_url_))
|
|
|
{
|
|
|
PLOGI.printf("StreamNode [%s] Error: CPU cv::VideoCapture failed to open %s", name_.c_str(), stream_url_.c_str());
|
|
|
cap_.reset(); // Release the failed object
|
|
@@ -75,48 +67,41 @@ bool StreamNode::open_stream() {
|
|
|
status_ = StreamStatus::OPENED;
|
|
|
}
|
|
|
|
|
|
- frame_count_ = -1; // Reset frame count upon successful open
|
|
|
+ frame_count_ = -1;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
-// --- Main Work Loop ---
|
|
|
-
|
|
|
void StreamNode::work()
|
|
|
{
|
|
|
PLOGI.printf("StreamNode [%s] starting work loop. Decode type: %s",
|
|
|
name_.c_str(), (decode_type_ == DecodeType::GPU ? "GPU" : "CPU"));
|
|
|
- while (running_) // Main loop continues as long as the node is supposed to run
|
|
|
+ while (running_)
|
|
|
{
|
|
|
- if (status_ != StreamStatus::OPENED) // Check if stream needs opening/reopening
|
|
|
+ if (status_ != StreamStatus::OPENED)
|
|
|
{
|
|
|
PLOGI.printf("StreamNode [%s]: Stream not open (Status: %d). Attempting to open...",
|
|
|
name_.c_str(), static_cast<int>(status_));
|
|
|
|
|
|
- if (open_stream()) // Try to open
|
|
|
+ if (open_stream())
|
|
|
{
|
|
|
PLOGI.printf("StreamNode [%s]: Stream opened successfully.", name_.c_str());
|
|
|
- // Continue to processing immediately after successful open
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // Opening failed, wait before retrying
|
|
|
PLOGI.printf("StreamNode [%s]: Failed to open stream. Retrying in %d ms...",
|
|
|
name_.c_str(), retry_delay_ms_);
|
|
|
status_ = StreamStatus::OPEN_FAILED; // Ensure status reflects failure
|
|
|
|
|
|
- // Wait for the specified delay, but check running_ periodically
|
|
|
auto wakeUpTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(retry_delay_ms_);
|
|
|
while (running_ && std::chrono::steady_clock::now() < wakeUpTime) {
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Sleep in smaller chunks
|
|
|
}
|
|
|
|
|
|
- if (!running_) break; // Exit outer loop if stopped during wait
|
|
|
- continue; // Go back to the start of the outer loop to retry opening
|
|
|
+ if (!running_) break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // If we reach here, the stream should be OPENED
|
|
|
if (status_ == StreamStatus::OPENED)
|
|
|
{
|
|
|
PLOGI.printf("StreamNode [%s]: Starting stream processing...", name_.c_str());
|
|
@@ -128,34 +113,28 @@ void StreamNode::work()
|
|
|
{
|
|
|
process_stream_gpu();
|
|
|
}
|
|
|
- // After processing function returns, the stream might be closed or encountered an error.
|
|
|
- // The loop will re-evaluate the status_ at the beginning.
|
|
|
+
|
|
|
PLOGI.printf("StreamNode [%s]: Stream processing finished or stopped (Status: %d).",
|
|
|
name_.c_str(), static_cast<int>(status_));
|
|
|
|
|
|
if (status_ == StreamStatus::CLOSED || status_ == StreamStatus::ERROR)
|
|
|
{
|
|
|
- close_stream(); // Ensure resources are released if processing stopped abnormally
|
|
|
+ close_stream();
|
|
|
PLOGI.printf("StreamNode [%s]: Stream closed or errored. Will attempt reconnection if running.", name_.c_str());
|
|
|
- // Optional short delay even after normal close before retry?
|
|
|
- // std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // Should not happen if open_stream logic is correct, but good for debugging
|
|
|
PLOGD.printf("StreamNode [%s]: Unexpected status %d in work loop.", name_.c_str(), static_cast<int>(status_));
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Avoid tight loop on unexpected state
|
|
|
}
|
|
|
|
|
|
- } // End while(running_)
|
|
|
+ }
|
|
|
|
|
|
PLOGI.printf("StreamNode [%s] work loop finished.", name_.c_str());
|
|
|
- close_stream(); // Final cleanup
|
|
|
+ close_stream();
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
-// --- Stream Processing Methods ---
|
|
|
+-
|
|
|
|
|
|
void StreamNode::process_stream_cpu()
|
|
|
{
|
|
@@ -174,15 +153,15 @@ void StreamNode::process_stream_cpu()
|
|
|
success = cap_->read(frame);
|
|
|
} catch (const cv::Exception& ex) {
|
|
|
PLOGE.printf("StreamNode [%s] Error: Exception during cv::VideoCapture::read(): %s", name_.c_str(), ex.what());
|
|
|
- status_ = StreamStatus::ERROR; // Treat OpenCV exception as an error
|
|
|
- break; // Exit processing loop
|
|
|
+ status_ = StreamStatus::ERROR;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
if (!success || frame.empty())
|
|
|
{
|
|
|
PLOGE.printf("StreamNode [%s]: Cannot read frame (End of stream or error).", name_.c_str());
|
|
|
- status_ = StreamStatus::CLOSED; // Assume normal closure or recoverable error
|
|
|
- break; // Exit processing loop, work() will handle retry/stop
|
|
|
+ status_ = StreamStatus::CLOSED;
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
frame_count_++;
|
|
@@ -192,14 +171,13 @@ void StreamNode::process_stream_cpu()
|
|
|
}
|
|
|
|
|
|
auto metaData = std::make_shared<meta::MetaData>();
|
|
|
- // Use clone() to ensure the pushed data is independent if frame is reused by VideoCapture
|
|
|
metaData->image = frame.clone();
|
|
|
metaData->from = name_;
|
|
|
- // metaData->timestamp = getCurrentTimestamp(); // Add timestamp if useful
|
|
|
|
|
|
for (auto& output_buffer : output_buffers_)
|
|
|
{
|
|
|
- if (output_buffer.second) { // Check if buffer pointer is valid
|
|
|
+ if (output_buffer.second)
|
|
|
+ {
|
|
|
output_buffer.second->push(metaData);
|
|
|
}
|
|
|
}
|
|
@@ -310,17 +288,12 @@ void StreamNode::process_stream_gpu()
|
|
|
continue; // Skip this decoded frame
|
|
|
}
|
|
|
|
|
|
- // Important: Create cv::Mat header WITHOUT copying data yet.
|
|
|
- // The data pointer (frame_data) is owned by the decoder.
|
|
|
- // We need to clone it if we push it asynchronously.
|
|
|
cv::Mat frame_gpu(decoder_->get_height(), decoder_->get_width(), CV_8UC3, frame_data);
|
|
|
|
|
|
// Create metadata and copy the frame data
|
|
|
auto metaData = std::make_shared<meta::MetaData>();
|
|
|
metaData->image = frame_gpu.clone(); // CLONE is crucial here!
|
|
|
metaData->from = name_;
|
|
|
- // metaData->timestamp = getCurrentTimestamp(); // Or use frame_pts if preferred
|
|
|
- // metaData->frame_idx = frame_count_; // Add frame index if needed downstream
|
|
|
|
|
|
bool pushed = false;
|
|
|
for (auto& output_buffer : output_buffers_)
|
|
@@ -331,22 +304,13 @@ void StreamNode::process_stream_gpu()
|
|
|
pushed = true;
|
|
|
}
|
|
|
}
|
|
|
- // Optional: Log if data wasn't pushed anywhere
|
|
|
- // if (!pushed) {
|
|
|
- // printf("StreamNode [%s]: Warning - GPU Frame %d processed but not pushed to any output buffer.\n", name_.c_str(), frame_count_);
|
|
|
- // }
|
|
|
-
|
|
|
- // Optional delay if needed for rate limiting, but usually not here
|
|
|
- // std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
- } // End loop over decoded frames
|
|
|
-
|
|
|
- // If status changed to ERROR within the frame loop, break the outer while loop
|
|
|
+ }
|
|
|
if (status_ == StreamStatus::ERROR)
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- }; // End while(running_ && status_ == StreamStatus::OPENED)
|
|
|
+ };
|
|
|
|
|
|
PLOGI.printf("StreamNode [%s]: Exiting GPU processing loop (Running: %s, Status: %d, Total frames processed this session: %d).",
|
|
|
name_.c_str(), running_ ? "true" : "false", static_cast<int>(status_), frame_count_ + 1);
|