fix(stream): fix black screen on remote/Tailscale streaming
Three root-cause fixes for VLC showing a black screen when opening a
stream from a different network or via Tailscale:
1. PrioritizeTail: when VLC opens an MKV/MP4 stream it immediately seeks
to the end of the file to read the container index (seekhead/moov
atom). For active torrents those end-pieces aren't downloaded yet, so
the reader blocks indefinitely. PrioritizeTail() opens a background
reader positioned at the last 5 MB, keeping those pieces at high
priority until ctx is cancelled or they finish downloading.
2. /health endpoint: GET /health returns a lightweight JSON response
{"status":"ok","streaming":bool,...} so connectivity can be tested
with a simple curl from any device before involving VLC.
3. Per-request logging: every incoming /stream request now logs the
client IP and Range header, making it trivial to confirm whether
remote/Tailscale clients are reaching the server at all.
This commit is contained in:
parent
7eaf357680
commit
f1b4f2e327
5 changed files with 185 additions and 0 deletions
|
|
@ -303,6 +303,38 @@ func (s *StreamEngine) FileSize() int64 { return s.totalBytes }
|
|||
// BufferTarget returns the buffer threshold in bytes.
|
||||
func (s *StreamEngine) BufferTarget() int64 { return s.bufferTarget }
|
||||
|
||||
// PrioritizeTail abre un lector posicionado cerca del final del archivo para
|
||||
// forzar la descarga anticipada de los metadatos del container (moov atom en
|
||||
// MP4, seekhead en MKV). Sin esto, VLC busca el final del archivo al abrirlo
|
||||
// y el lector bloquea indefinidamente si esas piezas aún no están descargadas,
|
||||
// resultando en pantalla negra en redes lentas o remotas.
|
||||
//
|
||||
// Se ejecuta en una goroutine y se cancela cuando ctx expira.
|
||||
func (s *StreamEngine) PrioritizeTail(ctx context.Context, tailBytes int64) {
|
||||
if s.file == nil || s.totalBytes <= tailBytes*2 {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
reader := s.file.NewReader()
|
||||
defer reader.Close()
|
||||
|
||||
seekPos := s.totalBytes - tailBytes
|
||||
reader.Seek(seekPos, io.SeekStart) //nolint:errcheck
|
||||
reader.SetReadahead(tailBytes)
|
||||
reader.SetContext(ctx)
|
||||
|
||||
// Leer continuamente para mantener las piezas priorizadas hasta que
|
||||
// ctx se cancele o el final del archivo esté completamente descargado.
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
_, err := reader.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Shutdown gracefully closes the torrent and client.
|
||||
func (s *StreamEngine) Shutdown(_ context.Context) error {
|
||||
if s.tor != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue