fix: count repeated frame steps and retain the forward decoder cursor

This commit is contained in:
2026-10-10 18:35:13 +02:00
parent 2bb7d9b4ee
commit 131acbe41b
6 changed files with 130 additions and 21 deletions
+40
View File
@@ -80,6 +80,45 @@ size_t firstActive(const float *samples, size_t frames, float threshold = .002f)
require(first < frames, "tone is present");
return first;
}
void playStepped(const std::string &path, const std::vector<float> &reference) {
capturedFrames = 0;
std::unique_ptr<Player, decltype(&player_destroy)> player(player_create(), player_destroy);
require(bool(player), "create stepped audio player");
auto wait = [&](auto ready) {
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(12);
while (std::chrono::steady_clock::now() < deadline) {
auto state = player_get_state(player.get());
require(state.status != PLAYER_ERROR, state.error);
if (ready(state)) return state;
std::this_thread::sleep_for(std::chrono::milliseconds(2));
}
require(false, "stepped audio operation completes");
return PlayerState{};
};
require(player_open(player.get(), path.c_str()), "open video with PCM audio");
wait([](auto s) { return s.status == PLAYER_PAUSED; });
require(player_step_frame(player.get(), -1), "establish first frame");
wait([](auto s) { return !s.seek_pending; });
for (int i = 0; i < 32; ++i) require(player_step_frame(player.get(), 1), "queue forward audio frame steps");
for (int i = 0; i < 19; ++i) require(player_step_frame(player.get(), -1), "queue backward audio frame steps");
auto selected = wait([](auto s) { return !s.seek_pending; });
require(std::abs(selected.position - 13.0 / 24) < .0001, "resume starts at selected video frame");
require(capturedFrames == 0, "frame stepping never starts audio output");
require(player_play(player.get()), "resume audio after repeated steps");
auto ended = wait([](auto s) { return s.status == PLAYER_ENDED; });
require(std::abs(ended.position - 2) < .0001, "stepped audio reaches original media end");
player.reset(); // Stop/join before inspecting callback-owned storage.
constexpr size_t sourceStart = 26000; // 13 / 24 seconds at 48 kHz.
const size_t remaining = reference.size() / 2 - sourceStart;
const size_t offset = firstActive(capture.data(), capturedFrames);
require(capturedFrames >= offset + remaining, "all remaining PCM reaches the callback");
float maximumError = 0;
for (size_t i = 0; i < remaining * 2; ++i)
maximumError = std::max(maximumError,
std::abs(capture[offset * 2 + i] - reference[sourceStart * 2 + i]));
std::printf("stepped resume: max remaining PCM error %.8f\n", maximumError);
require(maximumError < .00003f, "resume audio matches selected-frame suffix, without stale or dropped samples");
}
}
// Linked only into the check build. No allocation, locks, or file I/O in callback.
@@ -115,6 +154,7 @@ int main(int argc, char **argv) {
std::printf("%s: max sample error %.8f\n", name, maximumError);
require(maximumError < .00003f, "continuous callback PCM matches reference (no inserted/dropped samples)");
if (std::string(name) == "tone480.wav") {
playStepped(directory + "/tone-step.mov", reference);
play(path, .25f);
size_t quietStart = firstActive(capture.data(), capturedFrames, .002f * .25f);
require(quietStart >= referenceStart, "fractional-volume alignment");