Coder Social home page Coder Social logo

go-astits's Introduction

GoReportCard GoDoc Test Coveralls

This is a Golang library to natively demux and mux MPEG Transport Streams (ts) in GO.

WARNING: this library is not yet production ready. Use at your own risks!

Installation

To install the library use the following:

go get -u github.com/asticode/go-astits/...

To install the executables use the following:

go install github.com/asticode/go-astits/cmd

Before looking at the code...

The transport stream is made of packets.
Each packet has a header, an optional adaptation field and a payload.
Several payloads can be appended and parsed as a data.

                                           TRANSPORT STREAM
 +--------------------------------------------------------------------------------------------------+
 |                                                                                                  |
 
                       PACKET                                         PACKET
 +----------------------------------------------+----------------------------------------------+----
 |                                              |                                              |
 
 +--------+---------------------------+---------+--------+---------------------------+---------+
 | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | ...
 +--------+---------------------------+---------+--------+---------------------------+---------+
 
                                      |         |                                    |         |
                                      +---------+                                    +---------+
                                           |                                              |
                                           +----------------------------------------------+
                                                                DATA

Using the library in your code

WARNING: the code below doesn't handle errors for readability purposes. However you SHOULD!

Demux

// 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
    }
}

Mux

// 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,
})

Options

In order to pass options to the demuxer or the muxer, look for the methods prefixed with DemuxerOpt or MuxerOpt and add them upon calling NewDemuxer or NewMuxer :

// 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))

CLI

This library provides 2 CLIs that will automatically get installed in GOPATH/bin on go get execution.

astits-probe

List streams

$ astits-probe -i <path to your file> -f <format: text|json (default: text)>

List packets

$ astits-probe packets -i <path to your file>

List data

$ astits-probe data -i <path to your file> -d <data type: eit|nit|... (repeatable argument | if empty, all data types are shown)>

astits-es-split

Split streams into separate .ts files

$ astits-es-split <path to your file> -o <path to output dir>

Features and roadmap

  • Add demuxer
  • Add muxer
  • Demux PES packets
  • Mux PES packets
  • Demux PAT packets
  • Mux PAT packets
  • Demux PMT packets
  • Mux PMT packets
  • Demux EIT packets
  • Mux EIT packets
  • Demux NIT packets
  • Mux NIT packets
  • Demux SDT packets
  • Mux SDT packets
  • Demux TOT packets
  • Mux TOT packets
  • Demux BAT packets
  • Mux BAT packets
  • Demux DIT packets
  • Mux DIT packets
  • Demux RST packets
  • Mux RST packets
  • Demux SIT packets
  • Mux SIT packets
  • Mux ST packets
  • Demux TDT packets
  • Mux TDT packets
  • Demux TSDT packets
  • Mux TSDT packets

go-astits's People

Contributors

aler9 avatar asticode avatar barbashov avatar eric avatar k-danil avatar tmm1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-astits's Issues

panic: runtime error: index out of range in parseEITSection

Hello.

I found a index out of range bug in go-astits.

Please confirm.

Thanks.

reproduce code:

package astits

import (
	"testing"
)



func Test(t *testing.T) {
	var offset int
	inputs := []string{
			"7",
	}
	for _, input := range inputs {
		parseEITSection([]byte(input), &offset, len(input), uint16(1))
	}
}

Crash Log

--- FAIL: Test (0.00s)
panic: runtime error: index out of range [recovered]
	panic: runtime error: index out of range

goroutine 34 [running]:
panic(0x67bd00, 0xc42000c140)
	/usr/lib/go-1.7/src/runtime/panic.go:500 +0x1a1
testing.tRunner.func1(0xc42017f380)
	/usr/lib/go-1.7/src/testing/testing.go:579 +0x25d
panic(0x67bd00, 0xc42000c140)
	/usr/lib/go-1.7/src/runtime/panic.go:458 +0x243
github.com/asticode/go-astits.parseEITSection(0xc42025cfa0, 0x1, 0x8, 0xc420025f48, 0x1, 0x1, 0x45e4e3)
	/home/karas/go/src/github.com/asticode/go-astits/data_eit.go:32 +0x48f
github.com/asticode/go-astits.Test(0xc42017f380)
	/home/karas/go/src/github.com/asticode/go-astits/fuzz_test.go:15 +0xb1
testing.tRunner(0xc42017f380, 0x6edf18)
	/usr/lib/go-1.7/src/testing/testing.go:610 +0x81
created by testing.(*T).Run
	/usr/lib/go-1.7/src/testing/testing.go:646 +0x2ec
exit status 2
FAIL	github.com/asticode/go-astits	0.009s

demuxer fails to parse complex PMT with multiple tables

The PMT in https://s3.amazonaws.com/tmm1/combined-pmt-tids.ts cannot be parsed by go-astits.

For reference, see my fixes for the same bug in ffmpeg and in gots

(FYI this contains another variant where some PMT payloads don't contain the program map table).

func TestDemuxerNextDataPMTMutipleTables(t *testing.T) {
	// pmt with two tables in one packet, with 0xc0 preceeding the 0x2 program map
	pmt := hexToBytes(`47403b1e00c000150000010061000000000000010000000045491be065
f0050e030004b00fe066f0060a04656e670086e06ef0007f
c9ad32ffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffff`)
	r := bytes.NewReader(pmt)
	assert.Equal(t, 188, r.Len())

	dmx := NewDemuxer(context.Background(), r, DemuxerOptPacketSize(188))
	dmx.programMap.set(59, 1)

	d, err := dmx.NextData()
	assert.NoError(t, err)
	assert.NotNil(t, d)
	assert.Equal(t, uint16(59), d.FirstPacket.Header.PID)
	assert.NotNil(t, d.PMT)
}

func TestDemuxerNextDataPMTComplex(t *testing.T) {
	// complex pmt with two tables (0xc0 and 0x2) split across two packets
	pmt := hexToBytes(`47403b1e00c0001500000100610000000000000100000000
0035e3e2d702b0b20001c50000eefdf01809044749e10b05
0441432d330504454143330504435545491beefdf0102a02
7e1f9700e9080c001f418507d04181eefef00f810706380f
ff1f003f0a04656e670081eefff00f8107061003ff1f003f
0a047370610086ef00f00f8a01009700e9080c001f418507
d041c0ef01f012050445545631a100e9080c001f418507d0
41c0ef02f013050445545631a20100e9080c001f47003b1f
418507d041c0ef03f008bf06496e76696469a5cff3afffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff`)
	r := bytes.NewReader(pmt)
	assert.Equal(t, 188*2, r.Len())

	dmx := NewDemuxer(context.Background(), r, DemuxerOptPacketSize(188))
	dmx.programMap.set(59, 1)

	d, err := dmx.NextData()
	assert.NoError(t, err)
	assert.NotNil(t, d)
	assert.Equal(t, uint16(59), d.FirstPacket.Header.PID)
	assert.NotNil(t, d.PMT)
}

Implement ATSC standard as well

I'm trying to read the program listings from the channel data stream from a HDHomerun. For now, I'm just using curl to pull from the device into a file.
I am taking the sample app and just reporting on any EIT packets. My app finds none. If I use dvbtee to dump out data, I definitely get schedule listings from the same file.
Looking for some guidance around retrieving the program listings. Is the EIT packet the correct one? Is that something that the library supports? (The checkbox indicates it should...)

Thanks!

muxer fail then panic "writePacket: can't write 185 bytes of payload: only 184 is available... " on muxer.WriteData(&astits.MuxerData{ ...

Hi,

I'm working on a simple RTSP to SRT/MPEG-TS gateway (X.7). Lately I'm having error messages and panics during my development and tests.
Here is the test flow:

ffmpeg (mimicing a RTSP camera) --> RTSP server ( mediamtx) --> X.7 gateway app --> ffplay (mimicing SRT consumer)

RTSP Source:

ffmpeg -re -f lavfi -i "smptehdbars=rate=15:size=1280x720" -f lavfi -i "sine=frequency=1000:sample_rate=48000" -vf drawtext="text='Test Stream #1 // :timecode=01\:00\:00\:00':rate=25:x=(w-tw)/2:y=(h-lh)/2:fontsize=55:fontcolor=black:box=1:boxcolor=red" -f rtsp -c:v h264 -crf 26 -preset fast -tune zerolatency -c:a aac -b:a 128k "rtsp://127.0.0.1:8554/live1"

mediamtx is the prebuilt binary with default configuration available on github.

X.7 is using the following packages:
"github.com/asticode/go-astits"
"github.com/haivision/srtgo"
"github.com/bluenviron/gortsplib/v3"

X.7 decode H.264 video and MPEG-4 audio from RTSP stream using gortsplib, extract access units then feed them to MPEG-TS/SRT encoder.
the encoder muxes the access units into TS stream withgo- astits with SRT wrapping using srtgo then transmit it to SRT consumer.

and finally ffplay is acting like a SRT consumer
ffplay "srt://127.0.0.1:5808?mode=listener"

This workflow was working fine for several hours without any error or panic. Then last week I faced a panic with the working code.
The panic is not instant, I run the code, have a fine stream on ffplay for a while (several minutes mainly) then start to get error messages on app log

2023/06/13 10:46:23 SRT MPEGTS muxer write SRT Video error: writePacket: can't write 213 bytes of payload: only 182 is available... 

after some point the video on ffplay freeze and stay frozen forever and ffplay log popping the following following messages continously.

...
[mpegts @ 0x7fbf19f16c00] changing packet size to 192    0B f=0/0   
[mpegts @ 0x7fbf19f16c00] changing packet size to 188    0B f=0/0   
[mpegts @ 0x7fbf19f16c00] changing packet size to 192    0B f=0/0   
[mpegts @ 0x7fbf19f16c00] changing packet size to 188    0B f=0/0   
[mpegts @ 0x7fbf19f16c00] changing packet size to 204    0B f=0/0   
[mpegts @ 0x7fbf19f16c00] changing packet size to 188    0B f=0/0   
1906.04 A-V:520.879 fd= 123 aq=    0KB vq=    0KB sq=    0B f=0/0   
...

after a while, I have panic on app:

panic: runtime error: slice bounds out of range [120:0]

goroutine 52 [running]:
bytes.(*Buffer).Write(0x3?, {0xc000280318?, 0xc00004a970?, 0x10039f53b?})
        /usr/local/go/src/bytes/buffer.go:172 +0xd6
github.com/asticode/go-astikit.(*BitsWriter).write(0xc000298320, {0xc000280318, 0x1, 0x1})
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:142 +0x3d
github.com/asticode/go-astikit.(*BitsWriter).flushBsCache(...)
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:169
github.com/asticode/go-astikit.(*BitsWriter).writeFullByte(0x0?, 0x20?)
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:179 +0x7e
github.com/asticode/go-astikit.(*BitsWriter).writeByteSlice(0xc000298320, {0xc000599180, 0xa8, 0xc00004aaa0?})
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:130 +0x89
github.com/asticode/go-astikit.(*BitsWriter).Write(0xc000298320?, {0x1003e5cc0?, 0xc00004aa50?})
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:75 +0xa5
github.com/asticode/go-astits.writePESData(0x100405d00?, 0xc000282570?, {0xc000599180?, 0x1aa?, 0x1aa?}, 0x8?, 0xb6?)
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/data_pes.go:465 +0xb3
github.com/asticode/go-astits.(*Muxer).WriteData(0xc0002846e0, 0xc00004acb8)
        /Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/muxer.go:230 +0x4dd
X.7/gateway.(*SRT).Encode(0xc0000e2580, {0xc0003c9aa0, 0x4, 0x10038d28a?}, {0x0?, 0x10005687f?, 0x10004e552?}, 0x7db35b0a00)
        /Users/anyuser/GolandProjects/X.7/gateway/srt.go:175 +0x10cd
X.7/gateway.(*Gateway).RunRTSP.func1(0xc000292140?)
        /Users/anyuser/GolandProjects/X.7/gateway/rtsp.go:127 +0x191
github.com/bluenviron/gortsplib/v3.(*clientFormat).readRTPUDP(0xc0002983c0, 0xc000282870?)
        /Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_format.go:117 +0x185
github.com/bluenviron/gortsplib/v3.(*clientMedia).readRTPUDPPlay(0xc000296480, {0xc000176000, 0xc00004af20?, 0x5c1})
        /Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_media.go:291 +0xae
github.com/bluenviron/gortsplib/v3.(*clientUDPListener).runReader(0xc0002b80e0)
        /Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_udp_listener.go:195 +0x1e5
created by github.com/bluenviron/gortsplib/v3.(*clientUDPListener).start
        /Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_udp_listener.go:153 +0xaa
exit status 2

This is how I implemented the encoder:
(error handling removed for brevity)

func (s *SRT) Encode(auv [][]byte, aua []byte, pts time.Duration) error {
	if auv != nil && aua == nil { //Video stream
		filteredNALUs := [][]byte{
			{byte(h264.NALUTypeAccessUnitDelimiter), 240},
		}
		nonIDRPresent := false
		idrPresent := false
		for _, nalu := range auv {
			typ := h264.NALUType(nalu[0] & 0x1F)
			switch typ {
			case h264.NALUTypeSPS:
				s.sps = append([]byte(nil), nalu...)
				continue
			case h264.NALUTypePPS:
				s.pps = append([]byte(nil), nalu...)
				continue
			case h264.NALUTypeAccessUnitDelimiter:
				continue
			case h264.NALUTypeIDR:
				idrPresent = true
			case h264.NALUTypeNonIDR:
				nonIDRPresent = true
			}
			filteredNALUs = append(filteredNALUs, nalu)
		}
		auv = filteredNALUs
		if !nonIDRPresent && !idrPresent {
			log.Println("IDR-nonIDR Not Present Error: ")
			return fmt.Errorf("IDR-nonIDR Not Present Error")
		}
		if idrPresent {
			auv = append([][]byte{s.sps, s.pps}, auv...)
		}
		var dts time.Duration
		if !s.firstIDRReceived {
			// skip samples silently until we find one with a IDR
			if !idrPresent {
				log.Println("IDR Not Present Error: ")
				return fmt.Errorf("IDR Not Present Error")
			}
			s.firstIDRReceived = true
			s.dtsExtractor = h264.NewDTSExtractor()
			dts, err = s.dtsExtractor.Extract(auv, pts)
			s.startDTS = dts
			dts = 0
			pts -= s.startDTS
		} else {
			dts, err = s.dtsExtractor.Extract(auv, pts)
			if err != nil {
				log.Println("DTS Extract Error: ", err)
			}
			dts -= s.startDTS
			pts -= s.startDTS
		}
		oh := &astits.PESOptionalHeader{
			MarkerBits: 2,
		}
		if dts == pts {
			oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
			oh.PTS = &astits.ClockReference{Base: int64(pts.Seconds() * 90000)}
		} else {
			oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
			oh.DTS = &astits.ClockReference{Base: int64(dts.Seconds() * 90000)}
			oh.PTS = &astits.ClockReference{Base: int64(pts.Seconds() * 90000)}
		}
		// encode into Annex-B
		annexb, err := h264.AnnexBMarshal(auv)
		// write TS packet to srt
		_, err = s.muxSRT.WriteData(&astits.MuxerData{   //////// this is where I get the error message !
			PID: 256,
			AdaptationField: &astits.PacketAdaptationField{
				RandomAccessIndicator: idrPresent,
			},
			PES: &astits.PESData{
				Header: &astits.PESHeader{
					OptionalHeader: oh,
					StreamID:       224, // video
				},
				Data: annexb,
			},
		})
	} else if auv == nil && aua != nil{ //Audio stream
		// wrap access unit inside an ADTS packet
		pkts := mpeg4audio.ADTSPackets{
			{
				Type:         s.config.Type,
				SampleRate:   s.config.SampleRate,
				ChannelCount: s.config.ChannelCount,
				AU:           aua,
			},
		}
		enc, err := pkts.Marshal()
		err = s.muxSRT.WriteData(&astits.MuxerData{  //////// this is where I get the error message !
			PID: 257,
			AdaptationField: &astits.PacketAdaptationField{
				RandomAccessIndicator: true,
			},
			PES: &astits.PESData{
				Header: &astits.PESHeader{
					OptionalHeader: &astits.PESOptionalHeader{
						MarkerBits:      2,
						PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
						PTS:             &astits.ClockReference{Base: int64(pts.Seconds() * 90000)},
					},
					PacketLength: uint16(len(enc) + 8),
					StreamID:     192, // audio
				},
				Data: enc,
			},
		})
	}
	return nil
}

Thanks...

Incorrect calculation of duration compared to ffprobe.

Hey.
Thanks for the project)

I need to get the duration of the file, but I’m not getting the correct value compared to ffprobe.

Here is an example of my code and file
videoAndAudio.zip

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	astits "github.com/asticode/go-astits"
)

var (
	tsfile  = flag.String("f", "", "path to ts file")
	vebrose = flag.Bool("vebrose", false, "vebrose duration")
)

func main() {
	flag.Parse()

	// 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()
	}()

	durationBasedPTS, err := GetChunkFileDurationPTS(ctx, *tsfile, *vebrose)
	if err != nil {
		fmt.Printf("Failed to open file: %v\n", err)
		return
	}
	fmt.Println("Chunk duration PTS based:", durationBasedPTS.Nanoseconds())
	durationBasedPCR, err := GetChunkFileDurationPCR(ctx, *tsfile, *vebrose)
	if err != nil {
		fmt.Printf("Failed to open file: %v\n", err)
		return
	}
	fmt.Println("Chunk duration PCR based:", durationBasedPCR.Nanoseconds())
}

func GetChunkFileDurationPTS(ctx context.Context, filename string, vebrose bool) (time.Duration, error) {
	var (
		firstPTSDuration time.Duration
		lastPTSDuration  time.Duration
	)

	// Open your file or initialize any kind of io.Reader
	f, err := os.Open(filename)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	// Create the demuxer
	dmx := astits.New(ctx, f)
	for {
		d, err := dmx.NextData()
		if err != nil {
			if err == astits.ErrNoMorePackets {
				duration := lastPTSDuration - firstPTSDuration
				return duration, nil
			}
			// Если есть ошибка и это не индикатор окончания данных, то
			// возвращаем саму ошибку
			return 0, err
		}

		if d.PES != nil {
			ptsDuration := d.PES.Header.OptionalHeader.PTS.Duration()
			if vebrose {
				fmt.Println(ptsDuration.Nanoseconds())
			}
			if firstPTSDuration == 0 {
				firstPTSDuration = ptsDuration
			} else {
				lastPTSDuration = ptsDuration
			}
		}
	}
}

func GetChunkFileDurationPCR(ctx context.Context, filename string, vebrose bool) (time.Duration, error) {
	var (
		firstPCRDuration time.Duration
		lastPCRDuration  time.Duration
	)

	// Open your file or initialize any kind of io.Reader
	f, err := os.Open(filename)
	if err != nil {
		return 0, err
	}
	defer f.Close()
	// Create the demuxer
	dmx := astits.New(ctx, f)
	for {
		d, err := dmx.NextPacket()
		if err != nil {
			if err == astits.ErrNoMorePackets {
				duration := lastPCRDuration - firstPCRDuration
				return duration, nil
			}
			// Если есть ошибка и это не индикатор окончания данных, то
			// возвращаем саму ошибку
			return 0, err
		}
		// fmt.Println(d.AdaptationField.)
		if d.Header.HasAdaptationField {
			if d.AdaptationField.HasPCR {
				pcrDuration := d.AdaptationField.PCR.Duration()
				if vebrose {
					fmt.Println(pcrDuration.Nanoseconds())
				}
				if firstPCRDuration == 0 {
					firstPCRDuration = pcrDuration
				} else {
					lastPCRDuration = pcrDuration
				}
			}
		}
	}
}

example response:

$ ffprobe -v 8 -hide_banner -of json -show_format  testdata/videoAndAudio.ts | jq .format.duration
"5.052000"

$ go run main.go -f testdata/videoAndAudio.ts
Chunk duration PTS based: 4766666666
Chunk duration PCR based: 4520000000

Am I doing something wrong?
Why do PCR and PTS values differ from each other, and ffprobe?

Decode packet encoded in ts from socket

Hello,

I would like to decode some packets encoded in H.264/AAC data encapsulated in MPEG2/TS container coming from tcp socket.

How can I proceed using your library ?

Is that possible ? If not, it can be a good improvement !
Thanks

Logging

Hello, I am getting strange descriptors in https://github.com/asticode/go-astits/blob/master/descriptor.go#L1239 in my stream and your package spam messages from https://github.com/asticode/go-astits/blob/master/descriptor.go#L1398.
Can we refactor message logging in one of the following ways:

  • pass an instance of the logger (interface) and log in to it. There is a disadvantage that several pure functions will have one more logger argument.
  • do not log this message at all, then, however, we will lose this information

What do you think about it?

P.S. I can create pull request, if you agree with me)

Question about astikit

Hello! Can we refuse the package github.com/asticode/go-astikit?
This package use only astikit.BytesIterator and astikit.NewBytesIterator from astikit.

Astikit package has a lot of not used here functions. Also this package has strange tests that use file system and don't pass in my ci:
https://github.com/asticode/go-astikit/blob/master/ssh_test.go#L39
https://github.com/asticode/go-astikit/blob/master/archive_test.go#L23

Or we can think about refactoring these tests. What do you think?
I would like to use this package in my solution, but I can't do it yet.

demuxer.NextData not returning PAT/PMT when seen

I have a mpegts segment which starts with three packets (SDT, PAT, PMT), followed by a ES data.

With astits-probe packets -i file.ts, I can confirm the first three packets:

2021/04/01 17:04:25 Fetching packets...
2021/04/01 17:04:25 PKT: 17
2021/04/01 17:04:25   Continuity Counter: 0
2021/04/01 17:04:25   Payload Unit Start Indicator: true
2021/04/01 17:04:25   Has Payload: true
2021/04/01 17:04:25   Has Adaptation Field: false
2021/04/01 17:04:25   Transport Error Indicator: false
2021/04/01 17:04:25   Transport Priority: false
2021/04/01 17:04:25   Transport Scrambling Control: 0
2021/04/01 17:04:25 PKT: 0
2021/04/01 17:04:25   Continuity Counter: 0
2021/04/01 17:04:25   Payload Unit Start Indicator: true
2021/04/01 17:04:25   Has Payload: true
2021/04/01 17:04:25   Has Adaptation Field: false
2021/04/01 17:04:25   Transport Error Indicator: false
2021/04/01 17:04:25   Transport Priority: false
2021/04/01 17:04:25   Transport Scrambling Control: 0
2021/04/01 17:04:25 PKT: 4096
2021/04/01 17:04:25   Continuity Counter: 0
2021/04/01 17:04:25   Payload Unit Start Indicator: true
2021/04/01 17:04:25   Has Payload: true
2021/04/01 17:04:25   Has Adaptation Field: false
2021/04/01 17:04:25   Transport Error Indicator: false
2021/04/01 17:04:25   Transport Priority: false
2021/04/01 17:04:25   Transport Scrambling Control: 0
2021/04/01 17:04:25 PKT: 256
2021/04/01 17:04:25   Continuity Counter: 0
2021/04/01 17:04:25   Payload Unit Start Indicator: true
2021/04/01 17:04:25   Has Payload: true
2021/04/01 17:04:25   Has Adaptation Field: true
2021/04/01 17:04:25   Transport Error Indicator: false
2021/04/01 17:04:25   Transport Priority: false
2021/04/01 17:04:25   Transport Scrambling Control: 0
2021/04/01 17:04:25   Adaptation Field: &{AdaptationExtensionField:<nil> DiscontinuityIndicator:false ElementaryStreamPriorityIndicator:false HasAdaptationExtensionField:false HasOPCR:false HasPCR:true HasTransportPrivateData:false HasSplicingCountdown:false Length:7 IsOneByteStuffing:false StuffingLength:0 OPCR:<nil> PCR:0xc000192070 RandomAccessIndicator:true SpliceCountdown:0 TransportPrivateDataLength:0 TransportPrivateData:[]}

However when I try to use the demuxer on the same file, the PAT/PMT are not returned to me first as I would expect.

for {
    d, err := r.demux.NextData()
    if err != nil {
        panic(err)
    }

    if d.PAT != nil {
        log.Printf("action=pat")
    }

    if d.PMT != nil {
        log.Printf("action=pmt")
    }

    if d.PES == nil {
        continue
    }

    pid := d.FirstPacket.Header.PID
    log.Printf("action=pes pid=%v", pid)
}

I would expect to see action=pat and action=pmt printed as the first two lines. Instead I see:

...
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=257
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=257
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=257
2021/04/01 17:08:41 action=pat
2021/04/01 17:08:41 action=pes pid=256
2021/04/01 17:08:41 action=pes pid=257
2021/04/01 17:08:41 action=pmt
panic: astits: no more packets

Somehow the PAT and PMT are buffered until the entire file is done, and only returned at the end.

Is there a queue somewhere that's being read in the wrong order?

PES parsing fails with out of range index

Using the provided example program in master, I get an error trying to dump PES info.

ERRO[0000] astits: fetching data failed: astits: getting next data failed: astits: building new data failed: astits: parsing PES data failed: astits: getting next bytes failed: astits: slice length is 15922, offset 15935 is invalid

This seems to have broke @8d58895.

Here's an example ts that triggers this if you need it.

The command I'm running is astits data -d all -i test.ts

[Question] Realtime muxer

Hello @asticode,

great work here and I'm new to programming in Go! I've been using TSduck in exec to do what I need to however, a quick look at what you have done is promising. How far off are implementation of TDT and TOT tables.?

I'm using Teletext PES and guessing the muxer is capable of muxing the PES from another program once formatted correctly. The teletext PES need its own PTS clock or does your mux do all the PCR and PTS metrics?

Q: aac and h264 codecs

In here #9 you have said to use data.PMT.ElementaryStreams to retrieve information about tracks, essentially, and data.PES.Data to get the actual data, which I am used to from https://github.com/nareix/joy4.

But it seems you have no support for h264 video and very small for audio - I can see you have defined only few audio stream types:

// Stream types
const (
	StreamTypeLowerBitrateVideo          = 27 // ITU-T Rec. H.264 and ISO/IEC 14496-10
	StreamTypeMPEG1Audio                 = 3  // ISO/IEC 11172-3
	StreamTypeMPEG2HalvedSampleRateAudio = 4  // ISO/IEC 13818-3
	StreamTypeMPEG2PacketizedData        = 6  // ITU-T Rec. H.222 and ISO/IEC 13818-1 i.e., DVB subtitles/VBI and AC-3
)

so I wonder why not provide the packet parser as well, ie. https://github.com/nareix/joy4/tree/master/codec, since mepgts is mostly used with h264 anyway?

Also what is the difference between data.PES.Header.OptionalHeader.PTS, data.PES.Header.OptionalHeader.DTS and data.PES.Header.OptionalHeader.ESCR?

I would assume that one is the duration "timestamp" of the packet on the media timeline, the other is the duration of the packet itself and the third is the duration since the stream begun(ie. not the data stream, but the connection).

Add a muxer

i.e. I might want to read in a stream, modify it and output the stream.

In fact, I did just that in a project I'm working on where I take the m2ts stream from libbluray, and add in language descriptors (from the buray clipinfo) and then serialize it out.

End of PES payload is truncated for certain streams

I'm trying to extract timed ID3 data, which contains a JSON payload, but the end of every PES payload seems to be truncated by a few bytes. This also happens when attempting to extract the AAC audio (results in a corrupted file), the H264 stream seems to extract fine though.

I've compared this to extracting the data using ffmpeg (ffmpeg -i test.ts -map 0:2 -c copy -f data ID3.bin), ffmpeg actually has it's own issues in that it doesn't output the magic number at the start of the stream for whatever reason, but it seems to correctly extract the payload otherwise.

Here is an example ts file to use,
and here is the extracted ID3 stream using astits vs ffmpeg. You can see the first payload astits extracted ends in "transc_s":15694516, while ffmpeg is "transc_s":1569451686155}

You can see the code I'm using to extract the PES payload here: https://gist.github.com/Hakkin/6cab34dcf004494d436025a1846633c0
It's possible I'm doing something wrong in my code, but it seems fairly straightforward, so I'm not sure.

demuxer fails when corrupted PES packet advertises incorrect length

I have a sample that looks like this:

$ ./astits-probe packets -i id3.ts
2022/01/21 12:07:04 Fetching packets...
2022/01/21 12:07:04 PKT: 0
2022/01/21 12:07:04   Continuity Counter: 4
2022/01/21 12:07:04   Payload Unit Start Indicator: true
2022/01/21 12:07:04   Has Payload: true
2022/01/21 12:07:04   Has Adaptation Field: false
2022/01/21 12:07:04   Transport Error Indicator: false
2022/01/21 12:07:04   Transport Priority: false
2022/01/21 12:07:04   Transport Scrambling Control: 0
2022/01/21 12:07:04 PKT: 480
2022/01/21 12:07:04   Continuity Counter: 4
2022/01/21 12:07:04   Payload Unit Start Indicator: true
2022/01/21 12:07:04   Has Payload: true
2022/01/21 12:07:04   Has Adaptation Field: false
2022/01/21 12:07:04   Transport Error Indicator: false
2022/01/21 12:07:04   Transport Priority: false
2022/01/21 12:07:04   Transport Scrambling Control: 0
2022/01/21 12:07:04 PKT: 502
2022/01/21 12:07:04   Continuity Counter: 13
2022/01/21 12:07:04   Payload Unit Start Indicator: true
2022/01/21 12:07:04   Has Payload: true
2022/01/21 12:07:04   Has Adaptation Field: false
2022/01/21 12:07:04   Transport Error Indicator: false
2022/01/21 12:07:04   Transport Priority: false
2022/01/21 12:07:04   Transport Scrambling Control: 0
2022/01/21 12:07:04 PKT: 502
2022/01/21 12:07:04   Continuity Counter: 14
2022/01/21 12:07:04   Payload Unit Start Indicator: true
2022/01/21 12:07:04   Has Payload: true
2022/01/21 12:07:04   Has Adaptation Field: true
2022/01/21 12:07:04   Transport Error Indicator: false
2022/01/21 12:07:04   Transport Priority: false
2022/01/21 12:07:04   Transport Scrambling Control: 0
2022/01/21 12:07:04   Adaptation Field: &{AdaptationExtensionField:<nil> DiscontinuityIndicator:false ElementaryStreamPriorityIndicator:false HasAdaptationExtensionField:false HasOPCR:false HasPCR:false HasTransportPrivateData:false HasSplicingCountdown:false Length:80 IsOneByteStuffing:false StuffingLength:79 OPCR:<nil> PCR:<nil> RandomAccessIndicator:false SpliceCountdown:0 TransportPrivateDataLength:0 TransportPrivateData:[]}
2022/01/21 12:07:04 PKT: 502
2022/01/21 12:07:04   Continuity Counter: 15
2022/01/21 12:07:04   Payload Unit Start Indicator: false
2022/01/21 12:07:04   Has Payload: true
2022/01/21 12:07:04   Has Adaptation Field: true
2022/01/21 12:07:04   Transport Error Indicator: false
2022/01/21 12:07:04   Transport Priority: false
2022/01/21 12:07:04   Transport Scrambling Control: 0
2022/01/21 12:07:04   Adaptation Field: &{AdaptationExtensionField:<nil> DiscontinuityIndicator:false ElementaryStreamPriorityIndicator:false HasAdaptationExtensionField:false HasOPCR:false HasPCR:false HasTransportPrivateData:false HasSplicingCountdown:false Length:82 IsOneByteStuffing:false StuffingLength:81 OPCR:<nil> PCR:<nil> RandomAccessIndicator:false SpliceCountdown:0 TransportPrivateDataLength:0 TransportPrivateData:[]}

which fails to demux with this error:

$ ./astits-probe data -i id3.ts
2022/01/21 12:09:22 Fetching data...
2022/01/21 12:09:22 astits: fetching data failed: astits: getting next data failed: astits: building new data failed: astits: parsing PES data failed: astits: fetching next bytes failed: astikit: slice length is 184, offset 285 is invalid

http://0x0.st/oo1F.bin

parseDescriptors panics

I have a test file that produces a panic in parseDescriptors from current master:

$ astits -i test.ts -v
DEBU[0000] Fetching data...
DEBU[0000] astits: unlisted descriptor tag 0x6
DEBU[0000] astits: unlisted descriptor tag 0xf
DEBU[0000] astits: unlisted descriptor tag 0xfe
DEBU[0000] astits: unlisted descriptor tag 0xf
DEBU[0000] astits: unlisted descriptor tag 0xfe
DEBU[0000] astits: unlisted descriptor tag 0xf
DEBU[0000] astits: unlisted descriptor tag 0xfe
DEBU[0000] astits: unlisted descriptor tag 0xf
DEBU[0000] astits: unlisted descriptor tag 0x90
DEBU[0000] astits: unlisted descriptor tag 0xfe
DEBU[0000] astits: unlisted descriptor tag 0xf
...
DEBU[0000] astits: unlisted descriptor tag 0xfe         
DEBU[0000] astits: unlisted descriptor tag 0xf          
DEBU[0000] astits: unlisted descriptor tag 0xfe         
DEBU[0000] astits: unlisted descriptor tag 0xf          
DEBU[0000] astits: unlisted descriptor tag 0x90         
DEBU[0000] astits: unlisted descriptor tag 0xfe         
DEBU[0000] astits: unlisted descriptor tag 0xf          
DEBU[0000] astits: unlisted descriptor tag 0x90         
DEBU[0000] astits: unlisted descriptor tag 0xfe         
DEBU[0000] astits: unlisted descriptor tag 0xf
panic: runtime error: index out of range

goroutine 1 [running]:
github.com/asticode/go-astits.parseDescriptors(0xc420420240, 0xb8, 0xb8, 0xc4200538b8, 0x5, 0xc42029d940, 0x4)
	/home/chalupecky/src/github.com/asticode/go-astits/descriptor.go:791 +0x10b2
github.com/asticode/go-astits.parsePMTSection(0xc420420240, 0xb8, 0xb8, 0xc4200538b8, 0x11e, 0x411b1c, 0x10)
	/home/chalupecky/src/github.com/asticode/go-astits/data_pmt.go:51 +0x1a2
github.com/asticode/go-astits.parsePSISectionSyntaxData(0xc420420240, 0xb8, 0xb8, 0xc4200538b8, 0xc4200c7780, 0xc4201893e0, 0x11e, 0x1)
	/home/chalupecky/src/github.com/asticode/go-astits/data_psi.go:318 +0x57c
github.com/asticode/go-astits.parsePSISectionSyntax(0xc420420240, 0xb8, 0xb8, 0xc4200538b8, 0xc4200c7780, 0x11e, 0x4)
	/home/chalupecky/src/github.com/asticode/go-astits/data_psi.go:261 +0xc2
github.com/asticode/go-astits.parsePSISection(0xc420420240, 0xb8, 0xb8, 0xc4200538b8, 0x524477, 0xc4200bc008, 0xc4200b8030, 0xc4200538c6)
	/home/chalupecky/src/github.com/asticode/go-astits/data_psi.go:120 +0x174
github.com/asticode/go-astits.parsePSIData(0xc420420240, 0xb8, 0xb8, 0xc420420201, 0xb8, 0xb8)
	/home/chalupecky/src/github.com/asticode/go-astits/data_psi.go:93 +0x104
github.com/asticode/go-astits.parseData(0xc42000e1f0, 0x1, 0x1, 0x0, 0xc4200bc008, 0xc4200b8030, 0x0, 0x409130, 0xc4201893c2, 0xea3e6e4c, ...)
	/home/chalupecky/src/github.com/asticode/go-astits/data.go:58 +0x453
github.com/asticode/go-astits.(*Demuxer).NextData(0xc4200be000, 0xc420053c60, 0xc4201893c2, 0xc420053cf0)
	/home/chalupecky/src/github.com/asticode/go-astits/demuxer.go:128 +0x156
main.programs(0xc4200be000, 0xc420010200, 0x673b20, 0xc4200b4000, 0x0, 0x0)
	/home/chalupecky/src/github.com/asticode/go-astits/astits/main.go:238 +0x31f
main.main()
	/home/chalupecky/src/github.com/asticode/go-astits/astits/main.go:79 +0x2dc

Incorrect PTS calculation on 32 bit platforms

Hello.
I use your library for native prober
There was a need for distribution under raspberry pi (arm 32)
And I found that I get incorrect PTS values with 32 bit architecture.

my code:

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	astits "github.com/asticode/go-astits"
)

var (
	filename = flag.String("f", "", "path to ts file")
	verbose  = flag.Bool("verbose", false, "verbose duration")
)

func main() {
	flag.Parse()

	// 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()
	}()

	durationBasedPTS, startTime, err := GetChunkFileDuration(ctx, *filename, *verbose)
	if err != nil {
		fmt.Printf("Failed to open file: %v\n", err)
		return
	}
	fmt.Printf("start_time: [%v]\n", startTime)
	fmt.Printf("duration: [%v]\n", durationBasedPTS)
}

// GetChunkFileDuration find PTS and calculate  duration
func GetChunkFileDuration(ctx context.Context, filename string, verbose bool) (time.Duration, time.Duration, error) {
	var (
		firstPTSDuration time.Duration
		lastPTSDuration  time.Duration
	)

	// Open your file or initialize any kind of io.Reader
	f, err := os.Open(filename)
	if err != nil {
		return 0, 0, err
	}
	defer f.Close()
	var esCount int
	// Create the demuxer
	dmx := astits.New(ctx, f)
	for {
		d, err := dmx.NextData()
		if err != nil {
			if err == astits.ErrNoMorePackets {
				durationPTS := lastPTSDuration - firstPTSDuration
				if durationPTS != 0 {
					duration := CalculateDuration(esCount, durationPTS, verbose)
					return duration, firstPTSDuration, nil
				}
			}
			// Если есть ошибка и это не индикатор окончания данных, то
			// возвращаем саму ошибку
			return 0, 0, err
		}

		// Если тип элементарного стрима video, то считываем
		// значение PTS
		if d.PES != nil {
			typeES := ESType(d.PES.Header.StreamID)
			if typeES == "video" {
				ptsDuration := d.PES.Header.OptionalHeader.PTS.Duration()
				if verbose {
					fmt.Printf("PTS: %v\n", ptsDuration)
				}
				esCount++
				if firstPTSDuration == 0 {
					firstPTSDuration = ptsDuration
				} else {
					lastPTSDuration = ptsDuration
				}
			}
		}
	}
}

// ESType get content type in ES
func ESType(currentType uint8) string {
	// ref:
	// https://github.com/asticode/go-astits/blob/master/data_pes.go#L51
	// https://en.wikipedia.org/wiki/Packetized_elementary_stream
	var (
		StreamIDVideo = []byte{0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF}
		StreamIDAudio = []byte{0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF}
	)
	streamIDMap := map[string][]byte{
		"video": StreamIDVideo,
		"audio": StreamIDAudio,
	}
	for name, bytes := range streamIDMap {
		for _, b := range bytes {
			if b == currentType {
				return name
			}
		}
	}
	return "unknown"
}

// CalculateDuration calculate fps and duration 1 frame.
// And adds up the duration of the PTS and the duration of 1 frame.
func CalculateDuration(esCount int, durationPTS time.Duration, verbose bool) time.Duration {
	durationPTSInt := int(durationPTS)
	fps := (esCount * 1000) / (durationPTSInt / 1000000)
	if verbose {
		fmt.Printf("FPS: [%v]\n", fps)
	}
	var frameDuration = (((1 / float32(fps)) * 1000) * 1000) * 1000
	frameDurationInt := int(frameDuration)
	durationInt := durationPTSInt + frameDurationInt
	return time.Duration(durationInt)
}

test fie : https://yadi.sk/d/Emx9YKJvHNv-1Q
run on amd64:

$ go run main.go -f testdata/ref.ts -verbose
PTS: 11.4s
PTS: 11.44s
PTS: 11.48s
PTS: 11.52s
PTS: 11.56s
PTS: 11.6s
PTS: 11.64s
PTS: 11.68s
PTS: 11.72s
PTS: 11.76s
PTS: 11.8s
PTS: 11.84s
PTS: 11.88s
PTS: 11.92s
PTS: 11.96s
PTS: 12s
PTS: 12.04s
PTS: 12.08s
PTS: 12.12s
PTS: 12.16s
PTS: 12.2s
PTS: 12.24s
PTS: 12.28s
PTS: 12.32s
PTS: 12.36s
PTS: 12.4s
PTS: 12.44s
PTS: 12.48s
PTS: 12.52s
PTS: 12.56s
PTS: 12.6s
PTS: 12.64s
PTS: 12.68s
PTS: 12.72s
PTS: 12.76s
PTS: 12.8s
PTS: 12.84s
PTS: 12.88s
PTS: 12.92s
PTS: 12.96s
PTS: 13s
PTS: 13.04s
PTS: 13.08s
PTS: 13.12s
PTS: 13.16s
PTS: 13.2s
PTS: 13.24s
PTS: 13.28s
PTS: 13.32s
PTS: 13.36s
FPS: [25]
start_time: [11.4s]
duration: [2s]

Build for 386 and run:
(on arm 32, actually)

$ GOOS=linux GOARCH=386 go build -o go-probe main.go

$ ./go-probe -f testdata/ref.ts -verbose
PTS: 11.471µs
PTS: 20.554µs
PTS: -18.085µs
PTS: -9.003µs
PTS: 79ns
PTS: 9.161µs
PTS: 18.243µs
PTS: -20.395µs
PTS: -11.313µs
PTS: -2.231µs
PTS: 6.85µs
PTS: 15.933µs
PTS: -22.706µs
PTS: -13.624µs
PTS: -4.541µs
PTS: 4.54µs
PTS: 13.622µs
PTS: 22.705µs
PTS: -15.934µs
PTS: -6.852µs
PTS: 2.23µs
PTS: 11.312µs
PTS: 20.394µs
PTS: -18.244µs
PTS: -9.162µs
PTS: -80ns
PTS: 9.002µs
PTS: 18.084µs
PTS: -20.555µs
PTS: -11.472µs
PTS: -2.39µs
PTS: 6.691µs
PTS: 15.773µs
PTS: -22.865µs
PTS: -13.783µs
PTS: -4.701µs
PTS: 4.381µs
PTS: 13.463µs
PTS: 22.545µs
PTS: -16.093µs
PTS: -7.011µs
PTS: 2.07µs
PTS: 11.153µs
PTS: 20.235µs
PTS: -18.404µs
PTS: -9.321µs
PTS: -239ns
PTS: 8.842µs
PTS: 17.924µs
PTS: -20.714µs
esCount:  50
durationPTSInt:  -32185
panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.CalculateDuration(0x32, 0xffff8247, 0xffffffff, 0xa496601, 0xa4d0f4c, 0x1)
	/home/jidckii/go/src/gitlab.com/yuccastream/go-probe/main.go:123 +0x26c
main.GetChunkFileDuration(0x839c9c0, 0xa49aaa0, 0xfff2bf56, 0xf, 0xa496601, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
	/home/jidckii/go/src/gitlab.com/yuccastream/go-probe/main.go:65 +0x3fd
main.main()
	/home/jidckii/go/src/gitlab.com/yuccastream/go-probe/main.go:34 +0x14c

demuxer.NextData returns (nil, nil) on unknown data types

This was a bit unexpected and I'm not sure if it was the desired/designed behavior?

I would have expected perhaps to receive a special error for unknown data, or simply have it skipped.

For example, SCTE35 tables are not understood and so this returns (nil, nil):

func TestDemuxerNextDataSCTE35(t *testing.T) {
	scte35 := hexToBytes(`4741f61900fc302500000003289800fff01405000002547fefff4fc614f8
fe00a4e9f50000000000000c1324f6ffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffff`)
	r := bytes.NewReader(scte35)
	assert.Equal(t, 188, r.Len())

	dmx := NewDemuxer(context.Background(), r, DemuxerOptPacketSize(188))
	dmx.programMap.set(502, 1)

	d, err := dmx.NextData()
	assert.NoError(t, err)
	assert.NotNil(t, d)
	assert.Equal(t, uint16(502), d.FirstPacket.Header.PID)
	//assert.NotNil(t, d.SCTE35)
}

Similar behavior is also seen on #25 for PMT tables other than program_map

split into files

Hey, thanks for this great package, I was building sort of a replacement for mediafilesegmenter which is a very apple specific thing.
I was successfully able to inject ID3 data into the stream, I was wondering if there is a way to split the files based on duration too. I saw the packets has a field called RandomAccessIndicator which we can sort of use, but is there a way to somehow calculate time too based on this.

I tried segmenting the file using ffmpeg but it did not do it properly the ID3 data was just everywhere.

Also if I do use, RandomAccessIndicator for my file I get every 250th packet as an I-frame confirmed via ffmpeg too.
How would I split a stream based on just this info.

Would I just add these packets, into n new mixer directly and it would generate the tables and all that. Or should I first grab the elementary stream add them, write the table and then grab the individual packets.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.