Added decoder_requires_next_packet to track whether the decoder still requires follow-up packets after a keyframe.

Continue submitting packets while this flag is set.
Clear the flag as soon as the decoder outputs the first decoded frame.
Reset the flag whenever the decoder state is flushed or recreated.
This commit is contained in:
IgorA100
2026-07-19 23:37:41 +03:00
parent d51485cc86
commit cc9bdb187e
3 changed files with 33 additions and 40 deletions

View File

@@ -3159,7 +3159,7 @@ bool Monitor::Decode() {
(decoding == DECODING_ALWAYS) ||
(decoding == DECODING_KEYFRAMES) ||
((decoding == DECODING_ONDEMAND) && (hasViewers() || shared_data->last_write_index == image_buffer_count)) ||
((decoding == DECODING_KEYFRAMESONDEMAND) && hasViewers());
((decoding == DECODING_KEYFRAMESONDEMAND) && (hasViewers() || decoder_requires_next_packet));
if (!needs_decoding) {
Debug(1, "Flushing decoder in phase 1: %zu packets queued but decoding no longer needed",
@@ -3180,11 +3180,16 @@ bool Monitor::Decode() {
auto front_packet = front_lock.packet_;
int ret = front_packet->receive_frame(context);
Debug(2, "RECV packet=%d ret=%d", front_packet->image_index, ret);
if (ret > 0) {
// Success - got a decoded frame, take ownership and process it
packet_lock = std::move(decoder_queue.front());
decoder_queue.pop_front();
Debug(2, "QUEUE POP size=%zu", decoder_queue.size());
packet = front_packet;
if ((decoding == DECODING_KEYFRAMES || (decoding == DECODING_KEYFRAMESONDEMAND && !hasViewers())) && decoder_requires_next_packet ) {
decoder_requires_next_packet = false;
}
Debug(2, "Received frame for packet %d", packet->image_index);
// Continue to PHASE 3 (frame processing)
} else if (ret < 0) {
@@ -3230,12 +3235,16 @@ bool Monitor::Decode() {
}
// Check if this packet needs to be sent to the decoder
if ((decoding == DECODING_KEYFRAMES || (decoding == DECODING_KEYFRAMESONDEMAND && !hasViewers())) && packet->keyframe) {
decoder_requires_next_packet = true;
Debug(2, "Decoder requires follow-up packets after keyframe %d", packet->image_index);
}
bool already_decoded = packet->image || packet->in_frame || !packet->packet->size;
bool should_decode = !already_decoded && (
(decoding == DECODING_ALWAYS) ||
((decoding == DECODING_ONDEMAND) && (hasViewers() || shared_data->last_write_index == image_buffer_count)) ||
((decoding == DECODING_KEYFRAMES) && packet->keyframe) ||
((decoding == DECODING_KEYFRAMESONDEMAND) && (hasViewers() || packet->keyframe))
((decoding == DECODING_KEYFRAMES) && (packet->keyframe || decoder_requires_next_packet)) ||
((decoding == DECODING_KEYFRAMESONDEMAND) && (hasViewers() || packet->keyframe || decoder_requires_next_packet))
);
if (!should_decode && !decoder_queue.empty()) {
@@ -3256,10 +3265,21 @@ bool Monitor::Decode() {
}
if (should_decode) {
Debug(2, "Sending packet %d to decoder", packet->image_index);
Debug(2,
"Sending packet=%d to decoder "
"key=%d "
"flags=0x%x "
"pts=%lld "
"dts=%lld",
packet->image_index,
packet->keyframe,
packet->packet->flags,
(long long)packet->packet->pts,
(long long)packet->packet->dts
);
SystemTimePoint starttime = std::chrono::system_clock::now();
int ret = packet->send_packet(context);
Debug(2, "SEND RESULT packet=%d ret=%d", packet->image_index, ret);
SystemTimePoint endtime = std::chrono::system_clock::now();
// Warn if send_packet is taking too long
@@ -3285,6 +3305,7 @@ bool Monitor::Decode() {
// Success - packet sent to decoder, queue it for receive later
decoder_queue.push_back(std::move(packet_lock));
Debug(2, "QUEUE PUSH size=%zu", decoder_queue.size());
packetqueue.increment_it(decoder_it, false);
return true; // Frame will be received on a future call
}
@@ -3299,24 +3320,6 @@ bool Monitor::Decode() {
// between keyframe decodes.
if (packet->codec_type == AVMEDIA_TYPE_VIDEO) {
shared_data->last_write_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// Update last_write_index to point to the most recent keyframe
// even when the frame hasn't been fully decoded to the image buffer.
// This allows mode=single snapshots to always return the latest available
// image (a keyframe), while only decoding full frames when needed.
if ((decoding == DECODING_KEYFRAMES || decoding == DECODING_KEYFRAMESONDEMAND) && packet->keyframe) {
unsigned int kf_index = packet->image_index % image_buffer_count;
// Only update if this is a newer keyframe than what last_write_index currently points to
int current_write_index = shared_data->last_write_index % image_buffer_count;
if (kf_index != current_write_index) {
Debug(2, "Updating last_write_index to keyframe index %u (was %u, packet %d)",
kf_index, current_write_index, packet->image_index);
// Update timestamp to signal freshness
shared_timestamps[kf_index] = zm::chrono::duration_cast<timeval>(packet->timestamp.time_since_epoch());
// Update index as atomic final step
shared_data->last_write_index = kf_index;
}
}
}
}
@@ -3433,18 +3436,13 @@ bool Monitor::Decode() {
// Write to shared image buffer.
unsigned int index = (shared_data->last_write_index + 1) % image_buffer_count;
decoding_image_count++;
Debug(2, "SHM WRITE packet=%d index=%d", packet->image_index, packet->image_index % image_buffer_count);
WriteShmFrame(index, capture_image);
shared_timestamps[index] = zm::chrono::duration_cast<timeval>(packet->timestamp.time_since_epoch());
shared_data->signal = signal_check_points ? CheckSignal(capture_image) : true;
shared_data->last_write_index = index;
shared_data->last_write_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// NEW: Track last decoded keyframe for mode=single snapshots
if (packet->keyframe) {
last_keyframe_index = index;
Debug(2, "Updated last_keyframe_index to %d (packet %d)", index, packet->image_index);
}
// Warn if falling behind
auto lag = std::chrono::system_clock::now() - packet->timestamp;
if (lag > Seconds(ZM_WATCH_MAX_DELAY)) {

View File

@@ -1079,7 +1079,11 @@ class Monitor : public std::enable_shared_from_this<Monitor> {
int StartupDelay() const { return startup_delay; }
private:
int last_keyframe_index = -1; // Track which slot contains the last decoded keyframe
// True after a keyframe is sent until the decoder outputs the first frame.
// Used by keyframe-based decoding modes to feed any required follow-up packets.
// A future improvement could eliminate mode-specific checks by relying solely
// on this flag to track the decoder state.
bool decoder_requires_next_packet = false;
};
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))

View File

@@ -1009,18 +1009,9 @@ void MonitorStream::SingleImage(int scale) {
return;
}
// Use the most recent keyframe if available, otherwise use last_write_index
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
// Validate the timestamp to ensure it's a real frame
if (!monitor->shared_data->valid || !monitor->shared_timestamps[index].tv_sec) {
Debug(1, "Image slot %d is invalid or not yet decoded. Waiting...", index);
sendTextFrame("Image slot not ready");
return;
}
AVPixelFormat pixformat = monitor->image_pixelformats[index];
Debug(1, "Sending image index %d, pix format is %d %s", index, pixformat, zm_get_pix_fmt_name(pixformat));
Debug(1, "Sending regular image index %d, pix format is %d %s", index, pixformat, zm_get_pix_fmt_name(pixformat));
Image *snap_image = monitor->ReadShmFrame(index);
if (!config.timestamp_on_capture) {
monitor->TimestampImage(snap_image,