Streaming sample getters make one file read per sample
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 30/100
- Issue type
- Refactor
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- go
- Domain
- performance
Research direction
Start with the per-sample ReadData calls in mp4/stream.go near lines 383 and 448, then the lazy read path in mp4/mdat.go around lines 244-252, and the existing grouped route in appendSamples. Run the reproduction script in the issue to confirm the baseline of 240 reads for GetSamples and GetSampleRange against 4 for the Append variants. Done means the getters make one read per contiguous run with the same SHA-256 and sample count, while getter errors and independent sample buffers still behave as before.
Written by the indexing model from the issue text.
Description
Streaming callers using InitDecodeStream with an unbuffered file and SampleAccessor.GetSamples or GetSampleRange read each selected sample separately, even when the bytes are contiguous. This affects callers that repackage or refragment through these getters.
On the committed mp4/testdata/v300_multiple_segments.mp4 at d98f0d6, a local comparison of existing APIs, not a patch, gave:
| API | Sample-access os.File.Read calls |
Local complete-operation median |
|---|---|---|
GetSamples |
240 | 826.828 µs |
AppendSamples(nil, nil, trackID) |
4 | 211.483 µs |
The timing covers file open, stream decoding, extraction, building new fragments with AddFullSamples, encoding to io.Discard, and close: 15 interleaved pairs × 500 operations per arm. This includes allocation/layout differences, without append-buffer reuse. File handles were fresh, not the OS cache. Production impact is unmeasured.
GetSampleRange also made 240 reads; AppendSampleRange made four. All four returned 240 samples and 134,386 payload bytes with the same SHA-256 (below). Counts exclude parsing.
Likely cause: the getters call ReadData per sample (mp4/stream.go:383,448). Lazy ReadData seeks and creates a buffer (mp4/mdat.go:244–252); BoxSeekReader does not read ahead. The grouped route already exists in appendSamples. #594 explicitly left getter reimplementation for later.
Reproduce the read counts
From the repository root, save the following as /tmp/stream-samples.go and run go run /tmp/stream-samples.go. This opens the real fixture afresh for each API and counts reads during sample access.
package main
import (
"crypto/sha256"
"fmt"
"os"
"github.com/Eyevinn/mp4ff/mp4"
)
type countedFile struct {
*os.File
reads int
}
func (f *countedFile) Read(p []byte) (int, error) {
f.reads++
return f.File.Read(p)
}
func main() {
for _, mode := range []string{"GetSamples", "GetSampleRange", "AppendSamples", "AppendSampleRange"} {
f, err := os.Open("mp4/testdata/v300_multiple_segments.mp4")
if err != nil {
panic(err)
}
r := &countedFile{File: f}
h := sha256.New()
reads, count, size := 0, 0, 0
sf, err := mp4.InitDecodeStream(r, mp4.WithFragmentCallback(func(frag *mp4.Fragment, sa mp4.SampleAccessor) error {
for _, traf := range frag.Moof.Trafs {
id := traf.Tfhd.TrackID
var samples []mp4.FullSample
var err error
before := r.reads
switch mode {
case "GetSamples":
samples, err = sa.GetSamples(id)
case "GetSampleRange":
samples, err = sa.GetSampleRange(id, 1, ^uint32(0))
case "AppendSamples":
samples, _, err = sa.AppendSamples(nil, nil, id)
case "AppendSampleRange":
samples, _, err = sa.AppendSampleRange(nil, nil, id, 1, ^uint32(0))
}
reads += r.reads - before
if err != nil {
return err
}
for _, s := range samples {
count++
size += len(s.Data)
h.Write(s.Data)
}
}
return nil
}))
if err == nil {
err = sf.ProcessFragments()
}
closeErr := f.Close()
if err != nil {
panic(err)
}
if closeErr != nil {
panic(closeErr)
}
fmt.Printf("%s reads=%d samples=%d bytes=%d sha256=%x\n", mode, reads, count, size, h.Sum(nil))
}
}
Observed output:
GetSamples reads=240 samples=240 bytes=134386 sha256=3449633b656952612c827c0232a94ee21713a14c1460ca982bda9ef147c42a57
GetSampleRange reads=240 samples=240 bytes=134386 sha256=3449633b656952612c827c0232a94ee21713a14c1460ca982bda9ef147c42a57
AppendSamples reads=4 samples=240 bytes=134386 sha256=3449633b656952612c827c0232a94ee21713a14c1460ca982bda9ef147c42a57
AppendSampleRange reads=4 samples=240 bytes=134386 sha256=3449633b656952612c827c0232a94ee21713a14c1460ca982bda9ef147c42a57
The fixture has four fragments and four track runs. The desired improvement is four run reads instead of 240 sample reads on this input, while preserving getter errors and independent sample-buffer use. These counts do not establish behavior for cached rereads or failing inputs.
Would you like a pull request to coalesce contiguous getter reads while preserving those semantics?
Made by Perfloop. Human sponsor: Tomás Senart.
- Dominant language
- Go
- Stars
- 667
- Forks
- 132
- Avg merge
- 20h 4m
- Merged PRs (30d)
- 54
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Eyevinn/mp4ff
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 18/100
Maintainers usually reply within 1 day
-
CBCS encryption accepts an IV that readers do not receivePossibly taken @perfloop-agent claimed this 1 day ago. Open
Difficulty 4/5 3-5 days Newbie friendliness 52/100
Maintainers usually reply within 1 day
Similar issues
-
bug Web API
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
kubevela/terraform-controller#423 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
fosrl/windows-linux#91 ·
Maintainers usually reply within 1 day