From the 2026-07-03 comprehensive review.
ProResDecoder::seek rounds fps to an integer for the target timestamp: frameRateRational.den = (int)(m_frameRate + 0.5) (ProResDecoder.cpp:349) → 29.97→30, then av_rescale_q (:350). For 29.97/23.976 the timestamp drifts ~0.1%, growing with frame index. The intra-only path then asserts exactness without checking: m_currentFrame = frameNumber - 1; (:376) — decodeFrame's catch-up loop is a no-op, so it returns whatever av_seek_frame(BACKWARD) landed on, labeled as frameNumber, and caches it under the wrong key. frameNumberFromPts (:406) uses the same rounding, so the inter-frame resync drifts too.
Sequential playback is unaffected; scrubbing / loop / section-jump seeks on fractional-fps content show frame N±1 and pollute the frame cache with mislabeled entries.
Fix: use the exact stream rational for the seek timestamp and validate the landed PTS — HAPDecoder::seek (HAPDecoder.cpp:244) already does this correctly; match it. Consider folding into the ProResDecoder→FFmpegDecoder rename (#8) since the fix touches the same code.
From the 2026-07-03 comprehensive review.
ProResDecoder::seekrounds fps to an integer for the target timestamp:frameRateRational.den = (int)(m_frameRate + 0.5)(ProResDecoder.cpp:349) → 29.97→30, then av_rescale_q (:350). For 29.97/23.976 the timestamp drifts ~0.1%, growing with frame index. The intra-only path then asserts exactness without checking:m_currentFrame = frameNumber - 1;(:376) — decodeFrame's catch-up loop is a no-op, so it returns whatever av_seek_frame(BACKWARD) landed on, labeled as frameNumber, and caches it under the wrong key. frameNumberFromPts (:406) uses the same rounding, so the inter-frame resync drifts too.Sequential playback is unaffected; scrubbing / loop / section-jump seeks on fractional-fps content show frame N±1 and pollute the frame cache with mislabeled entries.
Fix: use the exact stream rational for the seek timestamp and validate the landed PTS — HAPDecoder::seek (HAPDecoder.cpp:244) already does this correctly; match it. Consider folding into the ProResDecoder→FFmpegDecoder rename (#8) since the fix touches the same code.