AlexxIT/go2rtc · error

mpegts: wrong sync byte

Error message

mpegts: wrong sync byte

What it means

MPEG-TS packets must start with the fixed sync byte 0x47. Demuxer.readPacketHeader reads the first byte of each 188-byte packet and returns this error immediately if it differs, meaning the byte stream is no longer packet-aligned or is not an MPEG-TS stream at all.

Solutions

  1. Verify the input actually is MPEG-TS: first byte should be 0x47 and packets recur every 188 bytes — check with `xxd file | head`.
  2. Restart reading from a stream boundary (start of file, start of HTTP response) so the demuxer begins on a packet boundary.
  3. Resynchronize the stream by scanning forward for the next 0x47 byte and re-aligning (or wrap the reader to skip until 0x47) before calling ReadPacket again.
  4. For unreliable transports (UDP/pipe), add loss handling upstream or use a depacketizer/resync wrapper, since a single lost byte desyncs all subsequent packets.

Example fix

// before: reading from an offset mid-packet
f.Seek(137, 0)
pid, start, err := demuxer.ReadPacket() // wrong sync byte

// after: re-align to the next 0x47 sync byte
f.Seek(137, 0)
b, _ := f.Peek(1)
if b[0] != 0x47 {
    br := bufio.NewReader(f)
    br.ReadByte() // scan to next 0x47
    demuxer.Reset(br)
}
pid, start, err := demuxer.ReadPacket()
Defensive patterns

Strategy: validation

Validate before calling

func isTSSynced(data []byte, off int) bool {
	// 0x47 at packet boundaries of 188 bytes confirms real MPEG-TS alignment
	for i := 0; i < 3; i++ {
		p := off + i*188
		if p >= len(data) || data[p] != 0x47 {
			return false
		}
	}
	return true
}
// scan data until isTSSynced returns true before handing the reader to the demuxer

Try / catch

if _, _, err := demuxer.ReadPacket(); err != nil && strings.Contains(err.Error(), "wrong sync byte") {
	log.Warn("mpegts: desync detected, re-scanning for 0x47 sync byte")
	rd = resyncReader(rd) // skip bytes until 0x47 with 188-byte repetition
	demuxer.Reset(rd)
	continue
}

Prevention

When it happens

Trigger: Calling ReadPacket on data that is not MPEG-TS (e.g. an MP4, PS, or FLV stream), on a stream with lost bytes mid-way (partial reads, HTTP response truncated), on a TS stream with a non-standard packet size where alignment drifts, or starting to read at a non-zero offset into the stream.

Common situations: HTTP live streams where the first response bytes are HTTP headers/garbage; recording pipelines reading from a resync-prone pipe; camera TS streams with dropped UDP packets causing desync; accidentally demuxing raw H.264 or an MPEG-Program-Stream; reading a file from a wrong offset after a seek.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/bff3ca8f82442394. Report an issue: GitHub.

Appendix: source

Thrown at pkg/mpegts/demuxer.go:74

					pkt.Payload = append(pkt.Payload, pes.StreamType)
				}
				return pkt, nil
			}
			continue
		}

		if pkt := d.readPES(pid, start); pkt != nil {
			return pkt, nil
		}
	}
}

func (d *Demuxer) readPacketHeader() (pid uint16, start bool, err error) {
	d.reset()

	sb := d.readByte() // Sync byte
	if sb != SyncByte {
		return 0, false, errors.New("mpegts: wrong sync byte")
	}

	_ = d.readBit()        // Transport error indicator (TEI)
	pusi := d.readBit()    // Payload unit start indicator (PUSI)
	_ = d.readBit()        // Transport priority
	pid = d.readBits16(13) // PID

	_ = d.readBits(2) // Transport scrambling control (TSC)
	af := d.readBit() // Adaptation field
	_ = d.readBit()   // Payload
	_ = d.readBits(4) // Continuity counter

	if af != 0 {
		adSize := d.readByte() // Adaptation field length
		if adSize > PacketSize-6 {
			return 0, false, errors.New("mpegts: wrong adaptation size")
		}
		d.skip(adSize)

View on GitHub (pinned to c245815e75)