From cc9bdb187edd5e2ff95d97bb4f7fcd07323325af Mon Sep 17 00:00:00 2001 From: IgorA100 Date: Sun, 19 Jul 2026 23:37:41 +0300 Subject: [PATCH] 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. --- src/zm_monitor.cpp | 56 +++++++++++++++++++--------------------- src/zm_monitor.h | 6 ++++- src/zm_monitorstream.cpp | 11 +------- 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index d43b4fae0..a63085e22 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -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(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(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)) { diff --git a/src/zm_monitor.h b/src/zm_monitor.h index c61d1e2a4..27af2aab2 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -1079,7 +1079,11 @@ class Monitor : public std::enable_shared_from_this { 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)) diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 711c26d65..3a79e9447 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -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,