### Install go-astits Library Source: https://github.com/asticode/go-astits/blob/master/README.md Install the go-astits library using the go get command. This command fetches and installs the library and its dependencies. ```bash go get -u github.com/asticode/go-astits/... ``` -------------------------------- ### Install go-astits Executables Source: https://github.com/asticode/go-astits/blob/master/README.md Install the command-line executables provided by the go-astits library. These will be placed in your GOPATH/bin directory. ```bash go install github.com/asticode/go-astits/cmd ``` -------------------------------- ### Configure Demuxer with Options in Go Source: https://github.com/asticode/go-astits/blob/master/README.md Demonstrates how to create a demuxer with custom options in Go. This example shows setting the packet size and providing a custom packets parser function. ```go // This is your custom packets parser p := func(ps []*astits.Packet) (ds []*astits.Data, skip bool, err error) { // This is your logic skip = true return } // Now you can create a demuxer with the proper options dmx := NewDemuxer(ctx, f, DemuxerOptPacketSize(192), DemuxerOptPacketsParser(p)) ``` -------------------------------- ### Mux MPEG Transport Stream in Go Source: https://github.com/asticode/go-astits/blob/master/README.md Example of how to mux an MPEG Transport Stream using the go-astits library. It shows creating a muxer, adding elementary streams, writing tables, and writing data. Context cancellation and signal handling are included. Error handling is omitted for readability. ```go // Create a cancellable context in case you want to stop writing packets/data any time you want ctx, cancel := context.WithCancel(context.Background()) // Handle SIGTERM signal ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM) go func() { <-ch cancel() }() // Create your file or initialize any kind of io.Writer // Buffering using bufio.Writer is recommended for performance f, _ := os.Create("/path/to/file.ts") defer f.Close() // Create the muxer mx := astits.NewMuxer(ctx, f) // Add an elementary stream mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 1, StreamType: astits.StreamTypeMetadata, }) // Write tables // Using that function is not mandatory, WriteData will retransmit tables from time to time mx.WriteTables() // Write data mx.WriteData(&astits.MuxerData{ PES: &astits.PESData{ Data: []byte("test"), }, PID: 1, }) ``` -------------------------------- ### Demux MPEG Transport Stream in Go Source: https://github.com/asticode/go-astits/blob/master/README.md Example of how to demux an MPEG Transport Stream using the go-astits library. It demonstrates creating a demuxer, handling context cancellation, and processing PMT data to detect elementary streams. Error handling is omitted for brevity but is crucial in production. ```go // Create a cancellable context in case you want to stop reading packets/data any time you want ctx, cancel := context.WithCancel(context.Background()) // Handle SIGTERM signal ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM) go func() { <-ch cancel() }() // Open your file or initialize any kind of io.Reader // Buffering using bufio.Reader is recommended for performance f, _ := os.Open("/path/to/file.ts") defer f.Close() // Create the demuxer dmx := astits.NewDemuxer(ctx, f) for { // Get the next data d, _ := dmx.NextData() // Data is a PMT data if d.PMT != nil { // Loop through elementary streams for _, es := range d.PMT.ElementaryStreams { fmt.Printf("Stream detected: %d\n", es.ElementaryPID) } return } } ``` -------------------------------- ### CLI: Inspect Transport Streams with astits-probe Source: https://context7.com/asticode/go-astits/llms.txt Provides examples of using the `astits-probe` command-line tool to inspect transport streams. Supports listing programs, raw packets, and specific data types in text or JSON format. Can read from files or UDP multicast addresses. ```bash # List programs and streams (default text format) astits-probe -i broadcast.ts # List programs as JSON astits-probe -i broadcast.ts -f json # List all raw packets astits-probe packets -i broadcast.ts # List all data types astits-probe data -i broadcast.ts -d all # List only EIT and PMT data astits-probe data -i broadcast.ts -d eit -d pmt # Read from a UDP multicast source astits-probe -i udp://239.255.0.1:1234 # Example JSON output fragment: # [ # { # "id": 1, # "map_id": 4096, # "streams": [ # {"id": 256, "type": 27}, # {"id": 257, "type": 15} # ] # } # ] ``` -------------------------------- ### Reset Demuxer to Stream Start with Demuxer.Rewind Source: https://context7.com/asticode/go-astits/llms.txt Resets the demuxer's internal buffers and seeks the underlying reader to the beginning of the stream. Requires the reader to implement `io.Seeker`. ```go f, _ := os.Open("broadcast.ts") defer f.Close() dmx := astits.NewDemuxer(ctx, f) // First pass – collect PAT/PMT for { d, err := dmx.NextData(); if err != nil || d.PMT != nil { break } } // Rewind and do a second pass if _, err := dmx.Rewind(); err != nil { log.Fatal(err) } // Second pass – process PES packets for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if d.PES != nil { /* … */ } } ``` -------------------------------- ### Force PAT+PMT Emission Source: https://context7.com/asticode/go-astits/llms.txt Immediately writes the current PAT and PMT packets to the output stream regardless of the retransmission period counter. Useful at stream start or after a programme change to ensure decoders receive updated tables as quickly as possible. ```go if _, err := mx.WriteTables(); err != nil { log.Fatalf("WriteTables: %v", err) } ``` -------------------------------- ### Demuxer.Rewind Source: https://context7.com/asticode/go-astits/llms.txt Resets all internal buffers and packet pools, then seeks the underlying reader back to offset 0. This requires the reader to implement `io.Seeker`. After rewinding, iteration via `NextPacket` or `NextData` starts from the beginning of the stream. ```APIDOC ## Demuxer.Rewind — Reset demuxer to stream start Resets all internal buffers and packet pools, then seeks the underlying reader back to offset 0 (requires the reader to implement `io.Seeker`). After rewinding, iteration via `NextPacket` or `NextData` starts from the beginning of the stream. ```go f, _ := os.Open("broadcast.ts") defer f.Close() dmx := astits.NewDemuxer(ctx, f) // First pass – collect PAT/PMT for { d, err := dmx.NextData(); if err != nil || d.PMT != nil { break } } // Rewind and do a second pass if _, err := dmx.Rewind(); err != nil { log.Fatal(err) } // Second pass – process PES packets for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if d.PES != nil { /* … */ } } ``` ``` -------------------------------- ### Handle Well-known PID Constants Source: https://context7.com/asticode/go-astits/llms.txt Illustrates checking for the PID of a null packet to skip padding. Pre-defined PID constants for fixed/reserved tables are available. ```go // astits.PIDPAT = 0x0000 – Program Association Table // astits.PIDCAT = 0x0001 – Conditional Access Table // astits.PIDTSDT = 0x0002 – Transport Stream Description Table // astits.PIDNull = 0x1FFF – Null padding packets if pkt.Header.PID == astits.PIDNull { // skip null padding continue } ``` -------------------------------- ### Construct and Use ClockReference Source: https://context7.com/asticode/go-astits/llms.txt Demonstrates constructing a ClockReference with a specific duration and reading PCR from an adaptation field. Ensure the packet has an adaptation field and PCR data. ```go // Construct a 5-second PTS on the 90 kHz clock pts := &astits.ClockReference{Base: 5 * 90000} fmt.Println(pts.Duration()) // 5s // Read a PCR from an adaptation field if pkt.AdaptationField != nil && pkt.AdaptationField.HasPCR { pcr := pkt.AdaptationField.PCR fmt.Printf("PCR: base=%d ext=%d time=%s\n", pcr.Base, pcr.Extension, pcr.Duration()) } ``` -------------------------------- ### Probe streams with astits-probe Source: https://github.com/asticode/go-astits/blob/master/README.md Use astits-probe to list streams from a file. Specify the output format as text or JSON. ```bash astits-probe -i -f ``` -------------------------------- ### Probe packets with astits-probe Source: https://github.com/asticode/go-astits/blob/master/README.md Use astits-probe packets to list all packets from a file. ```bash astits-probe packets -i ``` -------------------------------- ### Inspect DemuxerData and Construct MuxerData Source: https://context7.com/asticode/go-astits/llms.txt Shows how to inspect DemuxerData for PES packets, including PTS and DTS, and how to construct MuxerData for writing. Ensure the PES header and optional header are correctly populated. ```go // DemuxerData inspection pattern d, _ := dmx.NextData() if d.PES != nil { oh := d.PES.Header.OptionalHeader if oh != nil { if oh.PTS != nil { fmt.Println("PTS:", oh.PTS.Duration()) } if oh.DTS != nil { fmt.Println("DTS:", oh.DTS.Duration()) } } fmt.Printf("payload %d bytes\n", len(d.PES.Data)) } // MuxerData construction md := &astits.MuxerData{ PID: 0x0101, PES: &astits.PESData{ Header: &astits.PESHeader{ OptionalHeader: &astits.PESOptionalHeader{ PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS, PTS: &astits.ClockReference{Base: 180000}, }, }, Data: aacFrameBytes, }, } mx.WriteData(md) ``` -------------------------------- ### Attach a logger to the demuxer Source: https://context7.com/asticode/go-astits/llms.txt Connects a standard *log.Logger (or any astikit.StdLogger-compatible implementation) to the demuxer so that internal warnings and non-fatal errors are surfaced rather than silently dropped. ```go dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptLogger(log.Default())) ``` -------------------------------- ### Probe data with astits-probe Source: https://github.com/asticode/go-astits/blob/master/README.md Use astits-probe data to list specific data types (e.g., EIT, NIT) from a file. If no data type is specified, all data types are shown. ```bash astits-probe data -i -d ``` -------------------------------- ### Create a transport stream demuxer Source: https://context7.com/asticode/go-astits/llms.txt Creates a Demuxer from an io.Reader. Use bufio.Reader for better performance on file sources. Handles context cancellation for graceful shutdown. ```go package main import ( "bufio" "context" "fmt" "log" "os" "os/signal" "syscall" "github.com/asticode/go-astits" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) go func() { <-ch; cancel() }() f, err := os.Open("broadcast.ts") if err != nil { log.Fatal(err) } defer f.Close() // bufio.Reader improves read performance dmx := astits.NewDemuxer(ctx, bufio.NewReader(f)) for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatal(err) } fmt.Printf("PID %d\n", d.PID) } } ``` -------------------------------- ### Handle Sentinel Errors Source: https://context7.com/asticode/go-astits/llms.txt Demonstrates using errors.Is to check for specific sentinel errors returned by library functions. This pattern is used for NextData, NextPacket, AddElementaryStream, RemoveElementaryStream, and WriteTables. ```go d, err := dmx.NextData() switch { case errors.Is(err, astits.ErrNoMorePackets): fmt.Println("stream exhausted") case errors.Is(err, astits.ErrPacketMustStartWithASyncByte): fmt.Println("sync lost – corrupted stream") case err != nil: log.Fatal(err) } err = mx.AddElementaryStream(astits.PMTElementaryStream{ElementaryPID: 0x0100, StreamType: astits.StreamTypeH264Video}) if errors.Is(err, astits.ErrPIDAlreadyExists) { fmt.Println("PID already registered") } ``` -------------------------------- ### NewMuxer Source: https://context7.com/asticode/go-astits/llms.txt Creates a `Muxer` that writes TS packets to any `io.Writer`. PAT and PMT are generated automatically. The PMT is retransmitted every 40 PES packets by default, which can be configured via `MuxerOptTablesRetransmitPeriod`. Using `bufio.Writer` wrapping is recommended for better throughput on file output. ```APIDOC ## NewMuxer — Create a transport stream muxer Creates a `Muxer` that writes TS packets to any `io.Writer`. PAT and PMT are generated automatically; the PMT is retransmitted every 40 PES packets by default (configurable via `MuxerOptTablesRetransmitPeriod`). Use `bufio.Writer` wrapping for better throughput on file output. ```go package main import ( "bufio" "context" "log" "os" "github.com/asticode/go-astits" ) func main() { ctx := context.Background() out, err := os.Create("output.ts") if err != nil { log.Fatal(err) } defer out.Close() mx := astits.NewMuxer(ctx, bufio.NewWriter(out), astits.WithTransportStreamID(1), astits.MuxerOptTablesRetransmitPeriod(20), ) // Register a video stream at PID 0x0100 if err = mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 0x0100, StreamType: astits.StreamTypeH264Video, }); err != nil { log.Fatal(err) } // PCR is carried on the video PID mx.SetPCRPID(0x0100) // Emit PAT+PMT immediately before the first data packet if _, err = mx.WriteTables(); err != nil { log.Fatal(err) } log.Println("Muxer ready") } ``` ``` -------------------------------- ### Split TS file into per-stream files with astits-es-split Source: https://context7.com/asticode/go-astits/llms.txt Use `astits-es-split` to demux a transport stream and write each elementary stream to a separate file. Output files are named after their PID. PAT/PMT are re-generated for each output file. Use `--discard` for a dry-run without writing files. ```bash # Split broadcast.ts; write output files to ./out/ astits-es-split broadcast.ts -o ./out/ # Expected output: # 2024/01/01 12:00:00 Got all PMTs # 2024/01/01 12:00:00 Program 1 PCR PID 256 # 2024/01/01 12:00:00 ES PID 256 type H264 Video # 2024/01/01 12:00:00 ES PID 257 type AAC Audio # 2024/01/01 12:00:01 1048576 bytes written at rate 12.34 mb/s # 2024/01/01 12:00:01 Done # Files created: out/256.ts out/257.ts # Dry-run / benchmark without writing files astits-es-split broadcast.ts --discard ``` -------------------------------- ### Plug in a custom payload parser Source: https://context7.com/asticode/go-astits/llms.txt Registers a PacketsParser callback that receives the raw []*Packet slice for every reassembled payload unit. Returning skip = true bypasses the built-in PSI/PES parsing, giving full control over proprietary or private PID payloads. ```go // Intercept PID 0x0200 and decode a proprietary binary format customParser := func(ps []*astits.Packet) (ds []*astits.DemuxerData, skip bool, err error) { if len(ps) > 0 && ps[0].Header.PID == 0x0200 { // Build payload manually var payload []byte for _, p := range ps { payload = append(payload, p.Payload...) } fmt.Printf("Custom PID 0x0200 payload: %d bytes\n", len(payload)) skip = true // don't run default parser return } return // skip=false → default parser continues } dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptPacketsParser(customParser)) ``` -------------------------------- ### Create Transport Stream Muxer with NewMuxer Source: https://context7.com/asticode/go-astits/llms.txt Creates a `Muxer` to write TS packets to an `io.Writer`. PAT and PMT are generated automatically and retransmitted periodically. Uses `bufio.Writer` for better throughput. ```go package main import ( "bufio" "context" "log" "os" "github.com/asticode/go-astits" ) func main() { ctx := context.Background() out, err := os.Create("output.ts") if err != nil { log.Fatal(err) } defer out.Close() mx := astits.NewMuxer(ctx, bufio.NewWriter(out), astits.WithTransportStreamID(1), astits.MuxerOptTablesRetransmitPeriod(20), ) // Register a video stream at PID 0x0100 if err = mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 0x0100, StreamType: astits.StreamTypeH264Video, }); err != nil { log.Fatal(err) } // PCR is carried on the video PID mx.SetPCRPID(0x0100) // Emit PAT+PMT immediately before the first data packet if _, err = mx.WriteTables(); err != nil { log.Fatal(err) } log.Println("Muxer ready") } ``` -------------------------------- ### DemuxerOptLogger — Attach a logger Source: https://context7.com/asticode/go-astits/llms.txt Connects a standard `*log.Logger` (or any `astikit.StdLogger`-compatible implementation) to the demuxer so that internal warnings and non-fatal errors are surfaced rather than silently dropped. ```APIDOC ## DemuxerOptLogger ### Description Connects a standard `*log.Logger` (or any `astikit.StdLogger`-compatible implementation) to the demuxer so that internal warnings and non-fatal errors are surfaced rather than silently dropped. ### Method Signature `func DemuxerOptLogger(l astikit.StdLogger) DemuxerOpt` ### Parameters - `l` (astikit.StdLogger) - The logger to use. ### Example Usage ```go dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptLogger(log.Default())) ``` ``` -------------------------------- ### Split streams with astits-es-split Source: https://github.com/asticode/go-astits/blob/master/README.md Use astits-es-split to split streams from a file into separate .ts files in the specified output directory. ```bash astits-es-split -o ``` -------------------------------- ### Classify and Convert Stream Types Source: https://context7.com/asticode/go-astits/llms.txt Iterates through elementary streams in PMT data, printing their PIDs and stream types. Uses helper methods to determine if a stream is video or audio and to convert to PES stream IDs. Supported constants are listed for reference. ```go for _, es := range pmtData.ElementaryStreams { fmt.Printf("PID 0x%04x: %s", es.ElementaryPID, es.StreamType.String()) switch { case es.StreamType.IsVideo(): fmt.Println(" [video]") case es.StreamType.IsAudio(): fmt.Println(" [audio]") default: fmt.Println(" [other]") } // Auto-map to PES stream ID fmt.Printf(" PES stream ID: 0x%02x\n", es.StreamType.ToPESStreamID()) } // Supported constants (sample): // astits.StreamTypeH264Video (0x1B) // astits.StreamTypeH265Video (0x24) // astits.StreamTypeAACAudio (0x0F) // astits.StreamTypeAC3Audio (0x81) // astits.StreamTypeMetadata (0x15) // astits.StreamTypeSCTE35 (0x86) ``` -------------------------------- ### NewDemuxer — Create a transport stream demuxer Source: https://context7.com/asticode/go-astits/llms.txt Creates a Demuxer from any io.Reader. Optional functional options can configure packet size, logging, custom packet parsers, and packet skippers. It's recommended to use bufio.Reader for better I/O performance on file sources. ```APIDOC ## NewDemuxer ### Description Creates a `Demuxer` from any `io.Reader`. Optional functional options configure packet size, logging, custom packet parsers, and packet skippers. Use `bufio.Reader` wrapping for better I/O performance on file sources. ### Method Signature `func NewDemuxer(ctx context.Context, r io.Reader, opts ...DemuxerOpt) *Demuxer` ### Parameters - `ctx` (context.Context) - Context for cancellation. - `r` (io.Reader) - The input source for the transport stream. - `opts` ([]DemuxerOpt) - Optional functional options to configure the demuxer. ### Example Usage ```go package main import ( "bufio" "context" "fmt" "log" "os" "os/signal" "syscall" "github.com/asticode/go-astits" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) go func() { <-ch; cancel() }() f, err := os.Open("broadcast.ts") if err != nil { log.Fatal(err) } defer f.Close() // bufio.Reader improves read performance dmx := astits.NewDemuxer(ctx, bufio.NewReader(f)) for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatal(err) } fmt.Printf("PID %d\n", d.PID) } } ``` ``` -------------------------------- ### Read Reassembled Data Units with Demuxer.NextData Source: https://context7.com/asticode/go-astits/llms.txt Reassembles payloads into data units and identifies them as PAT, PMT, PES, EIT, SDT, or TOT. Updates internal program maps. ```go for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatalf("NextData: %v", err) } switch { case d.PAT != nil: fmt.Printf("PAT: transport stream ID=%d, %d program(s)\n", d.PAT.TransportStreamID, len(d.PAT.Programs)) for _, p := range d.PAT.Programs { fmt.Printf(" program %d → map PID 0x%04x\n", p.ProgramNumber, p.ProgramMapID) } case d.PMT != nil: fmt.Printf("PMT: program=%d PCR PID=0x%04x\n", d.PMT.ProgramNumber, d.PMT.PCRPID) for _, es := range d.PMT.ElementaryStreams { fmt.Printf(" ES PID=0x%04x type=%s (video=%v audio=%v)\n", es.ElementaryPID, es.StreamType, es.StreamType.IsVideo(), es.StreamType.IsAudio()) } case d.PES != nil: oh := d.PES.Header.OptionalHeader if oh != nil && oh.PTS != nil { fmt.Printf("PES PID=%d stream=0x%02x pts=%s len=%d\n", d.PID, d.PES.Header.StreamID, oh.PTS.Duration(), len(d.PES.Data)) } case d.EIT != nil: fmt.Printf("EIT: service=%d events=%d\n", d.EIT.ServiceID, len(d.EIT.Events)) for _, ev := range d.EIT.Events { fmt.Printf(" event %d start=%s dur=%s status=%d\n", ev.EventID, ev.StartTime.Format("15:04:05"), ev.Duration, ev.RunningStatus) } case d.SDT != nil: fmt.Printf("SDT: %d service(s)\n", len(d.SDT.Services)) for _, svc := range d.SDT.Services { fmt.Printf(" service %d running=%d\n", svc.ServiceID, svc.RunningStatus) } case d.TOT != nil: fmt.Printf("TOT: UTC time=%s\n", d.TOT.UTCTime) } } ``` -------------------------------- ### Read Raw TS Packets with Demuxer.NextPacket Source: https://context7.com/asticode/go-astits/llms.txt Iterates through raw TS packets, extracting header information and PCR if available. Useful for low-level inspection. ```go for { pkt, err := dmx.NextPacket() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatal(err) } h := pkt.Header fmt.Printf("PID=%d CC=%d PUSI=%v HasAF=%v HasPayload=%v\n", h.PID, h.ContinuityCounter, h.PayloadUnitStartIndicator, h.HasAdaptationField, h.HasPayload) if h.HasAdaptationField && pkt.AdaptationField.HasPCR { pcr := pkt.AdaptationField.PCR fmt.Printf(" PCR base=%d ext=%d => %s\n", pcr.Base, pcr.Extension, pcr.Duration()) } } ``` -------------------------------- ### Write Raw TS Packet Source: https://context7.com/asticode/go-astits/llms.txt Writes a pre-built *Packet directly to the output, stuffing any remaining bytes to reach MpegTsPacketSize (188). Useful for passthrough scenarios where packets are re-stamped and forwarded without reassembly. ```go pkt := &astits.Packet{ Header: astits.PacketHeader{ HasPayload: true, PayloadUnitStartIndicator: true, PID: 0x0100, ContinuityCounter: 3, }, Payload: someBytes, } n, err := mx.WritePacket(pkt) if err != nil { log.Fatal(err) } ``` -------------------------------- ### Demuxer.NextData Source: https://context7.com/asticode/go-astits/llms.txt Reads the next reassembled data unit by reassembling payloads spanning multiple packets. Returns a `*DemuxerData` with typed fields (PAT, PMT, PES, EIT, NIT, SDT, TOT) that are non-nil according to the PID type. The demuxer automatically updates its internal program map as PAT/PMT tables arrive. ```APIDOC ## Demuxer.NextData — Read the next reassembled data unit Reassembles payloads spanning multiple packets and returns a `*DemuxerData` whose typed fields (`PAT`, `PMT`, `PES`, `EIT`, `NIT`, `SDT`, `TOT`) are non-nil according to the PID type. The demuxer also keeps the internal program map up to date as PAT/PMT tables arrive. ```go for { d, err := dmx.NextData() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatalf("NextData: %v", err) } switch { case d.PAT != nil: fmt.Printf("PAT: transport stream ID=%d, %d program(s)\n", d.PAT.TransportStreamID, len(d.PAT.Programs)) for _, p := range d.PAT.Programs { fmt.Printf(" program %d → map PID 0x%04x\n", p.ProgramNumber, p.ProgramMapID) } case d.PMT != nil: fmt.Printf("PMT: program=%d PCR PID=0x%04x\n", d.PMT.ProgramNumber, d.PMT.PCRPID) for _, es := range d.PMT.ElementaryStreams { fmt.Printf(" ES PID=0x%04x type=%s (video=%v audio=%v)\n", es.ElementaryPID, es.StreamType, es.StreamType.IsVideo(), es.StreamType.IsAudio()) } case d.PES != nil: oh := d.PES.Header.OptionalHeader if oh != nil && oh.PTS != nil { fmt.Printf("PES PID=%d stream=0x%02x pts=%s len=%d\n", d.PID, d.PES.Header.StreamID, oh.PTS.Duration(), len(d.PES.Data)) } case d.EIT != nil: fmt.Printf("EIT: service=%d events=%d\n", d.EIT.ServiceID, len(d.EIT.Events)) for _, ev := range d.EIT.Events { fmt.Printf(" event %d start=%s dur=%s status=%d\n", ev.EventID, ev.StartTime.Format("15:04:05"), ev.Duration, ev.RunningStatus) } case d.SDT != nil: fmt.Printf("SDT: %d service(s)\n", len(d.SDT.Services)) for _, svc := range d.SDT.Services { fmt.Printf(" service %d running=%d\n", svc.ServiceID, svc.RunningStatus) } case d.TOT != nil: fmt.Printf("TOT: UTC time=%s\n", d.TOT.UTCTime) } } ``` ``` -------------------------------- ### Set non-standard packet size for M2TS files Source: https://context7.com/asticode/go-astits/llms.txt Overrides automatic packet-size detection. Use 192 for M2TS (Blu-ray) files that prepend a 4-byte timestamp to each 188-byte packet. ```go // Parse a 192-byte M2TS file dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptPacketSize(192)) ``` -------------------------------- ### DemuxerOptPacketsParser — Plug in a custom payload parser Source: https://context7.com/asticode/go-astits/llms.txt Registers a `PacketsParser` callback that receives the raw `[]*Packet` slice for every reassembled payload unit. Returning `skip = true` bypasses the built-in PSI/PES parsing, giving full control over proprietary or private PID payloads. ```APIDOC ## DemuxerOptPacketsParser ### Description Registers a `PacketsParser` callback that receives the raw `[]*Packet` slice for every reassembled payload unit. Returning `skip = true` bypasses the built-in PSI/PES parsing, giving full control over proprietary or private PID payloads. ### Method Signature `func DemuxerOptPacketsParser(pp PacketsParser) DemuxerOpt` ### Parameters - `pp` (PacketsParser) - The custom packets parser callback. - `type PacketsParser func(ps []*Packet) (ds []*DemuxerData, skip bool, err error)` ### Example Usage ```go // Intercept PID 0x0200 and decode a proprietary binary format customParser := func(ps []*astits.Packet) (ds []*astits.DemuxerData, skip bool, err error) { if len(ps) > 0 && ps[0].Header.PID == 0x0200 { // Build payload manually var payload []byte for _, p := range ps { payload = append(payload, p.Payload...) } fmt.Printf("Custom PID 0x0200 payload: %d bytes\n", len(payload)) skip = true // don't run default parser return } return // skip=false → default parser continues } dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptPacketsParser(customParser)) ``` ``` -------------------------------- ### Muxer.WritePacket Source: https://context7.com/asticode/go-astits/llms.txt Writes a pre-built raw MPEG-TS packet directly to the output, padding with stuffing bytes if necessary to reach 188 bytes. This is useful for passthrough scenarios where packets are re-stamped and forwarded without reassembly. ```APIDOC ## Muxer.WritePacket — Write a raw TS packet Writes a pre-built `*Packet` directly to the output, stuffing any remaining bytes to reach `MpegTsPacketSize` (188). Useful for passthrough scenarios where packets are re-stamped and forwarded without reassembly. ```go pkt := &astits.Packet{ Header: astits.PacketHeader{ HasPayload: true, PayloadUnitStartIndicator: true, PID: 0x0100, ContinuityCounter: 3, }, Payload: someBytes, } n, err := mx.WritePacket(pkt) if err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### Muxer.AddElementaryStream Source: https://context7.com/asticode/go-astits/llms.txt Registers an elementary stream with the PMT. If ElementaryPID is 0, it's assigned automatically. Returns ErrPIDAlreadyExists if the PID is already in use. This must be called before the first WriteData for that PID. ```APIDOC ## Muxer.AddElementaryStream — Register an elementary stream Adds a `PMTElementaryStream` to the PMT. If `ElementaryPID` is 0 it is assigned automatically starting at `0x0100`. Returns `ErrPIDAlreadyExists` if the PID is already registered. Must be called before the first `WriteData` for that PID. ```go mx := astits.NewMuxer(ctx, w) // Add an H.264 video stream on auto-assigned PID if err := mx.AddElementaryStream(astits.PMTElementaryStream{ StreamType: astits.StreamTypeH264Video, }); err != nil { log.Fatal(err) } // Add an AAC audio stream on PID 0x0101 if err := mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 0x0101, StreamType: astits.StreamTypeAACAudio, }); err != nil { log.Fatal(err) } ``` ``` -------------------------------- ### Muxer.WriteTables Source: https://context7.com/asticode/go-astits/llms.txt Forces immediate emission of the current PAT and PMT packets to the output stream, overriding the normal retransmission period. This is useful at the beginning of a stream or after program changes to ensure decoders receive updated tables promptly. ```APIDOC ## Muxer.WriteTables — Force PAT+PMT emission Immediately writes the current PAT and PMT packets to the output stream regardless of the retransmission period counter. Useful at stream start or after a programme change to ensure decoders receive updated tables as quickly as possible. ```go if _, err := mx.WriteTables(); err != nil { log.Fatalf("WriteTables: %v", err) } ``` ``` -------------------------------- ### Muxer.WriteData Source: https://context7.com/asticode/go-astits/llms.txt Serializes MuxerData (PES payload and optional adaptation field) into MPEG-TS packets and writes them to the output. PAT/PMT tables are retransmitted automatically. If AdaptationField.RandomAccessIndicator is true and the PID matches the PCR PID, tables are forced out immediately. ```APIDOC ## Muxer.WriteData — Write a PES packet to the stream Serialises a `MuxerData` (PES payload + optional adaptation field) into one or more 188-byte TS packets and writes them to the underlying `io.Writer`. PAT/PMT tables are retransmitted automatically according to the configured period. When `AdaptationField.RandomAccessIndicator` is true and the PID matches the PCR PID, tables are forced out immediately (random access point signalling). ```go import "github.com/asticode/go-astits" // Write a video access unit with PTS pts := &astits.ClockReference{Base: 900000} // 10 s at 90 kHz n, err := mx.WriteData(&astits.MuxerData{ PID: 0x0100, AdaptationField: &astits.PacketAdaptationField{ HasPCR: true, PCR: pts, RandomAccessIndicator: true, // IDR / keyframe }, PES: &astits.PESData{ Header: &astits.PESHeader{ OptionalHeader: &astits.PESOptionalHeader{ PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS, PTS: pts, }, }, Data: h264AUBytes, // raw H.264 access unit }, }) if err != nil { log.Fatal(err) } fmt.Printf("%d bytes written\n", n) ``` ``` -------------------------------- ### Add Elementary Stream to PMT Source: https://context7.com/asticode/go-astits/llms.txt Registers a PMTElementaryStream to the PMT. If ElementaryPID is 0, it's assigned automatically. Must be called before the first WriteData for that PID. Returns ErrPIDAlreadyExists if the PID is already registered. ```go mx := astits.NewMuxer(ctx, w) // Add an H.264 video stream on auto-assigned PID if err := mx.AddElementaryStream(astits.PMTElementaryStream{ StreamType: astits.StreamTypeH264Video, }); err != nil { log.Fatal(err) } // Add an AAC audio stream on PID 0x0101 if err := mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 0x0101, StreamType: astits.StreamTypeAACAudio, }); err != nil { log.Fatal(err) } ``` -------------------------------- ### Filter packets before parsing Source: https://context7.com/asticode/go-astits/llms.txt Registers a PacketSkipper that is called after the header and adaptation field have been parsed. Returning true drops the packet entirely from the pipeline; NextPacket will advance to the next unskipped packet. ```go // Only process video and audio PIDs; discard everything else skipper := func(p *astits.Packet) bool { return p.Header.PID != 0x0100 && p.Header.PID != 0x0101 } dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptPacketSkipper(skipper)) ``` -------------------------------- ### Write PES Packet with Adaptation Field Source: https://context7.com/asticode/go-astits/llms.txt Serializes a MuxerData (PES payload + optional adaptation field) into TS packets and writes them to the io.Writer. PAT/PMT tables are retransmitted automatically. When AdaptationField.RandomAccessIndicator is true and the PID matches the PCR PID, tables are forced out immediately. ```go import "github.com/asticode/go-astits" // Write a video access unit with PTS pts := &astits.ClockReference{Base: 900000} // 10 s at 90 kHz n, err := mx.WriteData(&astits.MuxerData{ PID: 0x0100, AdaptationField: &astits.PacketAdaptationField{ HasPCR: true, PCR: pts, RandomAccessIndicator: true, // IDR / keyframe }, PES: &astits.PESData{ Header: &astits.PESHeader{ OptionalHeader: &astits.PESOptionalHeader{ PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS, PTS: pts, }, }, Data: h264AUBytes, // raw H.264 access unit }, }) if err != nil { log.Fatal(err) } fmt.Printf("%d bytes written\n", n) ``` -------------------------------- ### Configure Table Retransmission Interval Source: https://context7.com/asticode/go-astits/llms.txt Sets the number of PES packets between automatic PAT/PMT retransmissions. The default is 40. Lower values increase overhead but shorten channel-change lock times for IRDs. ```go mx := astits.NewMuxer(ctx, w, astits.MuxerOptTablesRetransmitPeriod(10)) ``` -------------------------------- ### Configure Transport Stream Metadata Source: https://context7.com/asticode/go-astits/llms.txt WithTransportStreamID sets the transport stream ID written into the PAT (default 0). WithPMTPID overrides the PID used for PMT packets (default 0x1000). ```go mx := astits.NewMuxer(ctx, w, astits.WithTransportStreamID(0x0001), astits.WithPMTPID(0x1001), ) ``` -------------------------------- ### MuxerOptTablesRetransmitPeriod Source: https://context7.com/asticode/go-astits/llms.txt Configures the interval, in number of PES packets, between automatic PAT/PMT retransmissions. The default is 40. Lower values increase overhead but reduce channel-change lock times for IRDs. ```APIDOC ## MuxerOptTablesRetransmitPeriod — Configure table retransmission interval Sets the number of PES packets between automatic PAT/PMT retransmissions. The default is 40. Lower values increase overhead but shorten channel-change lock times for IRDs. ```go mx := astits.NewMuxer(ctx, w, astits.MuxerOptTablesRetransmitPeriod(10)) ``` ``` -------------------------------- ### Demuxer.NextPacket Source: https://context7.com/asticode/go-astits/llms.txt Reads the next raw TS packet (188-byte or custom-size) with its parsed PacketHeader and optional AdaptationField. Useful for low-level packet inspection like PCR extraction and continuity counter checking. ```APIDOC ## Demuxer.NextPacket — Read one raw TS packet Returns the next 188-byte (or custom-size) `*Packet` with its parsed `PacketHeader` and optional `PacketAdaptationField`. Useful when low-level per-packet inspection is required (PCR extraction, continuity counter checking, etc.). ```go for { pkt, err := dmx.NextPacket() if err == astits.ErrNoMorePackets { break } if err != nil { log.Fatal(err) } h := pkt.Header fmt.Printf("PID=%d CC=%d PUSI=%v HasAF=%v HasPayload=%v\n", h.PID, h.ContinuityCounter, h.PayloadUnitStartIndicator, h.HasAdaptationField, h.HasPayload) if h.HasAdaptationField && pkt.AdaptationField.HasPCR { pcr := pkt.AdaptationField.PCR fmt.Printf(" PCR base=%d ext=%d => %s\n", pcr.Base, pcr.Extension, pcr.Duration()) } } ``` ``` -------------------------------- ### WithTransportStreamID / WithPMTPID Source: https://context7.com/asticode/go-astits/llms.txt Options to configure the transport stream ID and the PID for PMT packets. WithTransportStreamID sets the transport stream ID in the PAT (default 0). WithPMTPID overrides the default PMT PID (0x1000). ```APIDOC ## WithTransportStreamID / WithPMTPID — Transport stream metadata options `WithTransportStreamID` sets the transport stream ID written into the PAT (default 0). `WithPMTPID` overrides the PID used for PMT packets (default `0x1000`). ```go mx := astits.NewMuxer(ctx, w, astits.WithTransportStreamID(0x0001), astits.WithPMTPID(0x1001), ) ``` ``` -------------------------------- ### Designate PCR-Carrying PID Source: https://context7.com/asticode/go-astits/llms.txt Marks which elementary PID carries the Program Clock Reference (PCR). The PCR PID must match one of the registered elementary stream PIDs. Otherwise, WriteTables/generatePMT will return ErrPCRPIDInvalid. ```go mx.AddElementaryStream(astits.PMTElementaryStream{ ElementaryPID: 0x0100, StreamType: astits.StreamTypeH264Video, }) mx.SetPCRPID(0x0100) ``` -------------------------------- ### DemuxerOptPacketSkipper — Filter packets before parsing Source: https://context7.com/asticode/go-astits/llms.txt Registers a `PacketSkipper` that is called after the header and adaptation field have been parsed. Returning `true` drops the packet entirely from the pipeline; `NextPacket` will advance to the next unskipped packet. ```APIDOC ## DemuxerOptPacketSkipper ### Description Registers a `PacketSkipper` that is called after the header and adaptation field have been parsed. Returning `true` drops the packet entirely from the pipeline; `NextPacket` will advance to the next unskipped packet. ### Method Signature `func DemuxerOptPacketSkipper(s PacketSkipper) DemuxerOpt` ### Parameters - `s` (PacketSkipper) - The packet skipper callback. - `type PacketSkipper func(p *Packet) bool` ### Example Usage ```go // Only process video and audio PIDs; discard everything else skipper := func(p *astits.Packet) bool { return p.Header.PID != 0x0100 && p.Header.PID != 0x0101 } dmx := astits.NewDemuxer(ctx, r, astits.DemuxerOptPacketSkipper(skipper)) ``` ```