add SharedMemoryWriter support
This commit is contained in:
@@ -598,6 +598,104 @@ Retrieve all current eHDR settings.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 21. Enable Shared Memory
|
||||||
|
|
||||||
|
Enable shared memory output for direct frame access by external processes. This creates a shared memory region at `/dev/shm/<name>` where frames are written in parallel to the GStreamer pipeline.
|
||||||
|
|
||||||
|
**Note:** Cannot be enabled while streaming is active. Must be enabled before starting the stream.
|
||||||
|
|
||||||
|
**Command:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "enable_shared_memory",
|
||||||
|
"params": {
|
||||||
|
"name": "/vizion_frame",
|
||||||
|
"buffer_size": "8294528"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `name`: Shared memory object name (optional, default: "/vizion_frame")
|
||||||
|
- `buffer_size`: Buffer size in bytes (optional, default: 8294528 for 1080p RGBA + header)
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Shared memory enabled",
|
||||||
|
"name": "/vizion_frame",
|
||||||
|
"size": 8294528
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Shared Memory Layout:**
|
||||||
|
- **Header (128 bytes):** Contains frame metadata
|
||||||
|
- Magic number (0x56495A4E = "VIZN")
|
||||||
|
- Width, height, format
|
||||||
|
- Data size, timestamp (nanoseconds)
|
||||||
|
- Frame sequence counter
|
||||||
|
- Atomic write sequence (for lock-free synchronization)
|
||||||
|
- **Frame Data:** Raw frame bytes
|
||||||
|
|
||||||
|
**Example Buffer Sizes:**
|
||||||
|
- 1920×1080 RGBA: 8294528 bytes (1920×1080×4 + 128)
|
||||||
|
- 1280×720 RGBA: 3686528 bytes (1280×720×4 + 128)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 22. Disable Shared Memory
|
||||||
|
|
||||||
|
Disable and cleanup shared memory output.
|
||||||
|
|
||||||
|
**Command:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "disable_shared_memory"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"message": "Shared memory disabled"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 23. Get Shared Memory Status
|
||||||
|
|
||||||
|
Query the current shared memory configuration.
|
||||||
|
|
||||||
|
**Command:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"command": "get_shared_memory_status"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (when enabled):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"shared_memory_enabled": true,
|
||||||
|
"name": "/vizion_frame",
|
||||||
|
"size": 8294528
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response (when disabled):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"shared_memory_enabled": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
### Complete Workflow Example
|
### Complete Workflow Example
|
||||||
@@ -620,14 +718,21 @@ echo '{"command":"set_ehdr_exposure_max","params":{"value":"4"}}' | socat - UNIX
|
|||||||
echo '{"command":"set_ehdr_ratio_min","params":{"value":"12"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"set_ehdr_ratio_min","params":{"value":"12"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
echo '{"command":"set_ehdr_ratio_max","params":{"value":"24"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"set_ehdr_ratio_max","params":{"value":"24"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# 3b. (Optional) Enable shared memory output
|
||||||
|
echo '{"command":"enable_shared_memory","params":{"name":"/vizion_frame","buffer_size":"8294528"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
# 4. Start streaming
|
# 4. Start streaming
|
||||||
echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"start_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
# 5. Check status
|
# 5. Check status
|
||||||
echo '{"command":"get_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"get_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
echo '{"command":"get_shared_memory_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
# 6. Stop streaming when done
|
# 6. Stop streaming when done
|
||||||
echo '{"command":"stop_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"stop_stream"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# 7. (Optional) Disable shared memory
|
||||||
|
echo '{"command":"disable_shared_memory"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
### GStreamer Pipeline Examples
|
### GStreamer Pipeline Examples
|
||||||
@@ -692,6 +797,28 @@ echo '{"command":"set_ehdr_ratio_max","params":{"value":"24"}}' | socat - UNIX-C
|
|||||||
echo '{"command":"get_ehdr_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
echo '{"command":"get_ehdr_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Shared Memory Control Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable shared memory with default settings
|
||||||
|
echo '{"command":"enable_shared_memory"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# Enable shared memory with custom name and size
|
||||||
|
echo '{"command":"enable_shared_memory","params":{"name":"/vizion_cam0","buffer_size":"8294528"}}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# Get shared memory status
|
||||||
|
echo '{"command":"get_shared_memory_status"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# Disable shared memory
|
||||||
|
echo '{"command":"disable_shared_memory"}' | socat - UNIX-CONNECT:/tmp/vizion_control.sock
|
||||||
|
|
||||||
|
# Verify shared memory file exists (should show ~8.3MB file)
|
||||||
|
ls -lh /dev/shm/vizion_frame
|
||||||
|
|
||||||
|
# Watch shared memory updates in real-time (check modification time)
|
||||||
|
watch -n 0.1 'ls -l /dev/shm/vizion_frame'
|
||||||
|
```
|
||||||
|
|
||||||
### Using `nc` (netcat with Unix socket support)
|
### Using `nc` (netcat with Unix socket support)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -736,6 +863,17 @@ print(send_command("set_ehdr_exposure_max", {"value": "4"}))
|
|||||||
print(send_command("set_ehdr_ratio_min", {"value": "12"}))
|
print(send_command("set_ehdr_ratio_min", {"value": "12"}))
|
||||||
print(send_command("set_ehdr_ratio_max", {"value": "24"}))
|
print(send_command("set_ehdr_ratio_max", {"value": "24"}))
|
||||||
print(send_command("get_ehdr_status")) # Get current eHDR settings
|
print(send_command("get_ehdr_status")) # Get current eHDR settings
|
||||||
|
|
||||||
|
# Shared memory control examples
|
||||||
|
print(send_command("enable_shared_memory", {
|
||||||
|
"name": "/vizion_frame",
|
||||||
|
"buffer_size": "8294528"
|
||||||
|
}))
|
||||||
|
print(send_command("get_shared_memory_status"))
|
||||||
|
print(send_command("start_stream"))
|
||||||
|
# ... streaming active, external process can read /dev/shm/vizion_frame ...
|
||||||
|
print(send_command("stop_stream"))
|
||||||
|
print(send_command("disable_shared_memory"))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using C++
|
### Using C++
|
||||||
@@ -777,6 +915,14 @@ int main() {
|
|||||||
std::cout << sendCommand(R"({"command":"set_ehdr_exposure_max","params":{"value":"4"}})") << std::endl;
|
std::cout << sendCommand(R"({"command":"set_ehdr_exposure_max","params":{"value":"4"}})") << std::endl;
|
||||||
std::cout << sendCommand(R"({"command":"get_ehdr_status"})") << std::endl;
|
std::cout << sendCommand(R"({"command":"get_ehdr_status"})") << std::endl;
|
||||||
|
|
||||||
|
// Shared memory control examples
|
||||||
|
std::cout << sendCommand(R"({"command":"enable_shared_memory","params":{"name":"/vizion_frame","buffer_size":"8294528"}})") << std::endl;
|
||||||
|
std::cout << sendCommand(R"({"command":"get_shared_memory_status"})") << std::endl;
|
||||||
|
std::cout << sendCommand(R"({"command":"start_stream"})") << std::endl;
|
||||||
|
// ... streaming active, external process can read /dev/shm/vizion_frame ...
|
||||||
|
std::cout << sendCommand(R"({"command":"stop_stream"})") << std::endl;
|
||||||
|
std::cout << sendCommand(R"({"command":"disable_shared_memory"})") << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -873,6 +1019,291 @@ ffplay http://192.168.1.100:8080
|
|||||||
curl http://192.168.1.100:8080 > stream.mjpg
|
curl http://192.168.1.100:8080 > stream.mjpg
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Shared Memory Reader Implementation
|
||||||
|
|
||||||
|
When shared memory output is enabled, external processes can directly read frame data from `/dev/shm/<name>`. Here's how to implement a reader:
|
||||||
|
|
||||||
|
### Shared Memory Structure
|
||||||
|
|
||||||
|
```c
|
||||||
|
struct SharedMemoryHeader {
|
||||||
|
uint32_t magic; // 0x56495A4E ("VIZN") - for validation
|
||||||
|
uint32_t width; // Frame width in pixels
|
||||||
|
uint32_t height; // Frame height in pixels
|
||||||
|
uint32_t format; // Format enum (VX_IMAGE_FORMAT)
|
||||||
|
uint32_t data_size; // Frame data size in bytes
|
||||||
|
uint64_t timestamp_ns; // Timestamp in nanoseconds
|
||||||
|
uint32_t frame_sequence; // Monotonic frame counter
|
||||||
|
atomic_uint32_t write_sequence; // Lock-free sync counter
|
||||||
|
char format_str[16]; // Format string ("YUY2", "MJPG", etc.)
|
||||||
|
uint8_t reserved[72]; // Reserved (padding to 128 bytes)
|
||||||
|
};
|
||||||
|
// Frame data starts at offset 128
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lock-Free Read Protocol
|
||||||
|
|
||||||
|
The `write_sequence` counter enables lock-free synchronization:
|
||||||
|
- **Even values**: Write complete, data is consistent
|
||||||
|
- **Odd values**: Write in progress, data may be inconsistent
|
||||||
|
|
||||||
|
**Reader Algorithm:**
|
||||||
|
1. Read `write_sequence` (must be even)
|
||||||
|
2. Read header and frame data
|
||||||
|
3. Read `write_sequence` again
|
||||||
|
4. If values match → data is consistent
|
||||||
|
5. If values differ → retry
|
||||||
|
|
||||||
|
### C++ Reader Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
struct SharedMemoryHeader {
|
||||||
|
uint32_t magic;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t format;
|
||||||
|
uint32_t data_size;
|
||||||
|
uint64_t timestamp_ns;
|
||||||
|
uint32_t frame_sequence;
|
||||||
|
std::atomic<uint32_t> write_sequence;
|
||||||
|
char format_str[16];
|
||||||
|
uint8_t reserved[72];
|
||||||
|
};
|
||||||
|
|
||||||
|
class SharedMemoryReader {
|
||||||
|
private:
|
||||||
|
int fd_;
|
||||||
|
void* ptr_;
|
||||||
|
size_t size_;
|
||||||
|
uint32_t last_sequence_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SharedMemoryReader(const char* name, size_t size)
|
||||||
|
: fd_(-1), ptr_(nullptr), size_(size), last_sequence_(0) {
|
||||||
|
|
||||||
|
// Open shared memory
|
||||||
|
fd_ = shm_open(name, O_RDONLY, 0666);
|
||||||
|
if (fd_ < 0) {
|
||||||
|
throw std::runtime_error("Failed to open shared memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map memory
|
||||||
|
ptr_ = mmap(nullptr, size_, PROT_READ, MAP_SHARED, fd_, 0);
|
||||||
|
if (ptr_ == MAP_FAILED) {
|
||||||
|
close(fd_);
|
||||||
|
throw std::runtime_error("Failed to map shared memory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~SharedMemoryReader() {
|
||||||
|
if (ptr_ != nullptr && ptr_ != MAP_FAILED) {
|
||||||
|
munmap(ptr_, size_);
|
||||||
|
}
|
||||||
|
if (fd_ >= 0) {
|
||||||
|
close(fd_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readFrame(uint8_t* buffer, size_t buffer_size,
|
||||||
|
uint32_t* width, uint32_t* height,
|
||||||
|
char* format, uint64_t* timestamp) {
|
||||||
|
|
||||||
|
auto* header = static_cast<SharedMemoryHeader*>(ptr_);
|
||||||
|
auto* frame_data = static_cast<uint8_t*>(ptr_) + sizeof(SharedMemoryHeader);
|
||||||
|
|
||||||
|
uint32_t seq1, seq2;
|
||||||
|
do {
|
||||||
|
// Read sequence number
|
||||||
|
seq1 = header->write_sequence.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
// Wait if write is in progress (odd sequence)
|
||||||
|
while (seq1 & 1) {
|
||||||
|
seq1 = header->write_sequence.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate magic number
|
||||||
|
if (header->magic != 0x56495A4E) {
|
||||||
|
std::cerr << "Invalid magic number" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a new frame
|
||||||
|
if (header->frame_sequence <= last_sequence_) {
|
||||||
|
return false; // Already seen this frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check buffer size
|
||||||
|
if (header->data_size > buffer_size) {
|
||||||
|
std::cerr << "Buffer too small" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read metadata
|
||||||
|
*width = header->width;
|
||||||
|
*height = header->height;
|
||||||
|
*timestamp = header->timestamp_ns;
|
||||||
|
strncpy(format, header->format_str, 15);
|
||||||
|
format[15] = '\0';
|
||||||
|
|
||||||
|
// Copy frame data
|
||||||
|
memcpy(buffer, frame_data, header->data_size);
|
||||||
|
|
||||||
|
// Verify sequence hasn't changed
|
||||||
|
seq2 = header->write_sequence.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
} while (seq1 != seq2);
|
||||||
|
|
||||||
|
last_sequence_ = header->frame_sequence;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Usage example
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
SharedMemoryReader reader("/vizion_frame", 8294528);
|
||||||
|
|
||||||
|
std::vector<uint8_t> frame_buffer(8294400); // 1920x1080x4
|
||||||
|
uint32_t width, height;
|
||||||
|
char format[16];
|
||||||
|
uint64_t timestamp;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (reader.readFrame(frame_buffer.data(), frame_buffer.size(),
|
||||||
|
&width, &height, format, ×tamp)) {
|
||||||
|
std::cout << "New frame: " << width << "x" << height
|
||||||
|
<< " format=" << format
|
||||||
|
<< " timestamp=" << timestamp << std::endl;
|
||||||
|
|
||||||
|
// Process frame data here...
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(10000); // Poll every 10ms
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python Reader Example
|
||||||
|
|
||||||
|
```python
|
||||||
|
import mmap
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class SharedMemoryReader:
|
||||||
|
HEADER_SIZE = 128
|
||||||
|
MAGIC = 0x56495A4E # "VIZN"
|
||||||
|
|
||||||
|
def __init__(self, name, size):
|
||||||
|
self.name = name
|
||||||
|
self.size = size
|
||||||
|
self.last_sequence = 0
|
||||||
|
|
||||||
|
# Open shared memory file
|
||||||
|
shm_path = Path(f"/dev/shm{name}")
|
||||||
|
self.fd = open(shm_path, "rb")
|
||||||
|
self.mmap = mmap.mmap(self.fd.fileno(), size, access=mmap.ACCESS_READ)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.mmap:
|
||||||
|
self.mmap.close()
|
||||||
|
if self.fd:
|
||||||
|
self.fd.close()
|
||||||
|
|
||||||
|
def read_frame(self):
|
||||||
|
while True:
|
||||||
|
# Read write_sequence (offset 28)
|
||||||
|
self.mmap.seek(28)
|
||||||
|
seq1 = struct.unpack('I', self.mmap.read(4))[0]
|
||||||
|
|
||||||
|
# Wait if write in progress (odd)
|
||||||
|
while seq1 & 1:
|
||||||
|
time.sleep(0.0001)
|
||||||
|
self.mmap.seek(28)
|
||||||
|
seq1 = struct.unpack('I', self.mmap.read(4))[0]
|
||||||
|
|
||||||
|
# Read header
|
||||||
|
self.mmap.seek(0)
|
||||||
|
header_bytes = self.mmap.read(self.HEADER_SIZE)
|
||||||
|
|
||||||
|
magic, width, height, fmt, data_size, timestamp, frame_seq = \
|
||||||
|
struct.unpack('IIIIIQII', header_bytes[:40])
|
||||||
|
|
||||||
|
format_str = header_bytes[40:56].decode('utf-8').strip('\x00')
|
||||||
|
|
||||||
|
# Validate magic
|
||||||
|
if magic != self.MAGIC:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check if new frame
|
||||||
|
if frame_seq <= self.last_sequence:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Read frame data
|
||||||
|
frame_data = self.mmap.read(data_size)
|
||||||
|
|
||||||
|
# Verify sequence
|
||||||
|
self.mmap.seek(28)
|
||||||
|
seq2 = struct.unpack('I', self.mmap.read(4))[0]
|
||||||
|
|
||||||
|
if seq1 == seq2:
|
||||||
|
self.last_sequence = frame_seq
|
||||||
|
return {
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'format': format_str,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'sequence': frame_seq,
|
||||||
|
'data': frame_data
|
||||||
|
}
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
reader = SharedMemoryReader("/vizion_frame", 8294528)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
frame = reader.read_frame()
|
||||||
|
if frame:
|
||||||
|
print(f"New frame: {frame['width']}x{frame['height']} "
|
||||||
|
f"format={frame['format']} seq={frame['sequence']}")
|
||||||
|
# Process frame['data'] here...
|
||||||
|
time.sleep(0.01) # Poll every 10ms
|
||||||
|
finally:
|
||||||
|
reader.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Considerations
|
||||||
|
|
||||||
|
**Polling vs. Blocking:**
|
||||||
|
- The reader examples use polling (checking periodically)
|
||||||
|
- For lower CPU usage, increase poll interval
|
||||||
|
- For lower latency, decrease poll interval
|
||||||
|
- Alternative: Use inotify to watch `/dev/shm/<name>` for modifications
|
||||||
|
|
||||||
|
**Memory Bandwidth:**
|
||||||
|
- Reading shared memory creates an additional memcpy
|
||||||
|
- For zero-copy processing, process data in-place (requires careful synchronization)
|
||||||
|
- Multiple readers can access the same shared memory simultaneously
|
||||||
|
|
||||||
|
**Frame Rate:**
|
||||||
|
- Reader must keep up with writer's frame rate
|
||||||
|
- Use `frame_sequence` to detect dropped frames
|
||||||
|
- Monitor `last_sequence` vs `header->frame_sequence` difference
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- The socket file is automatically created when VizionStreamer starts
|
- The socket file is automatically created when VizionStreamer starts
|
||||||
@@ -885,3 +1316,7 @@ curl http://192.168.1.100:8080 > stream.mjpg
|
|||||||
- Default pipeline: `videoconvert ! autovideosink` (display locally)
|
- Default pipeline: `videoconvert ! autovideosink` (display locally)
|
||||||
- eHDR features require compatible camera models (VCI/VCS/VLS3/VLS-GM2/TEVS-AR0821/AR0822)
|
- eHDR features require compatible camera models (VCI/VCS/VLS3/VLS-GM2/TEVS-AR0821/AR0822)
|
||||||
- eHDR settings may be reset to defaults when the camera starts streaming (driver behavior)
|
- eHDR settings may be reset to defaults when the camera starts streaming (driver behavior)
|
||||||
|
- Shared memory output is independent of GStreamer pipeline (both run in parallel)
|
||||||
|
- Shared memory must be enabled before starting the stream
|
||||||
|
- Shared memory files are automatically cleaned up when disabled or on clean exit
|
||||||
|
- Multiple processes can read from the same shared memory region simultaneously
|
||||||
|
|||||||
Reference in New Issue
Block a user