Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 11, 2026

Eliminates per-frame heap allocations in EventStream::sendFrame by replacing the local std::string filepath variable with a reusable member reuse_filepath_.

Changes

  • src/zm_eventstream.h: Added std::string reuse_filepath_ member to EventStream class
  • src/zm_eventstream.cpp:
    • Replaced local filepath variable with reuse_filepath_.clear() at method entry
    • Updated all references (assignments, .c_str(), .empty() calls) to use member variable

Performance Impact

After the first frame, the string's internal buffer is reused across subsequent sendFrame() calls, avoiding repeated allocations from stringtf() on hot path.

Before:

std::string filepath;  // new allocation each sendFrame call
filepath = stringtf(...);

After:

reuse_filepath_.clear();  // reuses existing buffer capacity
reuse_filepath_ = stringtf(...);
Original prompt

Problem Statement

Refactor the filepath handling in EventStream::sendFrame to use a pre-allocated reusable std::string member instead of the C-style char[PATH_MAX] buffer with snprintf() introduced in PR #4609.

Context

PR #4609 (commit c8d5ea3) optimized EventStream::sendFrame by replacing stringtf() calls with snprintf() into a stack-allocated char[PATH_MAX] buffer wrapped in std::string_view. While this eliminated heap allocations, it uses C-style string handling.

This PR proposes a more idiomatic C++ approach that achieves the same performance benefits while maintaining type safety.

Proposed Changes

1. Add reusable string member to EventStream class

File: src/zm_eventstream.h

In the private section of the EventStream class (around line 127-130), modify:

private:
  bool send_file(const std::string &filepath);
  bool send_buffer(uint8_t * buffer, int size);
  Storage *storage;
  FFmpeg_Input  *ffmpeg_input;
  Image reuse_image_;  // reused across sendFrame calls to avoid per-frame heap alloc
  std::string reuse_filepath_;  // reused across sendFrame calls to avoid per-frame heap alloc
};

2. Refactor sendFrame method to use reuse_filepath_

File: src/zm_eventstream.cpp

In the EventStream::sendFrame method (starting around line 844), replace the current implementation that uses char filepath_buf[PATH_MAX] and std::string_view filepath with the reusable string approach:

Current code (from PR #4609):

  // Stack buffer + string_view avoids the per-frame heap allocations that
  // stringtf() would incur (2 allocs per call).  All consumers need const char*
  // anyway (access, ReadJpeg, Image ctor, Debug/Error format args).
  char filepath_buf[PATH_MAX] = "";
  std::string_view filepath;  // non-owning view into filepath_buf

  // This needs to be abstracted.  If we are saving jpgs, then load the capture file.
  // If we are only saving analysis frames, then send that.
  if ((frame_type == FRAME_ANALYSIS) && (event_data->SaveJPEGs & 2)) {
    snprintf(filepath_buf, sizeof(filepath_buf), staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
    if (access(filepath_buf, R_OK) != 0) {
      Debug(1, "analyze file %s not found will try to stream from other", filepath_buf);
      snprintf(filepath_buf, sizeof(filepath_buf), staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
      if (access(filepath_buf, R_OK) != 0) {
        Debug(1, "capture file %s not found either", filepath_buf);
        filepath_buf[0] = '\0';
      }
    }
    filepath = filepath_buf;
  } else if (event_data->SaveJPEGs & 1) {
    snprintf(filepath_buf, sizeof(filepath_buf), staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
    filepath = filepath_buf;
  } else if (!ffmpeg_input) {
    Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet");
    return false;
  }

Replace with:

  // Reusable string member avoids per-frame heap allocations.
  // After the first frame, the string's buffer is reused (unless path exceeds capacity).
  reuse_filepath_.clear();

  // This needs to be abstracted.  If we are saving jpgs, then load the capture file.
  // If we are only saving analysis frames, then send that.
  if ((frame_type == FRAME_ANALYSIS) && (event_data->SaveJPEGs & 2)) {
    reuse_filepath_ = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
    if (access(reuse_filepath_.c_str(), R_OK) != 0) {
      Debug(1, "analyze file %s not found will try to stream from other", reuse_filepath_.c_str());
      reuse_filepath_ = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
      if (access(reuse_filepath_.c_str(), R_OK) != 0) {
        Debug(1, "capture file %s not found either", reuse_filepath_.c_str());
        reuse_filepath_.clear();
      }
    }
  } else if (event_data->SaveJPEGs & 1) {
    reuse_filepath_ = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
  } else if (!ffmpeg_input) {
    Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet");
    return false;
  }

3. Update all uses of filepath in sendFrame

Throughout the rest of the sendFrame method, replace references to filepath_buf and filepath with reuse_filepath_:

Around line 877-882 (STREAM_MPEG path):

  if ( type == STREAM_MPEG ) {
    Image image(reuse_filepath_.c_str());

    Image *send_image = prepareImage(&image);

Around line 901-908 (send_raw path):

    if (send_raw) {
      fprintf(stdout, "--" BOUNDARY "\r\n");
      if (!send_file(reuse_filepath_)) {
        Error("Can't send %s: %s", reuse_filepath_.c_str(), strerror(errno));
        return false;
      }

Around line 910-913 (image loading path):

      Image *image = null...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI and others added 2 commits February 11, 2026 04:19
Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
Co-authored-by: connortechnology <925519+connortechnology@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor filepath handling in EventStream::sendFrame Refactor EventStream::sendFrame to use reusable filepath member Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants