Skip to content

fix(mod_shout): update handle->pos in shout_file_read() to fix backward seek for MP3 files#2

Merged
pinc444 merged 2 commits intomasterfrom
copilot/fix-backward-seek-mp3
Mar 24, 2026
Merged

fix(mod_shout): update handle->pos in shout_file_read() to fix backward seek for MP3 files#2
pinc444 merged 2 commits intomasterfrom
copilot/fix-backward-seek-mp3

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 19, 2026

mod_shout's shout_file_read() never updated handle->pos, causing switch_core_file_seek() to always compute 0 - samps for backward seeks — clamping to position 0 instead of the correct relative position.

Change

Added handle->pos += *len in shout_file_read(), matching the pattern already used by mod_sndfile:

// src/mod/formats/mod_shout/mod_shout.c — shout_file_read()
handle->pos += *len;          // was missing; mod_sndfile has this
handle->sample_count += *len;

This ensures fh->pos accurately reflects playback position so that callers doing fh->pos - samps (backward seek via SEEK_SET) compute the correct target sample.

Original prompt

Bug

When using session.streamFile() with an MP3 file (handled by mod_shout), backward seek does not work correctly. The file always seeks to position 0 instead of the expected relative backward position.

WAV files work correctly because mod_sndfile properly tracks handle->pos in its file_read() function. mod_shout does not update handle->pos during reads, which breaks the seek logic in switch_core_file_seek().

Root Cause

In src/mod/formats/mod_shout/mod_shout.c, the function shout_file_read() increments handle->sample_count but never updates handle->pos.

Meanwhile, in src/switch_core_file.c, the switch_core_file_seek() function uses fh->offset_pos for SEEK_CUR operations:

if (whence == SWITCH_SEEK_CUR) {
    unsigned int cur = 0;
    if (switch_test_flag(fh, SWITCH_FILE_FLAG_WRITE)) {
        fh->file_interface->file_seek(fh, &cur, fh->samples_out, SEEK_SET);
    } else {
        fh->file_interface->file_seek(fh, &cur, fh->offset_pos, SEEK_SET);
    }
}

And fh->offset_pos is set from *cur_pos which comes from the file_seek return. But the first seek (to establish current position) relies on fh->offset_pos being correct. Since mod_shout never updates handle->pos (and offset_pos is derived from it after seeks), the position is always 0, making backward seeks always go to position 0.

For comparison, mod_sndfile in src/mod/formats/mod_sndfile/mod_sndfile.c correctly updates handle->pos in its read function:

static switch_status_t sndfile_file_read(switch_file_handle_t *handle, void *data, size_t *len)
{
    // ... read logic ...
    handle->pos += *len;        // <-- THIS IS WHAT mod_shout IS MISSING
    handle->sample_count += *len;
    return *len ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}

Fix Required

In src/mod/formats/mod_shout/mod_shout.c, find the shout_file_read() function. It currently has a line:

handle->sample_count += *len;

Add the following line immediately before that existing line:

handle->pos += *len;

So the result looks like:

handle->pos += *len;
handle->sample_count += *len;

This is the only change needed. It matches the pattern used by mod_sndfile and ensures that switch_core_file_seek() has the correct current position when computing relative seeks (both forward and backward via SEEK_CUR).

The following is the prior conversation context from the user's chat exploration (may be truncated):

Assistant:

# Generates a 2-minute WAV file with a voice announcing each second
# Uses .NET System.Speech (built into Windows)

Add-Type -AssemblyName System.Speech

$SAMPLE_RATE = 8000
$OUTPUT = Join-Path (Get-Location) "counting_seconds.wav"
$TEMP_DIR = Join-Path $env:TEMP "counting_$(Get-Random)"
New-Item -ItemType Directory -Path $TEMP_DIR -Force | Out-Null

$TOTAL_SECONDS = 120

Write-Host "Generating $TOTAL_SECONDS seconds of spoken counting audio..."
Write-Host ""

$synth = New-Object System.Speech.Synthesis.SpeechSynthesizer

for ($i = 0; $i -le $TOTAL_SECONDS; $i++) {
    $voiceFile = Join-Path $TEMP_DIR "voice_$($i.ToString('D4')).wav"
    $finalFile = Join-Path $TEMP_DIR "final_$($i.ToString('D4')).wav"

    # Generate spoken number to WAV
    $synth.SetOutputToWaveFile($voiceFile)
    $synth.Rate = 3  # faster speech so it fits in 1 second
    $synth.Speak("$i")
    $synth.SetOutputToNull()

    # Convert to 8kHz mono and pad/trim to exactly 1 second
    $soxArgs = @(
        "`"$voiceFile`""
        "-r", "$SAMPLE_RATE"
        "-c", "1"
        "`"$finalFile`""
        "pad", "0", "1.0"
        "trim", "0", "1.0"
    )
    Start-Process -FilePath "sox" -ArgumentList ($soxArgs -join " ") -NoNewWindow -Wait

    Write-Host "`r  Generated second $i / $TOTAL_SECONDS" -NoNewline
}

$synth.Dispose()

Write-Host ""
Write-Host "Combining all seconds into $OUTPUT..."

# Get all final files in order
$allFiles = (0..$TOTAL_SECONDS | ForEach-Object {
    "`"$(Join-Path $TEMP_DIR "final_$($_.ToString('D4')).wav")`""
}) -join " "

Start-Process -FilePath "sox" -ArgumentList "$allFiles `"$OUTPUT`"" -NoNewWindow -Wait

Write-Host ""
Write-Host "=========================================="
Write-Host "  FILE GENERATED: $OUTPUT"
Write-Host "=========================================="
Write-Host ""
Write-Host "  You will hear: 'zero' 'one' 'two' ... 'one hundred twenty'"
Write-Host "  Each number takes exactly 1 second."
Write-Host "  The number you hear = the second position in the file."
Write-Host ""
Write-Host "HOW TO TEST:"
Write-Host ""
Write-Host "  1. Play this file with your streamFile + onPlayFile callback"
Write-Host "  2. Wait until you hear 'sixty'"
Write-Host "  3. Press BACKWARD key (1) - seek:-15000"
Write-Host "     Should hear: 'forty-five' (60 - 15 = 45)"
Write-Host "     Bug: you hear 'zero' or 'one'"
Write-Host ""
Write-Host "  4. Reset. Wait until you hear 'sixty' again"
Write-Host ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

<!-- START COPILOT CODING AGENT TIPS -->
---

✨ Let Copilot coding agent [set things up for you](https://github.com/pinc444/freeswitch/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

… MP3 files

Co-authored-by: pinc444 <148037078+pinc444@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix backward seek issue with session.streamFile for MP3 fix(mod_shout): update handle->pos in shout_file_read() to fix backward seek for MP3 files Mar 19, 2026
Copilot AI requested a review from pinc444 March 19, 2026 16:23
@pinc444 pinc444 marked this pull request as ready for review March 24, 2026 21:26
@pinc444 pinc444 merged commit 84ce6d6 into master Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants