From 93781fd3bf1559d56ab0f1a560912ccf79b547d5 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 30 Nov 2015 09:58:44 +0800 Subject: [PATCH 01/82] first commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..139597f --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ + + From cdd2f7f227933e13b27d71ee88027feeb74fd77e Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 2 Dec 2015 12:46:07 +0800 Subject: [PATCH 02/82] first --- example/convts.sh | 2 + example/test.go | 108 ++++++++++ ts.go | 512 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 622 insertions(+) create mode 100644 example/convts.sh create mode 100644 example/test.go create mode 100644 ts.go diff --git a/example/convts.sh b/example/convts.sh new file mode 100644 index 0000000..ca86cce --- /dev/null +++ b/example/convts.sh @@ -0,0 +1,2 @@ + +avconv -i tiny2.mov -acodec aac -vcodec h264 -strict experimental tiny2.ts diff --git a/example/test.go b/example/test.go new file mode 100644 index 0000000..01d9273 --- /dev/null +++ b/example/test.go @@ -0,0 +1,108 @@ + +package main + +import ( + "bytes" + "os" + "io" + ts "../" + "fmt" + "encoding/hex" +) + +type Stream struct { + PID uint + Header *ts.PESHeader + Title string + Data bytes.Buffer +} + +func main() { + var file *os.File + var err error + if file, err = os.Open("tiny2.ts"); err != nil { + return + } + + data := [188]byte{} + + var n int + var header ts.TSHeader + var pat ts.PAT + var pmt ts.PMT + var payload []byte + var info ts.ElementaryStreamInfo + streams := map[uint]*Stream{} + + findOrCreateStream := func(pid uint) (stream *Stream) { + stream, _ = streams[pid] + if stream == nil { + stream = &Stream{ + PID: pid, + } + if info.StreamType == ts.ElementaryStreamTypeH264 { + stream.Title = "h264" + } else if info.StreamType == ts.ElementaryStreamTypeAdtsAAC { + stream.Title = "aac" + } + streams[pid] = stream + } + return + } + + onStreamPayload := func() (err error) { + stream := findOrCreateStream(header.PID) + r := bytes.NewReader(payload) + lr := &io.LimitedReader{R: r, N: int64(len(payload))} + if header.PayloadUnitStart { + stream.Data = bytes.Buffer{} + if stream.Header, err = ts.ReadPESHeader(lr); err != nil { + return + } + } + if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { + return + } + if stream.Data.Len() == int(stream.Header.DataLength) { + fmt.Println(stream.Title, stream.Data.Len(), "total") + fmt.Println(hex.Dump(stream.Data.Bytes())) + } + return + } + + for { + if header, n, err = ts.ReadTSPacket(file, data[:]); err != nil { + return + } + //fmt.Println(header, n) + + payload = data[:n] + + if header.PID == 0 { + if pat, err = ts.ReadPAT(bytes.NewReader(payload)); err != nil { + return + } + } + + for _, entry := range(pat.Entries) { + if entry.ProgramMapPID == header.PID { + //fmt.Println("matchs", entry) + if pmt, err = ts.ReadPMT(bytes.NewReader(payload)); err != nil { + return + } + //fmt.Println("pmt", pmt) + if false { + fmt.Println(hex.Dump(payload)) + } + } + } + + for _, info = range(pmt.ElementaryStreamInfos) { + if info.ElementaryPID == header.PID { + onStreamPayload() + } + } + + } +} + diff --git a/ts.go b/ts.go new file mode 100644 index 0000000..a1388dc --- /dev/null +++ b/ts.go @@ -0,0 +1,512 @@ + +package ts + +import ( + "io" + "io/ioutil" + "fmt" +) + +type TSHeader struct { + PID uint + PCR uint64 + OPCR uint64 + ContinuityCounter uint + PayloadUnitStart bool +} + +func ReadUInt(r io.Reader, n int) (res uint, err error) { + var b [4]byte + if _, err = r.Read(b[0:n]); err != nil { + return + } + for i := 0; i < n; i++ { + res <<= 8 + res |= uint(b[i]) + } + return +} + +func ReadDummy(r io.Reader, n int) (err error) { + _, err = io.CopyN(ioutil.Discard, r, int64(n)) + return +} + +func ReadUInt64(r io.Reader, n int) (res uint64, err error) { + var res32 uint + if n > 4 { + if res32, err = ReadUInt(r, n-4); err != nil { + return + } + res |= uint64(res32)<<(uint(n-4)*8) + n = 4 + } + if res32, err = ReadUInt(r, n); err != nil { + return + } + res |= uint64(res32) + return +} + +func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { + lr := &io.LimitedReader{R: r, N: 188} + if self, err = ReadTSHeader(lr); err != nil { + return + } + if n, err = lr.Read(data[:lr.N]); err != nil { + return + } + return +} + +const ( + ElementaryStreamTypeH264 = 0x1B + ElementaryStreamTypeAdtsAAC = 0x0F +) + +type PATEntry struct { + ProgramNumber uint + NetworkPID uint + ProgramMapPID uint +} + +type PAT struct { + Entries []PATEntry +} + +type PMT struct { + PCRPID uint + ProgramDescriptors []Descriptor + ElementaryStreamInfos []ElementaryStreamInfo +} + +type Descriptor struct { + Tag uint + Data []byte +} + +type ElementaryStreamInfo struct { + StreamType uint + ElementaryPID uint + Descriptors []Descriptor +} + +type PSI struct { + TableIdExtension uint + TableId uint +} + +func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, err error) { + var flags, pointer, length uint + + // pointer field + if pointer, err = ReadUInt(r, 1); err != nil { + return + } + if pointer != 0 { + if err = ReadDummy(r, int(pointer)); err != nil { + return + } + } + + // table_id + if self.TableId, err = ReadUInt(r, 1); err != nil { + return + } + + // reserved(4) + // section_length(10) + if flags, err = ReadUInt(r, 2); err != nil { + return + } + length = flags & 0x3FF + lr = &io.LimitedReader{R: r, N: int64(length)} + + // Table ID extension(16) + if _, err = ReadUInt(lr, 2); err != nil { + return + } + + // _(2) + // version(5) + // Current_next_indicator(1) + if _, err = ReadUInt(lr, 1); err != nil { + return + } + + // section_number(8) + if _, err = ReadUInt(lr, 1); err != nil { + return + } + + // last_section_number(8) + if _, err = ReadUInt(lr, 1); err != nil { + return + } + + lr.N -= 4 + return +} + +func ReadPMT(r io.Reader) (self PMT, err error) { + var lr *io.LimitedReader + //var psi PSI + + if _, lr, err = ReadPSI(r); err != nil { + return + } + + var flags, length uint + + // Reserved(3) + // PCRPID(13) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + self.PCRPID = flags & 0x1fff + + // Reserved(6) + // Program info length(10) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + length = flags & 0x3ff + + readDescs := func(lr *io.LimitedReader) (res []Descriptor, err error) { + var desc Descriptor + for lr.N > 0 { + if desc.Tag, err = ReadUInt(lr, 1); err != nil { + return + } + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + desc.Data = make([]byte, length) + if _, err = lr.Read(desc.Data); err != nil { + return + } + res = append(res, desc) + } + return + } + + if length > 0 { + lr := &io.LimitedReader{R: lr, N: int64(length)} + if self.ProgramDescriptors, err = readDescs(lr); err != nil { + return + } + } + + for lr.N > 0 { + var info ElementaryStreamInfo + if info.StreamType, err = ReadUInt(lr, 1); err != nil { + return + } + + // Reserved(3) + // Elementary PID(13) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + info.ElementaryPID = flags & 0x1fff + + // Reserved(6) + // ES Info length length(10) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + length = flags & 0x3ff + + if length > 0 { + lr := &io.LimitedReader{R: lr, N: int64(length)} + if info.Descriptors, err = readDescs(lr); err != nil { + return + } + } + self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info) + } + + return +} + +func ReadPAT(r io.Reader) (self PAT, err error) { + var lr *io.LimitedReader + //var psi PSI + + if _, lr, err = ReadPSI(r); err != nil { + return + } + + for lr.N > 0 { + entry := PATEntry{} + if entry.ProgramNumber, err = ReadUInt(lr, 2); err != nil { + return + } + if entry.ProgramNumber == 0 { + if entry.NetworkPID, err = ReadUInt(lr, 2); err != nil { + return + } + entry.NetworkPID &= 0x1fff + } else { + if entry.ProgramMapPID, err = ReadUInt(lr, 2); err != nil { + return + } + entry.ProgramMapPID &= 0x1fff + } + self.Entries = append(self.Entries, entry) + } + + return +} + +func ReadTSHeader(r io.Reader) (self TSHeader, err error) { + var flags uint + + // sync(8) + // transport_error_indicator(1) + // payload_unit_start_indicator(1) + // transport_priority(1) + // pid(13) + // Scrambling control(2) + // Adaptation field flag(1) + // Continuity counter(4) + if flags, err = ReadUInt(r, 4); err != nil { + return + } + + if flags & 0x400000 != 0 { + self.PayloadUnitStart = true + } + + if (flags & 0xff000000) >> 24 != 0x47 { + err = fmt.Errorf("invalid sync") + return + } + + self.PID = (flags & 0x1fff00) >> 8 + self.ContinuityCounter = flags & 0xf + + if flags & 0x20 != 0 { + var flags, length uint + if length, err = ReadUInt(r, 1); err != nil { + return + } + lr := &io.LimitedReader{R: r, N: int64(length)} + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + // PCR + if flags & 0x10 != 0 { + if self.PCR, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // OPCR + if flags & 0x08 != 0 { + if self.OPCR, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // Splice countdown + if flags & 0x04 != 0 { + if _, err = ReadUInt(lr, 1); err != nil { + return + } + } + + // Transport private data + if flags & 0x02 != 0 { + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + + b := make([]byte, length) + if _, err = lr.Read(b); err != nil { + return + } + } + + // Adaptation extension + if err = ReadDummy(lr, int(lr.N)); err != nil { + return + } + } + + return +} + +type PESHeader struct { + StreamId uint // H264=0xe0 AAC=0xc0 + DataLength uint + PTS uint64 + DTS uint64 + ESCR uint64 +} + +func PESUIntToTs(v uint64) (ts uint64) { + // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 + return (((v>>33)&0x7)<<30) | (((v>>17)&0xef)<<15) | ((v>>1)&0xef) +} + +func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { + var flags, length uint + self := &PESHeader{} + + // start code 000001 + if flags, err = ReadUInt(r, 3); err != nil { + return + } + if flags != 0x000001 { + err = fmt.Errorf("invalid PES header") + return + } + + if self.StreamId, err = ReadUInt(r, 1); err != nil { + return + } + + if length, err = ReadUInt(r, 2); err != nil { + return + } + lrAll := &io.LimitedReader{R: r, N: int64(length)} + lr := lrAll + + // PES scrambling control + // PES priority + // data alignment indicator + // copyright + // original or copy + if _, err = ReadUInt(lr, 1); err != nil { + return + } + + // PTS DTS flags(2) + // ESCR flag(1) + // ES rate flag(1) + // DSM trick mode flag(1) + // additional copy info flag(1) + // PES CRC flag(1) + // PES extension flag(1) + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + // PES header data length(8) + if length, err = ReadUInt(lr, 1); err != nil { + return + } + lr = &io.LimitedReader{R: lr, N: int64(length)} + + if flags & 0x80 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 5); err != nil { + return + } + self.PTS = PESUIntToTs(v) + } + + if flags & 0x40 != 0 && flags & 0x80 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 5); err != nil { + return + } + self.DTS = PESUIntToTs(v) + } + + // ESCR flag + if flags & 0x20 != 0 { + if _, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // ES rate flag + if flags & 0x10 != 0 { + if _, err = ReadUInt64(lr, 3); err != nil { + return + } + } + + // additional copy info flag + if flags & 0x04 != 0 { + if _, err = ReadUInt(lr, 1); err != nil { + return + } + } + + // PES CRC flag + if flags & 0x02 != 0 { + if _, err = ReadUInt(lr, 2); err != nil { + return + } + } + + // PES extension flag + if flags & 0x01 != 0 { + var flags uint + + // PES private data flag(1) + // pack header field flag(1) + // program packet sequence counter flag(1) + // P-STD buffer flag(1) + // 111(3) + // PES extension flag 2(1) + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + // PES private data flag(1) + if flags & 0x80 != 0 { + // if set to 1 16 bytes of user defined data is appended to the header data field + if err = ReadDummy(lr, 16); err != nil { + return + } + } + + // pack header field flag(1) + if flags & 0x40 != 0 { + // if set to 1 the 8-bit pack field length value is appended to the header data field + if err = ReadDummy(lr, 1); err != nil { + return + } + } + + // program packet sequence counter flag(1) + if flags & 0x20 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + + // P-STD buffer flag(1) + if flags & 0x10 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + + // PES extension flag 2(1) + if flags & 0x01 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + } + + if lr.N > 0 { + if err = ReadDummy(lr, int(lr.N)); err != nil { + return + } + } + + self.DataLength = uint(lrAll.N) + + res = self + return +} + From e335094c99feb2a0de51e0aaae05deb88673f842 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 4 Dec 2015 19:31:30 +0800 Subject: [PATCH 03/82] reader checksum complete --- checksum.go | 95 ++++++++ example/test.go | 9 +- reader.go | 610 ++++++++++++++++++++++++++++++++++++++++++++++++ ts.go | 458 ++---------------------------------- writer.go | 12 + 5 files changed, 740 insertions(+), 444 deletions(-) create mode 100644 checksum.go create mode 100644 reader.go create mode 100644 writer.go diff --git a/checksum.go b/checksum.go new file mode 100644 index 0000000..ae73093 --- /dev/null +++ b/checksum.go @@ -0,0 +1,95 @@ + +package ts + +import ( + "io" + "fmt" + "encoding/hex" +) + +var ieeeCrc32Tbl = []uint32 { + 0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517, + 0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B, + 0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048, + 0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652, + 0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D, + 0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095, + 0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA, + 0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0, + 0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3, + 0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF, + 0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730, + 0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A, + 0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05, + 0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475, + 0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A, + 0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840, + 0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB, + 0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87, + 0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4, + 0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE, + 0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1, + 0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64, + 0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B, + 0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351, + 0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832, + 0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E, + 0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5, + 0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF, + 0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0, + 0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0, + 0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F, + 0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185, + 0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A, + 0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176, + 0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15, + 0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F, + 0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620, + 0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8, + 0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7, + 0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD, + 0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E, + 0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2, + 0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1, 0x00000001, +} + +func updateIeeeCrc32(crc uint32, data []byte) uint32 { + for _, b := range data { + crc = ieeeCrc32Tbl[b^byte(crc)]^(crc>>8) + } + return crc +} + +type Crc32Reader struct { + R io.Reader + Crc32 uint32 +} + +var debugCrc32 = false + +func NewCrc32Reader(r io.Reader) *Crc32Reader { + return &Crc32Reader{R: r, Crc32: 0xffffffff} +} + +func (self *Crc32Reader) Read(b []byte) (n int, err error) { + if n, err = self.R.Read(b); err != nil { + return + } + if debugCrc32 { + fmt.Println("crc32: update", hex.EncodeToString(b)) + } + self.Crc32 = updateIeeeCrc32(self.Crc32, b) + return +} + +func (self *Crc32Reader) ReadCrc32UIntAndCheck() (err error) { + if err = ReadDummy(self, 4); err != nil { + return + } + if self.Crc32 != 0 { + err = fmt.Errorf("crc32 != 0") + return + } + return +} + diff --git a/example/test.go b/example/test.go index 01d9273..24203a8 100644 --- a/example/test.go +++ b/example/test.go @@ -74,12 +74,13 @@ func main() { if header, n, err = ts.ReadTSPacket(file, data[:]); err != nil { return } - //fmt.Println(header, n) + fmt.Println(header, n) payload = data[:n] + pr := bytes.NewReader(payload) if header.PID == 0 { - if pat, err = ts.ReadPAT(bytes.NewReader(payload)); err != nil { + if pat, err = ts.ReadPAT(pr); err != nil { return } } @@ -87,11 +88,11 @@ func main() { for _, entry := range(pat.Entries) { if entry.ProgramMapPID == header.PID { //fmt.Println("matchs", entry) - if pmt, err = ts.ReadPMT(bytes.NewReader(payload)); err != nil { + if pmt, err = ts.ReadPMT(pr); err != nil { return } //fmt.Println("pmt", pmt) - if false { + if true { fmt.Println(hex.Dump(payload)) } } diff --git a/reader.go b/reader.go new file mode 100644 index 0000000..96cc43b --- /dev/null +++ b/reader.go @@ -0,0 +1,610 @@ + +package ts + +import ( + "fmt" + "io" + "io/ioutil" +) + +func ReadUInt(r io.Reader, n int) (res uint, err error) { + var b [4]byte + if _, err = r.Read(b[0:n]); err != nil { + return + } + for i := 0; i < n; i++ { + res <<= 8 + res |= uint(b[i]) + } + return +} + +func ReadDummy(r io.Reader, n int) (err error) { + _, err = io.CopyN(ioutil.Discard, r, int64(n)) + return +} + +func ReadUInt64(r io.Reader, n int) (res uint64, err error) { + var res32 uint + if n > 4 { + if res32, err = ReadUInt(r, n-4); err != nil { + return + } + res |= uint64(res32)<<(uint(n-4)*8) + n = 4 + } + if res32, err = ReadUInt(r, n); err != nil { + return + } + res |= uint64(res32) + return +} + +func ReadTSHeader(r io.Reader) (self TSHeader, err error) { + var flags uint + + // sync(8) + // transport_error_indicator(1) + // payload_unit_start_indicator(1) + // transport_priority(1) + // pid(13) + // Scrambling control(2) + // Adaptation field flag(1) + // Continuity counter(4) + if flags, err = ReadUInt(r, 4); err != nil { + return + } + + if debug { + fmt.Printf("ts: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {8, "sync"}, + {1, "transport_error_indicator"}, + {1, "payload_unit_start_indicator"}, + {1, "transport_priority"}, + {13, "pid"}, + {2, "scrambling_control"}, + {1, "adaptation_field_flag"}, + {4, "continuity_counter"}, + }, + Val: flags, + Length: 32, + }) + } + + if flags & 0x400000 != 0 { + self.PayloadUnitStart = true + } + + if (flags & 0xff000000) >> 24 != 0x47 { + err = fmt.Errorf("invalid sync") + return + } + + self.PID = (flags & 0x1fff00) >> 8 + self.ContinuityCounter = flags & 0xf + + if flags & 0x20 != 0 { + var flags, length uint + if length, err = ReadUInt(r, 1); err != nil { + return + } + lr := &io.LimitedReader{R: r, N: int64(length)} + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + if debug { + fmt.Printf("ts: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {1, "discontinuity_indicator"}, + {1, "random_access_indicator"}, + {1, "elementary_stream_priority_indicator"}, + {1, "pcr_flag"}, + {1, "opcr_flag"}, + {1, "splicing_point_flag"}, + {1, "transport_private_data_flag"}, + {1, "adaptation_field_extension_flag"}, + }, + Val: flags, + Length: 8, + }) + } + + // PCR + if flags & 0x10 != 0 { + if self.PCR, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // OPCR + if flags & 0x08 != 0 { + if self.OPCR, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // Splice countdown + if flags & 0x04 != 0 { + if _, err = ReadUInt(lr, 1); err != nil { + return + } + } + + // Transport private data + if flags & 0x02 != 0 { + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + + b := make([]byte, length) + if _, err = lr.Read(b); err != nil { + return + } + } + + // Adaptation extension + if lr.N > 0 { + data := make([]byte, lr.N) + if _, err = lr.Read(data); err != nil { + return + } + + if debug { + // rubish + //fmt.Println("ts: ", data) + } + } + } + + return +} + +func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { + lr := &io.LimitedReader{R: r, N: 188} + if self, err = ReadTSHeader(lr); err != nil { + return + } + if n, err = lr.Read(data[:lr.N]); err != nil { + return + } + return +} + +func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err error) { + var flags, pointer, length uint + + // pointer field + if pointer, err = ReadUInt(r, 1); err != nil { + return + } + if pointer != 0 { + if err = ReadDummy(r, int(pointer)); err != nil { + return + } + } + + cr = NewCrc32Reader(r) + + // table_id + if self.TableId, err = ReadUInt(cr, 1); err != nil { + return + } + + // reserved(4)=0xb + // section_length(10) + if flags, err = ReadUInt(cr, 2); err != nil { + return + } + length = flags & 0x3FF + + if debug { + fmt.Printf("psi: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {4, "reserved"}, + }, + Val: flags, + Length: 16, + }) + } + + lr = &io.LimitedReader{R: cr, N: int64(length)} + + // Table ID extension(16) + if self.TableIdExtension, err = ReadUInt(lr, 2); err != nil { + return + } + + // resverd(2)=3 + // version(5) + // Current_next_indicator(1) + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + if debug { + fmt.Printf("psi: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {2, "resverd"}, + {5, "version"}, + {1, "current_next_indicator"}, + }, + Val: flags, + Length: 8, + }) + } + + // section_number(8) + if self.SecNum, err = ReadUInt(lr, 1); err != nil { + return + } + + // last_section_number(8) + if self.LastSecNum, err = ReadUInt(lr, 1); err != nil { + return + } + + if debug { + fmt.Printf("psi: table_id=%x table_extension=%x secnum=%x lastsecnum=%x\n", + self.TableId, + self.TableIdExtension, + self.SecNum, + self.LastSecNum, + ) + } + + lr.N -= 4 + return +} + +func ReadPMT(r io.Reader) (self PMT, err error) { + var lr *io.LimitedReader + var cr *Crc32Reader + //var psi PSI + + if _, lr, cr, err = ReadPSI(r); err != nil { + return + } + + var flags, length uint + + // 111(3) + // PCRPID(13) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + self.PCRPID = flags & 0x1fff + + if debug { + fmt.Printf("pmt: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {3, "reserved"}, + {13, "pcrpid"}, + }, + Val: flags, + Length: 16, + }) + } + + // Reserved(6) + // Program info length(10) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + length = flags & 0x3ff + + readDescs := func(lr *io.LimitedReader) (res []Descriptor, err error) { + var desc Descriptor + for lr.N > 0 { + if desc.Tag, err = ReadUInt(lr, 1); err != nil { + return + } + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + desc.Data = make([]byte, length) + if _, err = lr.Read(desc.Data); err != nil { + return + } + res = append(res, desc) + } + return + } + + if length > 0 { + lr := &io.LimitedReader{R: lr, N: int64(length)} + if self.ProgramDescriptors, err = readDescs(lr); err != nil { + return + } + } + + for lr.N > 0 { + var info ElementaryStreamInfo + if info.StreamType, err = ReadUInt(lr, 1); err != nil { + return + } + + // Reserved(3) + // Elementary PID(13) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + info.ElementaryPID = flags & 0x1fff + + // Reserved(6) + // ES Info length length(10) + if flags, err = ReadUInt(lr, 2); err != nil { + return + } + length = flags & 0x3ff + + if length > 0 { + lr := &io.LimitedReader{R: lr, N: int64(length)} + if info.Descriptors, err = readDescs(lr); err != nil { + return + } + } + self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info) + } + + if debug { + fmt.Printf("pmt: ElementaryStreamInfos %v\n", self.ElementaryStreamInfos) + + } + + if err = cr.ReadCrc32UIntAndCheck(); err != nil { + return + } + + return +} + +func ReadPAT(r io.Reader) (self PAT, err error) { + var lr *io.LimitedReader + var cr *Crc32Reader + //var psi PSI + + if _, lr, cr, err = ReadPSI(r); err != nil { + return + } + + for lr.N > 0 { + entry := PATEntry{} + if entry.ProgramNumber, err = ReadUInt(lr, 2); err != nil { + return + } + if entry.ProgramNumber == 0 { + if entry.NetworkPID, err = ReadUInt(lr, 2); err != nil { + return + } + entry.NetworkPID &= 0x1fff + } else { + if entry.ProgramMapPID, err = ReadUInt(lr, 2); err != nil { + return + } + entry.ProgramMapPID &= 0x1fff + } + self.Entries = append(self.Entries, entry) + } + + if err = cr.ReadCrc32UIntAndCheck(); err != nil { + return + } + + return +} + +func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { + var flags, length uint + self := &PESHeader{} + + // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html + + // start code 000001 + if flags, err = ReadUInt(r, 3); err != nil { + return + } + if flags != 0x000001 { + err = fmt.Errorf("invalid PES header") + return + } + + if self.StreamId, err = ReadUInt(r, 1); err != nil { + return + } + + if length, err = ReadUInt(r, 2); err != nil { + return + } + lrAll := &io.LimitedReader{R: r, N: int64(length)} + lr := lrAll + + // 10(2) + // PES scrambling control(2) + // PES priority(1) + // data alignment indicator(1) + // copyright(1) + // original or copy(1) + if _, err = ReadUInt(lr, 1); err != nil { + return + } + + if debug { + fmt.Printf("pes: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {2, "PES_scrambling_control"}, + {1, "PES_priority"}, + {1, "data_alignment_indicator"}, + {1, "copyright"}, + {1, "original_or_copy"}, + }, + Val: flags, + Length: 6, + }) + } + + // PTS DTS flags(2) + // ESCR flag(1) + // ES rate flag(1) + // DSM trick mode flag(1) + // additional copy info flag(1) + // PES CRC flag(1) + // PES extension flag(1) + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + if debug { + fmt.Printf("pes: %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {2, "pts_dts_flags"}, + {1, "escr_flag"}, + {1, "es_rate_flag"}, + {1, "dsm_trick_mode_flag"}, + {1, "additional_copy_info_flag"}, + {1, "pes_crc_flag"}, + {1, "pes_extension_flag"}, + }, + Val: flags, + Length: 8, + }) + } + + // PES header data length(8) + if length, err = ReadUInt(lr, 1); err != nil { + return + } + lr = &io.LimitedReader{R: lr, N: int64(length)} + + if flags & 0x80 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 5); err != nil { + return + } + self.PTS = PESUIntToTs(v) + } + + if flags & 0x40 != 0 && flags & 0x80 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 5); err != nil { + return + } + self.DTS = PESUIntToTs(v) + } + + // ESCR flag + if flags & 0x20 != 0 { + if _, err = ReadUInt64(lr, 6); err != nil { + return + } + } + + // ES rate flag + if flags & 0x10 != 0 { + if _, err = ReadUInt64(lr, 3); err != nil { + return + } + } + + // additional copy info flag + if flags & 0x04 != 0 { + if _, err = ReadUInt(lr, 1); err != nil { + return + } + } + + // PES CRC flag + if flags & 0x02 != 0 { + if _, err = ReadUInt(lr, 2); err != nil { + return + } + } + + // PES extension flag + if flags & 0x01 != 0 { + var flags uint + + // PES private data flag(1) + // pack header field flag(1) + // program packet sequence counter flag(1) + // P-STD buffer flag(1) + // 111(3) + // PES extension flag 2(1) + if flags, err = ReadUInt(lr, 1); err != nil { + return + } + + // PES private data flag(1) + if flags & 0x80 != 0 { + // if set to 1 16 bytes of user defined data is appended to the header data field + if err = ReadDummy(lr, 16); err != nil { + return + } + } + + // pack header field flag(1) + if flags & 0x40 != 0 { + // if set to 1 the 8-bit pack field length value is appended to the header data field + if err = ReadDummy(lr, 1); err != nil { + return + } + } + + // program packet sequence counter flag(1) + if flags & 0x20 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + + // P-STD buffer flag(1) + if flags & 0x10 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + + // PES extension flag 2(1) + if flags & 0x01 != 0 { + if err = ReadDummy(lr, 2); err != nil { + return + } + } + } + + if lr.N > 0 { + if err = ReadDummy(lr, int(lr.N)); err != nil { + return + } + } + + self.DataLength = uint(lrAll.N) + + res = self + return +} + diff --git a/ts.go b/ts.go index a1388dc..06c9c75 100644 --- a/ts.go +++ b/ts.go @@ -2,11 +2,11 @@ package ts import ( - "io" - "io/ioutil" "fmt" ) +const debug = true + type TSHeader struct { PID uint PCR uint64 @@ -15,50 +15,6 @@ type TSHeader struct { PayloadUnitStart bool } -func ReadUInt(r io.Reader, n int) (res uint, err error) { - var b [4]byte - if _, err = r.Read(b[0:n]); err != nil { - return - } - for i := 0; i < n; i++ { - res <<= 8 - res |= uint(b[i]) - } - return -} - -func ReadDummy(r io.Reader, n int) (err error) { - _, err = io.CopyN(ioutil.Discard, r, int64(n)) - return -} - -func ReadUInt64(r io.Reader, n int) (res uint64, err error) { - var res32 uint - if n > 4 { - if res32, err = ReadUInt(r, n-4); err != nil { - return - } - res |= uint64(res32)<<(uint(n-4)*8) - n = 4 - } - if res32, err = ReadUInt(r, n); err != nil { - return - } - res |= uint64(res32) - return -} - -func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { - lr := &io.LimitedReader{R: r, N: 188} - if self, err = ReadTSHeader(lr); err != nil { - return - } - if n, err = lr.Read(data[:lr.N]); err != nil { - return - } - return -} - const ( ElementaryStreamTypeH264 = 0x1B ElementaryStreamTypeAdtsAAC = 0x0F @@ -94,250 +50,8 @@ type ElementaryStreamInfo struct { type PSI struct { TableIdExtension uint TableId uint -} - -func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, err error) { - var flags, pointer, length uint - - // pointer field - if pointer, err = ReadUInt(r, 1); err != nil { - return - } - if pointer != 0 { - if err = ReadDummy(r, int(pointer)); err != nil { - return - } - } - - // table_id - if self.TableId, err = ReadUInt(r, 1); err != nil { - return - } - - // reserved(4) - // section_length(10) - if flags, err = ReadUInt(r, 2); err != nil { - return - } - length = flags & 0x3FF - lr = &io.LimitedReader{R: r, N: int64(length)} - - // Table ID extension(16) - if _, err = ReadUInt(lr, 2); err != nil { - return - } - - // _(2) - // version(5) - // Current_next_indicator(1) - if _, err = ReadUInt(lr, 1); err != nil { - return - } - - // section_number(8) - if _, err = ReadUInt(lr, 1); err != nil { - return - } - - // last_section_number(8) - if _, err = ReadUInt(lr, 1); err != nil { - return - } - - lr.N -= 4 - return -} - -func ReadPMT(r io.Reader) (self PMT, err error) { - var lr *io.LimitedReader - //var psi PSI - - if _, lr, err = ReadPSI(r); err != nil { - return - } - - var flags, length uint - - // Reserved(3) - // PCRPID(13) - if flags, err = ReadUInt(lr, 2); err != nil { - return - } - self.PCRPID = flags & 0x1fff - - // Reserved(6) - // Program info length(10) - if flags, err = ReadUInt(lr, 2); err != nil { - return - } - length = flags & 0x3ff - - readDescs := func(lr *io.LimitedReader) (res []Descriptor, err error) { - var desc Descriptor - for lr.N > 0 { - if desc.Tag, err = ReadUInt(lr, 1); err != nil { - return - } - var length uint - if length, err = ReadUInt(lr, 1); err != nil { - return - } - desc.Data = make([]byte, length) - if _, err = lr.Read(desc.Data); err != nil { - return - } - res = append(res, desc) - } - return - } - - if length > 0 { - lr := &io.LimitedReader{R: lr, N: int64(length)} - if self.ProgramDescriptors, err = readDescs(lr); err != nil { - return - } - } - - for lr.N > 0 { - var info ElementaryStreamInfo - if info.StreamType, err = ReadUInt(lr, 1); err != nil { - return - } - - // Reserved(3) - // Elementary PID(13) - if flags, err = ReadUInt(lr, 2); err != nil { - return - } - info.ElementaryPID = flags & 0x1fff - - // Reserved(6) - // ES Info length length(10) - if flags, err = ReadUInt(lr, 2); err != nil { - return - } - length = flags & 0x3ff - - if length > 0 { - lr := &io.LimitedReader{R: lr, N: int64(length)} - if info.Descriptors, err = readDescs(lr); err != nil { - return - } - } - self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info) - } - - return -} - -func ReadPAT(r io.Reader) (self PAT, err error) { - var lr *io.LimitedReader - //var psi PSI - - if _, lr, err = ReadPSI(r); err != nil { - return - } - - for lr.N > 0 { - entry := PATEntry{} - if entry.ProgramNumber, err = ReadUInt(lr, 2); err != nil { - return - } - if entry.ProgramNumber == 0 { - if entry.NetworkPID, err = ReadUInt(lr, 2); err != nil { - return - } - entry.NetworkPID &= 0x1fff - } else { - if entry.ProgramMapPID, err = ReadUInt(lr, 2); err != nil { - return - } - entry.ProgramMapPID &= 0x1fff - } - self.Entries = append(self.Entries, entry) - } - - return -} - -func ReadTSHeader(r io.Reader) (self TSHeader, err error) { - var flags uint - - // sync(8) - // transport_error_indicator(1) - // payload_unit_start_indicator(1) - // transport_priority(1) - // pid(13) - // Scrambling control(2) - // Adaptation field flag(1) - // Continuity counter(4) - if flags, err = ReadUInt(r, 4); err != nil { - return - } - - if flags & 0x400000 != 0 { - self.PayloadUnitStart = true - } - - if (flags & 0xff000000) >> 24 != 0x47 { - err = fmt.Errorf("invalid sync") - return - } - - self.PID = (flags & 0x1fff00) >> 8 - self.ContinuityCounter = flags & 0xf - - if flags & 0x20 != 0 { - var flags, length uint - if length, err = ReadUInt(r, 1); err != nil { - return - } - lr := &io.LimitedReader{R: r, N: int64(length)} - if flags, err = ReadUInt(lr, 1); err != nil { - return - } - - // PCR - if flags & 0x10 != 0 { - if self.PCR, err = ReadUInt64(lr, 6); err != nil { - return - } - } - - // OPCR - if flags & 0x08 != 0 { - if self.OPCR, err = ReadUInt64(lr, 6); err != nil { - return - } - } - - // Splice countdown - if flags & 0x04 != 0 { - if _, err = ReadUInt(lr, 1); err != nil { - return - } - } - - // Transport private data - if flags & 0x02 != 0 { - var length uint - if length, err = ReadUInt(lr, 1); err != nil { - return - } - - b := make([]byte, length) - if _, err = lr.Read(b); err != nil { - return - } - } - - // Adaptation extension - if err = ReadDummy(lr, int(lr.N)); err != nil { - return - } - } - - return + SecNum uint + LastSecNum uint } type PESHeader struct { @@ -353,160 +67,24 @@ func PESUIntToTs(v uint64) (ts uint64) { return (((v>>33)&0x7)<<30) | (((v>>17)&0xef)<<15) | ((v>>1)&0xef) } -func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { - var flags, length uint - self := &PESHeader{} - - // start code 000001 - if flags, err = ReadUInt(r, 3); err != nil { - return - } - if flags != 0x000001 { - err = fmt.Errorf("invalid PES header") - return +type FieldsDumper struct { + Fields []struct { + Length int + Desc string } + Val uint + Length uint +} - if self.StreamId, err = ReadUInt(r, 1); err != nil { - return - } - - if length, err = ReadUInt(r, 2); err != nil { - return - } - lrAll := &io.LimitedReader{R: r, N: int64(length)} - lr := lrAll - - // PES scrambling control - // PES priority - // data alignment indicator - // copyright - // original or copy - if _, err = ReadUInt(lr, 1); err != nil { - return - } - - // PTS DTS flags(2) - // ESCR flag(1) - // ES rate flag(1) - // DSM trick mode flag(1) - // additional copy info flag(1) - // PES CRC flag(1) - // PES extension flag(1) - if flags, err = ReadUInt(lr, 1); err != nil { - return - } - - // PES header data length(8) - if length, err = ReadUInt(lr, 1); err != nil { - return - } - lr = &io.LimitedReader{R: lr, N: int64(length)} - - if flags & 0x80 != 0 { - var v uint64 - if v, err = ReadUInt64(lr, 5); err != nil { - return - } - self.PTS = PESUIntToTs(v) - } - - if flags & 0x40 != 0 && flags & 0x80 != 0 { - var v uint64 - if v, err = ReadUInt64(lr, 5); err != nil { - return - } - self.DTS = PESUIntToTs(v) - } - - // ESCR flag - if flags & 0x20 != 0 { - if _, err = ReadUInt64(lr, 6); err != nil { - return +func (self FieldsDumper) String() (res string) { + pos := uint(self.Length) + for _, field := range self.Fields { + pos -= uint(field.Length) + val := (self.Val>>pos)&(1< 0 { - if err = ReadDummy(lr, int(lr.N)); err != nil { - return - } - } - - self.DataLength = uint(lrAll.N) - - res = self return } diff --git a/writer.go b/writer.go new file mode 100644 index 0000000..9b7bfd3 --- /dev/null +++ b/writer.go @@ -0,0 +1,12 @@ + +package ts + +import ( + _ "fmt" + "io" +) + +type SimpleH264Writer struct { + W io.Writer +} + From 841ebf6b90ab31857de74080852c5e7568cfb473 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 5 Dec 2015 01:30:10 +0800 Subject: [PATCH 04/82] fix ReadUInt64 bug due to PCR incorrect --- example/test.go | 2 +- reader.go | 19 ++++++++++++++++--- ts.go | 17 ++++++++++++----- writer.go | 35 ++++++++++++++++++++++++++++++++++- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/example/test.go b/example/test.go index 24203a8..0b11c3f 100644 --- a/example/test.go +++ b/example/test.go @@ -20,7 +20,7 @@ type Stream struct { func main() { var file *os.File var err error - if file, err = os.Open("tiny2.ts"); err != nil { + if file, err = os.Open("/tmp/out.ts"); err != nil { return } diff --git a/reader.go b/reader.go index 96cc43b..9012cf5 100644 --- a/reader.go +++ b/reader.go @@ -30,7 +30,7 @@ func ReadUInt64(r io.Reader, n int) (res uint64, err error) { if res32, err = ReadUInt(r, n-4); err != nil { return } - res |= uint64(res32)<<(uint(n-4)*8) + res |= uint64(res32)<<32 n = 4 } if res32, err = ReadUInt(r, n); err != nil { @@ -119,16 +119,24 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { // PCR if flags & 0x10 != 0 { - if self.PCR, err = ReadUInt64(lr, 6); err != nil { + var v uint64 + if v, err = ReadUInt64(lr, 6); err != nil { return } + // clock is 27MHz + self.PCR = UIntToPCR(v) + if debug { + fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/27000000) + } } // OPCR if flags & 0x08 != 0 { - if self.OPCR, err = ReadUInt64(lr, 6); err != nil { + var v uint64 + if v, err = ReadUInt64(lr, 6); err != nil { return } + self.OPCR = UIntToPCR(v) } // Splice countdown @@ -506,6 +514,10 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } self.PTS = PESUIntToTs(v) + + if debug { + fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/90000) + } } if flags & 0x40 != 0 && flags & 0x80 != 0 { @@ -514,6 +526,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } self.DTS = PESUIntToTs(v) + fmt.Printf("pes: dts %d\n", self.PTS) } // ESCR flag diff --git a/ts.go b/ts.go index 06c9c75..4cf1ff4 100644 --- a/ts.go +++ b/ts.go @@ -7,6 +7,11 @@ import ( const debug = true +const ( + ElementaryStreamTypeH264 = 0x1B + ElementaryStreamTypeAdtsAAC = 0x0F +) + type TSHeader struct { PID uint PCR uint64 @@ -15,11 +20,6 @@ type TSHeader struct { PayloadUnitStart bool } -const ( - ElementaryStreamTypeH264 = 0x1B - ElementaryStreamTypeAdtsAAC = 0x0F -) - type PATEntry struct { ProgramNumber uint NetworkPID uint @@ -67,6 +67,13 @@ func PESUIntToTs(v uint64) (ts uint64) { return (((v>>33)&0x7)<<30) | (((v>>17)&0xef)<<15) | ((v>>1)&0xef) } +func UIntToPCR(v uint64) uint64 { + // base(33)+resverd(6)+ext(9) + base := v>>15 + ext := v&0x1ff + return base*300+ext +} + type FieldsDumper struct { Fields []struct { Length int diff --git a/writer.go b/writer.go index 9b7bfd3..6b5405c 100644 --- a/writer.go +++ b/writer.go @@ -6,7 +6,40 @@ import ( "io" ) -type SimpleH264Writer struct { +type TSWriter struct { W io.Writer } +type PSIWriter struct { + W *TSWriter +} + +func (self PSIWriter) Write(b []byte) (err error) { + return +} + +func (self PSIWriter) Finish() (err error) { + return +} + +type PESWriter struct { + W io.Writer +} + +type SimpleH264Writer struct { + W io.Writer + headerHasWritten bool +} + +func WritePAT(w io.Writer, self PAT) (err error) { + return +} + +func (self *SimpleH264Writer) WriteSample(data []byte) (err error) { + return +} + +func (self *SimpleH264Writer) WriteNALU(data []byte) (err error) { + return +} + From 77392eddb23910049a7706d89758702aadfcab78 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 5 Dec 2015 14:26:52 +0800 Subject: [PATCH 05/82] fix PESUIntToTs problem --- example/test.go | 8 +++++--- reader.go | 3 ++- ts.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/example/test.go b/example/test.go index 0b11c3f..7666aed 100644 --- a/example/test.go +++ b/example/test.go @@ -15,6 +15,7 @@ type Stream struct { Header *ts.PESHeader Title string Data bytes.Buffer + Type uint } func main() { @@ -39,10 +40,11 @@ func main() { if stream == nil { stream = &Stream{ PID: pid, + Type: info.StreamType, } - if info.StreamType == ts.ElementaryStreamTypeH264 { + if stream.Type == ts.ElementaryStreamTypeH264 { stream.Title = "h264" - } else if info.StreamType == ts.ElementaryStreamTypeAdtsAAC { + } else if stream.Type == ts.ElementaryStreamTypeAdtsAAC { stream.Title = "aac" } streams[pid] = stream @@ -64,7 +66,7 @@ func main() { return } if stream.Data.Len() == int(stream.Header.DataLength) { - fmt.Println(stream.Title, stream.Data.Len(), "total") + fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") fmt.Println(hex.Dump(stream.Data.Bytes())) } return diff --git a/reader.go b/reader.go index 9012cf5..6054f8a 100644 --- a/reader.go +++ b/reader.go @@ -516,7 +516,8 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if debug { - fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/90000) + fmt.Printf("pes: pts %x(%x)=>%x %f\n", + v, (v>>1)&0xef, self.PTS, float64(self.PTS)/90000) } } diff --git a/ts.go b/ts.go index 4cf1ff4..bfebd1b 100644 --- a/ts.go +++ b/ts.go @@ -64,7 +64,7 @@ type PESHeader struct { func PESUIntToTs(v uint64) (ts uint64) { // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 - return (((v>>33)&0x7)<<30) | (((v>>17)&0xef)<<15) | ((v>>1)&0xef) + return (((v>>33)&0x7)<<30) | (((v>>17)&0x7fff)<<15) | ((v>>1)&0x7fff) } func UIntToPCR(v uint64) uint64 { From 264d172b2ba62c0f9e155c91ea24b574a6573997 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 5 Dec 2015 17:12:59 +0800 Subject: [PATCH 06/82] udpate convts.sh --- example/convts.sh | 4 +++- reader.go | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/example/convts.sh b/example/convts.sh index ca86cce..7623ed2 100644 --- a/example/convts.sh +++ b/example/convts.sh @@ -1,2 +1,4 @@ -avconv -i tiny2.mov -acodec aac -vcodec h264 -strict experimental tiny2.ts +./avconv -loglevel debug -y -i /tmp/tiny2.mov \ + -bsf h264_mp4toannexb -acodec copy -vcodec copy -strict experimental /tmp/out.ts 2>&1 + diff --git a/reader.go b/reader.go index 6054f8a..347481e 100644 --- a/reader.go +++ b/reader.go @@ -68,6 +68,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { {13, "pid"}, {2, "scrambling_control"}, {1, "adaptation_field_flag"}, + {1, "payload_flag"}, {4, "continuity_counter"}, }, Val: flags, @@ -76,6 +77,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } if flags & 0x400000 != 0 { + // When set to '1' it indicates that this TS packet contains the first PES packet. self.PayloadUnitStart = true } @@ -516,8 +518,8 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if debug { - fmt.Printf("pes: pts %x(%x)=>%x %f\n", - v, (v>>1)&0xef, self.PTS, float64(self.PTS)/90000) + fmt.Printf("pes: pts %x=>%x %f\n", + v, self.PTS, float64(self.PTS)/90000) } } From 5680c3b1027411a05bc7a02b62a4b41db3c96dc7 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 5 Dec 2015 22:05:25 +0800 Subject: [PATCH 07/82] finish WriteTSHeader --- example/test.go | 49 ++++++++++++++++++++++--- reader.go | 4 +- ts.go | 7 ++++ writer.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 7 deletions(-) diff --git a/example/test.go b/example/test.go index 7666aed..b279abc 100644 --- a/example/test.go +++ b/example/test.go @@ -16,9 +16,25 @@ type Stream struct { Title string Data bytes.Buffer Type uint + PTS uint + PCR uint } -func main() { +type Sample struct { + Type uint + PCR uint64 + PTS uint64 + DTS uint64 + Data []byte +} + +func readSamples(ch chan Sample) { + defer func() { + close(ch) + }() + + debug := false + var file *os.File var err error if file, err = os.Open("/tmp/out.ts"); err != nil { @@ -66,8 +82,14 @@ func main() { return } if stream.Data.Len() == int(stream.Header.DataLength) { - fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") - fmt.Println(hex.Dump(stream.Data.Bytes())) + if debug { + fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") + fmt.Println(hex.Dump(stream.Data.Bytes())) + } + ch <- Sample{ + Type: stream.Type, + Data: stream.Data.Bytes(), + } } return } @@ -76,7 +98,9 @@ func main() { if header, n, err = ts.ReadTSPacket(file, data[:]); err != nil { return } - fmt.Println(header, n) + if debug { + fmt.Println(header, n) + } payload = data[:n] pr := bytes.NewReader(payload) @@ -94,7 +118,7 @@ func main() { return } //fmt.Println("pmt", pmt) - if true { + if debug { fmt.Println(hex.Dump(payload)) } } @@ -109,3 +133,18 @@ func main() { } } +func main() { + ch := make(chan Sample) + go readSamples(ch) + + for { + var sample Sample + var ok bool + if sample, ok = <-ch; !ok { + break + } + if sample.Type == ts.ElementaryStreamTypeH264 { + } + } +} + diff --git a/reader.go b/reader.go index 347481e..c50a719 100644 --- a/reader.go +++ b/reader.go @@ -56,7 +56,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } if debug { - fmt.Printf("ts: %s\n", FieldsDumper{ + fmt.Printf("ts: flags %s\n", FieldsDumper{ Fields: []struct{ Length int Desc string @@ -100,7 +100,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } if debug { - fmt.Printf("ts: %s\n", FieldsDumper{ + fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ Fields: []struct{ Length int Desc string diff --git a/ts.go b/ts.go index bfebd1b..30b830d 100644 --- a/ts.go +++ b/ts.go @@ -18,6 +18,7 @@ type TSHeader struct { OPCR uint64 ContinuityCounter uint PayloadUnitStart bool + RandomAccessIndicator bool } type PATEntry struct { @@ -74,6 +75,12 @@ func UIntToPCR(v uint64) uint64 { return base*300+ext } +func PCRToUInt(pcr uint64) uint64 { + base := pcr/300 + ext := pcr%300 + return base<<15|0x3f<<9|ext +} + type FieldsDumper struct { Fields []struct { Length int diff --git a/writer.go b/writer.go index 6b5405c..8bce989 100644 --- a/writer.go +++ b/writer.go @@ -8,6 +8,103 @@ import ( type TSWriter struct { W io.Writer + ContinuityCounter uint + PayloadUnitStart bool +} + +func WriteUInt64(w io.Writer, val uint64, n int) (err error) { + var b [8]byte + for i := n-1; i >= 0; i-- { + b[i] = byte(val) + val >>= 8 + } + if _, err = w.Write(b[:]); err != nil { + return + } + return +} + +func WriteUInt(w io.Writer, val uint, n int) (err error) { + return WriteUInt64(w, uint64(val), n) +} + +func WriteTSHeader(w io.Writer, self TSHeader) (err error) { + var flags, extFlags uint + + // sync(8) + // transport_error_indicator(1) + // payload_unit_start_indicator(1) + // transport_priority(1) + // pid(13) + // Scrambling control(2) + // Adaptation field flag(1) 0x20 + // Payload flag(1) 0x10 + // Continuity counter(4) + + flags = 0x47<<24 + flags |= 0x10 + if self.PayloadUnitStart { + flags |= 0x400000 + } + flags |= (self.PID&0x1fff00)<<8 + flags |= self.ContinuityCounter&0xf + + if self.PCR != 0 { + extFlags |= 0x20 + } + if self.OPCR != 0 { + extFlags |= 0x08 + } + if self.RandomAccessIndicator { + extFlags |= 0x40 + } + + if extFlags != 0 { + flags |= 0x20 + } + + if err = WriteUInt(w, flags, 4); err != nil { + return + } + + if flags & 0x20 != 0 { + var length uint + + // Discontinuity indicator 1 0x80 + // Random Access indicator 1 0x40 + // Elementary stream priority indicator 1 0x20 + // PCR flag 1 0x10 + // OPCR flag 1 0x08 + + length = 1 + if extFlags & 0x10 != 0 { + length += 6 + } + if extFlags & 0x08 != 0 { + length += 6 + } + + if err = WriteUInt(w, length, 1); err != nil { + return + } + if err = WriteUInt(w, extFlags, 1); err != nil { + return + } + + if extFlags & 0x10 != 0 { + if err = WriteUInt64(w, PCRToUInt(self.PCR), 6); err != nil { + return + } + } + + if extFlags & 0x08 != 0 { + if err = WriteUInt64(w, PCRToUInt(self.OPCR), 6); err != nil { + return + } + } + } + + return } type PSIWriter struct { From 13160821be99b600341d637649288547fc8e47ed Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 5 Dec 2015 23:09:48 +0800 Subject: [PATCH 08/82] add WritePES/PSI --- checksum.go | 17 ++++-- reader.go | 6 +- ts.go | 5 ++ writer.go | 155 +++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 167 insertions(+), 16 deletions(-) diff --git a/checksum.go b/checksum.go index ae73093..6c2d97e 100644 --- a/checksum.go +++ b/checksum.go @@ -67,10 +67,6 @@ type Crc32Reader struct { var debugCrc32 = false -func NewCrc32Reader(r io.Reader) *Crc32Reader { - return &Crc32Reader{R: r, Crc32: 0xffffffff} -} - func (self *Crc32Reader) Read(b []byte) (n int, err error) { if n, err = self.R.Read(b); err != nil { return @@ -93,3 +89,16 @@ func (self *Crc32Reader) ReadCrc32UIntAndCheck() (err error) { return } +type Crc32Writer struct { + W io.Writer + Crc32 uint32 +} + +func (self *Crc32Writer) Write(b []byte) (n int, err error) { + if n, err = self.W.Write(b); err != nil { + return + } + self.Crc32 = updateIeeeCrc32(self.Crc32, b) + return +} + diff --git a/reader.go b/reader.go index c50a719..8b2b154 100644 --- a/reader.go +++ b/reader.go @@ -202,7 +202,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } } - cr = NewCrc32Reader(r) + cr = &Crc32Reader{R: r, Crc32: 0xffffffff} // table_id if self.TableId, err = ReadUInt(cr, 1); err != nil { @@ -463,8 +463,8 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { Length int Desc string }{ - {2, "PES_scrambling_control"}, - {1, "PES_priority"}, + {2, "scrambling_control"}, + {1, "priority"}, {1, "data_alignment_indicator"}, {1, "copyright"}, {1, "original_or_copy"}, diff --git a/ts.go b/ts.go index 30b830d..c59ade7 100644 --- a/ts.go +++ b/ts.go @@ -68,6 +68,11 @@ func PESUIntToTs(v uint64) (ts uint64) { return (((v>>33)&0x7)<<30) | (((v>>17)&0x7fff)<<15) | ((v>>1)&0x7fff) } +func PESTsToUInt(ts uint64) (v uint64) { + // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 + return ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (v&0x7fff)<<1 | 0x100010001 +} + func UIntToPCR(v uint64) uint64 { // base(33)+resverd(6)+ext(9) base := v>>15 diff --git a/writer.go b/writer.go index 8bce989..e7cfac8 100644 --- a/writer.go +++ b/writer.go @@ -107,20 +107,157 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { return } -type PSIWriter struct { - W *TSWriter -} +func WritePSI(w io.Writer, self PSI, data []byte) (err error) { + // pointer(8) + // table_id(8) + // reserved(4)=0xb,section_length(10) + // Table ID extension(16) + // resverd(2)=3,version(5),Current_next_indicator(1) + // section_number(8) + // last_section_number(8) + // data + // crc(32) + + // pointer(8) + if err = WriteUInt(w, 0, 1); err != nil { + return + } + + cw := &Crc32Writer{W: w, Crc32: 0xffffffff} + + // table_id(8) + if err = WriteUInt(cw, self.TableId, 1); err != nil { + return + } + + // reserved(4)=0xb,section_length(10) + var flags, length uint + length = 2+3+4+uint(len(data)) + flags = 0xb<<10|length + if err = WriteUInt(cw, flags, 1); err != nil { + return + } + + // Table ID extension(16) + if err = WriteUInt(cw, self.TableIdExtension, 2); err != nil { + return + } + + // resverd(2)=3,version(5)=0,Current_next_indicator(1)=1 + flags = 0x3<<6|1 + if err = WriteUInt(cw, flags, 1); err != nil { + return + } + + // section_number(8) + if err = WriteUInt(cw, self.SecNum, 1); err != nil { + return + } + + // last_section_number(8) + if err = WriteUInt(cw, self.LastSecNum, 1); err != nil { + return + } + + // data + if _, err = cw.Write(data); err != nil { + return + } + + // crc(32) + if err = WriteUInt(w, uint(cw.Crc32), 4); err != nil { + return + } -func (self PSIWriter) Write(b []byte) (err error) { return } -func (self PSIWriter) Finish() (err error) { - return -} +func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { + // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html -type PESWriter struct { - W io.Writer + var pts_dts_flags, header_length, packet_length uint + + // start code(24) 000001 + // StreamId(8) + // packet_length(16) + // resverd(6,2)=2,original_or_copy(0,1)=1 + // pts_dts_flags(6,2) + // header_length(8) + // pts(40)? + // dts(40)? + // data + + // start code(24) 000001 + if err = WriteUInt(w, 0x000001, 3); err != nil { + return + } + + // StreamId(8) + if err = WriteUInt(w, self.StreamId, 1); err != nil { + return + } + + const PTS = 1<<7 + const DTS = 1<<6 + + if self.PTS != 0 { + pts_dts_flags |= PTS + if self.DTS != 0 { + pts_dts_flags |= DTS + } + } + + if pts_dts_flags & PTS != 0 { + header_length += 5 + } + if pts_dts_flags & DTS != 0 { + header_length += 5 + } + packet_length = 3+header_length+uint(len(data)) + + // packet_length(16) + if err = WriteUInt(w, packet_length, 2); err != nil { + return + } + + // resverd(6,2)=2,original_or_copy(0,1)=1 + if err = WriteUInt(w, 2<<6|1, 1); err != nil { + return + } + + // pts_dts_flags(6,2) + if err = WriteUInt(w, pts_dts_flags, 1); err != nil { + return + } + + // header_length(8) + if err = WriteUInt(w, header_length, 1); err != nil { + return + } + + // pts(40)? + // dts(40)? + if pts_dts_flags & PTS != 0 { + if pts_dts_flags & DTS != 0 { + if err = WriteUInt64(w, PESTsToUInt(self.PTS)|3<<36, 5); err != nil { + return + } + if err = WriteUInt64(w, PESTsToUInt(self.DTS)|1<<36, 5); err != nil { + return + } + } else { + if err = WriteUInt64(w, PESTsToUInt(self.PTS)|2<<36, 5); err != nil { + return + } + } + } + + // data + if _, err = w.Write(data); err != nil { + return + } + + return } type SimpleH264Writer struct { From 19764e2fba0e79b682e160e185144f0aee7ea163 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 6 Dec 2015 10:06:40 +0800 Subject: [PATCH 09/82] add WritePMT/PAT --- reader.go | 2 +- writer.go | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/reader.go b/reader.go index 8b2b154..06426e5 100644 --- a/reader.go +++ b/reader.go @@ -376,8 +376,8 @@ func ReadPMT(r io.Reader) (self PMT, err error) { } if debug { + fmt.Printf("pmt: ProgramDescriptors %v\n", self.ProgramDescriptors) fmt.Printf("pmt: ElementaryStreamInfos %v\n", self.ElementaryStreamInfos) - } if err = cr.ReadCrc32UIntAndCheck(); err != nil { diff --git a/writer.go b/writer.go index e7cfac8..6465168 100644 --- a/writer.go +++ b/writer.go @@ -4,6 +4,7 @@ package ts import ( _ "fmt" "io" + "bytes" ) type TSWriter struct { @@ -260,15 +261,118 @@ func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { return } +func WritePAT(w io.Writer, self PAT) (err error) { + bw := &bytes.Buffer{} + + for _, entry := range self.Entries { + if err = WriteUInt(bw, entry.ProgramNumber, 2); err != nil { + return + } + if entry.ProgramNumber == 0 { + if err = WriteUInt(bw, entry.NetworkPID&0x1fff|7<<13, 2); err != nil { + return + } + } else { + if err = WriteUInt(bw, entry.ProgramMapPID&0x1fff|7<<13, 2); err != nil { + return + } + } + } + + psi := PSI { + TableIdExtension: 1, + } + if err = WritePSI(w, psi, bw.Bytes()); err != nil { + return + } + + return +} + +func WritePMT(w io.Writer, self PMT) (err error) { + writeDescs := func(w io.Writer, descs []Descriptor) (err error) { + for _, desc := range descs { + if err = WriteUInt(w, desc.Tag, 1); err != nil { + return + } + if err = WriteUInt(w, uint(len(desc.Data)), 1); err != nil { + return + } + if _, err = w.Write(desc.Data); err != nil { + return + } + } + return + } + + writeBody := func(w io.Writer) (err error) { + if err = writeDescs(w, self.ProgramDescriptors); err != nil { + return + } + + for _, info := range self.ElementaryStreamInfos { + if err = WriteUInt(w, info.StreamType, 1); err != nil { + return + } + + // Reserved(3) + // Elementary PID(13) + if err = WriteUInt(w, info.ElementaryPID|7<<13, 2); err != nil { + return + } + + bw := &bytes.Buffer{} + if err = writeDescs(bw, info.Descriptors); err != nil { + return + } + + // Reserved(6) + // ES Info length length(10) + if err = WriteUInt(w, uint(bw.Len())|0x3f<<10, 2); err != nil { + return + } + + if _, err = w.Write(bw.Bytes()); err != nil { + return + } + } + + return + } + + writeAll := func(w io.Writer) (err error) { + if err = WriteUInt(w, self.PCRPID|7<<13, 2); err != nil { + return + } + + bw := &bytes.Buffer{} + if err = writeBody(bw); err != nil { + return + } + return + } + + bw := &bytes.Buffer{} + if err = writeAll(bw); err != nil { + return + } + + psi := PSI { + TableId: 2, + TableIdExtension: 1, + } + if err = WritePSI(w, psi, bw.Bytes()); err != nil { + return + } + + return +} + type SimpleH264Writer struct { W io.Writer headerHasWritten bool } -func WritePAT(w io.Writer, self PAT) (err error) { - return -} - func (self *SimpleH264Writer) WriteSample(data []byte) (err error) { return } From 5d520f0d72705c2f1530f829506b333b4d48ea7d Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 6 Dec 2015 10:38:14 +0800 Subject: [PATCH 10/82] bugfix: read samples --- example/test.go | 11 ++++++++--- reader.go | 8 +++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/example/test.go b/example/test.go index b279abc..22ade5e 100644 --- a/example/test.go +++ b/example/test.go @@ -16,8 +16,7 @@ type Stream struct { Title string Data bytes.Buffer Type uint - PTS uint - PCR uint + PCR uint64 } type Sample struct { @@ -77,6 +76,7 @@ func readSamples(ch chan Sample) { if stream.Header, err = ts.ReadPESHeader(lr); err != nil { return } + stream.PCR = header.PCR } if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { return @@ -89,6 +89,9 @@ func readSamples(ch chan Sample) { ch <- Sample{ Type: stream.Type, Data: stream.Data.Bytes(), + PTS: stream.Header.PTS, + DTS: stream.Header.DTS, + PCR: stream.PCR, } } return @@ -134,7 +137,7 @@ func readSamples(ch chan Sample) { } func main() { - ch := make(chan Sample) + ch := make(chan Sample, 0) go readSamples(ch) for { @@ -144,6 +147,8 @@ func main() { break } if sample.Type == ts.ElementaryStreamTypeH264 { + fmt.Println("sample", len(sample.Data), "PCR", sample.PCR) + fmt.Print(hex.Dump(sample.Data)) } } } diff --git a/reader.go b/reader.go index 06426e5..b24f1cb 100644 --- a/reader.go +++ b/reader.go @@ -419,6 +419,10 @@ func ReadPAT(r io.Reader) (self PAT, err error) { return } + if debug { + fmt.Printf("pat: %v\n", self) + } + return } @@ -529,7 +533,9 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } self.DTS = PESUIntToTs(v) - fmt.Printf("pes: dts %d\n", self.PTS) + if debug { + fmt.Printf("pes: dts %d\n", self.PTS) + } } // ESCR flag From 57a7d68cf32a208a9adaf92ef0289c1266ca1cff Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 6 Dec 2015 16:55:18 +0800 Subject: [PATCH 11/82] fix WriteUInt --- checksum.go | 6 ++-- example/test.go | 51 +++++++++++++++++++++++++++---- reader.go | 47 ++++++++++++++--------------- ts.go | 2 -- writer.go | 79 +++++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 142 insertions(+), 43 deletions(-) diff --git a/checksum.go b/checksum.go index 6c2d97e..61fe192 100644 --- a/checksum.go +++ b/checksum.go @@ -65,13 +65,13 @@ type Crc32Reader struct { Crc32 uint32 } -var debugCrc32 = false +var DebugCrc32 = false func (self *Crc32Reader) Read(b []byte) (n int, err error) { if n, err = self.R.Read(b); err != nil { return } - if debugCrc32 { + if DebugCrc32 { fmt.Println("crc32: update", hex.EncodeToString(b)) } self.Crc32 = updateIeeeCrc32(self.Crc32, b) @@ -83,7 +83,7 @@ func (self *Crc32Reader) ReadCrc32UIntAndCheck() (err error) { return } if self.Crc32 != 0 { - err = fmt.Errorf("crc32 != 0") + err = fmt.Errorf("crc32(%x) != 0", self.Crc32) return } return diff --git a/example/test.go b/example/test.go index 22ade5e..6ab86a1 100644 --- a/example/test.go +++ b/example/test.go @@ -8,6 +8,7 @@ import ( ts "../" "fmt" "encoding/hex" + "flag" ) type Stream struct { @@ -27,7 +28,7 @@ type Sample struct { Data []byte } -func readSamples(ch chan Sample) { +func readSamples(filename string, ch chan Sample) { defer func() { close(ch) }() @@ -36,7 +37,7 @@ func readSamples(ch chan Sample) { var file *os.File var err error - if file, err = os.Open("/tmp/out.ts"); err != nil { + if file, err = os.Open(filename); err != nil { return } @@ -137,8 +138,46 @@ func readSamples(ch chan Sample) { } func main() { + input := flag.String("i", "", "input file") + output := flag.String("o", "", "output file") + flag.Parse() + + var file *os.File + var err error + ch := make(chan Sample, 0) - go readSamples(ch) + go readSamples(*input, ch) + + if *output != "" { + if file, err = os.Create(*output); err != nil { + return + } + } + + writePAT := func() (err error) { + w := &ts.TSWriter{ + W: file, + PID: 0, + } + pat := ts.PAT { + Entries: []ts.PATEntry{ + {ProgramNumber: 1, ProgramMapPID: 4096}, + }, + } + bw := &bytes.Buffer{} + if err = ts.WritePAT(bw, pat); err != nil { + return + } + if err = w.Write(bw.Bytes(), false); err != nil { + return + } + return + } + + if file != nil { + writePAT() + file.Close() + } for { var sample Sample @@ -147,8 +186,10 @@ func main() { break } if sample.Type == ts.ElementaryStreamTypeH264 { - fmt.Println("sample", len(sample.Data), "PCR", sample.PCR) - fmt.Print(hex.Dump(sample.Data)) + if false { + fmt.Println("sample: ", len(sample.Data), "PCR", sample.PCR) + //fmt.Print(hex.Dump(sample.Data)) + } } } } diff --git a/reader.go b/reader.go index b24f1cb..dc49f9a 100644 --- a/reader.go +++ b/reader.go @@ -7,6 +7,8 @@ import ( "io/ioutil" ) +var DebugReader = true + func ReadUInt(r io.Reader, n int) (res uint, err error) { var b [4]byte if _, err = r.Read(b[0:n]); err != nil { @@ -55,7 +57,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { return } - if debug { + if DebugReader { fmt.Printf("ts: flags %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -99,7 +101,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { return } - if debug { + if DebugReader { fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -127,7 +129,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } // clock is 27MHz self.PCR = UIntToPCR(v) - if debug { + if DebugReader { fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/27000000) } } @@ -168,7 +170,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { return } - if debug { + if DebugReader { // rubish //fmt.Println("ts: ", data) } @@ -216,17 +218,8 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } length = flags & 0x3FF - if debug { - fmt.Printf("psi: %s\n", FieldsDumper{ - Fields: []struct{ - Length int - Desc string - }{ - {4, "reserved"}, - }, - Val: flags, - Length: 16, - }) + if DebugReader { + fmt.Printf("psi: tableid=%d len=%d\n", self.TableId, length) } lr = &io.LimitedReader{R: cr, N: int64(length)} @@ -243,7 +236,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err return } - if debug { + if DebugReader { fmt.Printf("psi: %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -268,7 +261,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err return } - if debug { + if DebugReader { fmt.Printf("psi: table_id=%x table_extension=%x secnum=%x lastsecnum=%x\n", self.TableId, self.TableIdExtension, @@ -299,7 +292,7 @@ func ReadPMT(r io.Reader) (self PMT, err error) { } self.PCRPID = flags & 0x1fff - if debug { + if DebugReader { fmt.Printf("pmt: %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -375,12 +368,15 @@ func ReadPMT(r io.Reader) (self PMT, err error) { self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info) } - if debug { + if DebugReader { fmt.Printf("pmt: ProgramDescriptors %v\n", self.ProgramDescriptors) fmt.Printf("pmt: ElementaryStreamInfos %v\n", self.ElementaryStreamInfos) } if err = cr.ReadCrc32UIntAndCheck(); err != nil { + if DebugReader { + fmt.Printf("pmt: %s\n", err) + } return } @@ -416,10 +412,13 @@ func ReadPAT(r io.Reader) (self PAT, err error) { } if err = cr.ReadCrc32UIntAndCheck(); err != nil { + if DebugReader { + fmt.Printf("pat: %s\n", err) + } return } - if debug { + if DebugReader { fmt.Printf("pat: %v\n", self) } @@ -461,7 +460,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } - if debug { + if DebugReader { fmt.Printf("pes: %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -489,7 +488,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } - if debug { + if DebugReader { fmt.Printf("pes: %s\n", FieldsDumper{ Fields: []struct{ Length int @@ -521,7 +520,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } self.PTS = PESUIntToTs(v) - if debug { + if DebugReader { fmt.Printf("pes: pts %x=>%x %f\n", v, self.PTS, float64(self.PTS)/90000) } @@ -533,7 +532,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } self.DTS = PESUIntToTs(v) - if debug { + if DebugReader { fmt.Printf("pes: dts %d\n", self.PTS) } } diff --git a/ts.go b/ts.go index c59ade7..c0e943e 100644 --- a/ts.go +++ b/ts.go @@ -5,8 +5,6 @@ import ( "fmt" ) -const debug = true - const ( ElementaryStreamTypeH264 = 0x1B ElementaryStreamTypeAdtsAAC = 0x0F diff --git a/writer.go b/writer.go index 6465168..73b62ea 100644 --- a/writer.go +++ b/writer.go @@ -2,16 +2,12 @@ package ts import ( - _ "fmt" + "fmt" "io" "bytes" ) -type TSWriter struct { - W io.Writer - ContinuityCounter uint - PayloadUnitStart bool -} +const DebugWriter = true func WriteUInt64(w io.Writer, val uint64, n int) (err error) { var b [8]byte @@ -19,7 +15,7 @@ func WriteUInt64(w io.Writer, val uint64, n int) (err error) { b[i] = byte(val) val >>= 8 } - if _, err = w.Write(b[:]); err != nil { + if _, err = w.Write(b[:n]); err != nil { return } return @@ -108,6 +104,63 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { return } +type TSWriter struct { + W io.Writer + PID uint + PCR uint64 + OPCR uint64 + ContinuityCounter uint +} + +func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { + for i := 0; len(b) > 0; i++ { + header := TSHeader{ + PID: self.PID, + PCR: self.PCR, + OPCR: self.OPCR, + ContinuityCounter: self.ContinuityCounter, + RandomAccessIndicator: RandomAccessIndicator, + } + if i == 0 { + header.PayloadUnitStart = true + } + bw := &bytes.Buffer{} + if err = WriteTSHeader(bw, header); err != nil { + return + } + + var data []byte + dataLen := 188-bw.Len() + + if DebugWriter { + fmt.Printf("tsw: datalen=%d blen=%d\n", dataLen, len(b)) + } + + if len(b) > dataLen { + data = b[:dataLen] + b = b[dataLen:] + } else { + data = make([]byte, dataLen) + copy(data, b) + for i := len(b); i < dataLen; i++ { + data[i] = 0xff + } + b = b[len(b):] + } + + if _, err = self.W.Write(bw.Bytes()); err != nil { + return + } + if _, err = self.W.Write(data); err != nil { + return + } + + self.ContinuityCounter++ + } + + return +} + func WritePSI(w io.Writer, self PSI, data []byte) (err error) { // pointer(8) // table_id(8) @@ -135,10 +188,14 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { var flags, length uint length = 2+3+4+uint(len(data)) flags = 0xb<<10|length - if err = WriteUInt(cw, flags, 1); err != nil { + if err = WriteUInt(cw, flags, 2); err != nil { return } + if DebugWriter { + fmt.Printf("wpsi: flags=%x\n", flags) + } + // Table ID extension(16) if err = WriteUInt(cw, self.TableIdExtension, 2); err != nil { return @@ -166,13 +223,17 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { } // crc(32) - if err = WriteUInt(w, uint(cw.Crc32), 4); err != nil { + if err = WriteUInt(w, bswap32(uint(cw.Crc32)), 4); err != nil { return } return } +func bswap32(v uint) uint { + return (v>>24)|((v>>16)&0xff)<<8|((v>>8)&0xff)<<16|(v&0xff)<<24 +} + func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html From 848a9b2a397eab871474b45ae7fe98a3e3010e29 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 6 Dec 2015 19:50:46 +0800 Subject: [PATCH 12/82] writePMT() bugfix --- example/test.go | 26 ++++++++++++++++++++++++-- reader.go | 41 +++++++++++++++++++++++------------------ writer.go | 28 ++++++++++++++-------------- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/example/test.go b/example/test.go index 6ab86a1..f305a34 100644 --- a/example/test.go +++ b/example/test.go @@ -159,9 +159,9 @@ func main() { W: file, PID: 0, } - pat := ts.PAT { + pat := ts.PAT{ Entries: []ts.PATEntry{ - {ProgramNumber: 1, ProgramMapPID: 4096}, + {ProgramNumber: 1, ProgramMapPID: 0x1000}, }, } bw := &bytes.Buffer{} @@ -174,8 +174,30 @@ func main() { return } + writePMT := func() (err error) { + w := &ts.TSWriter{ + W: file, + PID: 0x1000, + } + pmt := ts.PMT{ + PCRPID: 0x100, + ElementaryStreamInfos: []ts.ElementaryStreamInfo{ + {StreamType: ts.ElementaryStreamTypeH264, ElementaryPID: 0x100}, + }, + } + bw := &bytes.Buffer{} + if err = ts.WritePMT(bw, pmt); err != nil { + return + } + if err = w.Write(bw.Bytes(), false); err != nil { + return + } + return + } + if file != nil { writePAT() + writePMT() file.Close() } diff --git a/reader.go b/reader.go index dc49f9a..23972de 100644 --- a/reader.go +++ b/reader.go @@ -275,6 +275,25 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } func ReadPMT(r io.Reader) (self PMT, err error) { + readDescs := func(lr *io.LimitedReader) (res []Descriptor, err error) { + var desc Descriptor + for lr.N > 0 { + if desc.Tag, err = ReadUInt(lr, 1); err != nil { + return + } + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + desc.Data = make([]byte, length) + if _, err = lr.Read(desc.Data); err != nil { + return + } + res = append(res, desc) + } + return + } + var lr *io.LimitedReader var cr *Crc32Reader //var psi PSI @@ -306,30 +325,16 @@ func ReadPMT(r io.Reader) (self PMT, err error) { }) } - // Reserved(6) + // Reserved(4)=0xf + // Reserved(2)=0x0 // Program info length(10) if flags, err = ReadUInt(lr, 2); err != nil { return } length = flags & 0x3ff - readDescs := func(lr *io.LimitedReader) (res []Descriptor, err error) { - var desc Descriptor - for lr.N > 0 { - if desc.Tag, err = ReadUInt(lr, 1); err != nil { - return - } - var length uint - if length, err = ReadUInt(lr, 1); err != nil { - return - } - desc.Data = make([]byte, length) - if _, err = lr.Read(desc.Data); err != nil { - return - } - res = append(res, desc) - } - return + if DebugReader { + fmt.Printf("pmt: ProgramDescriptorsLen=%d\n", length) } if length > 0 { diff --git a/writer.go b/writer.go index 73b62ea..d3945b5 100644 --- a/writer.go +++ b/writer.go @@ -367,7 +367,19 @@ func WritePMT(w io.Writer, self PMT) (err error) { } writeBody := func(w io.Writer) (err error) { - if err = writeDescs(w, self.ProgramDescriptors); err != nil { + if err = WriteUInt(w, self.PCRPID|7<<13, 2); err != nil { + return + } + + bw := &bytes.Buffer{} + if err = writeDescs(bw, self.ProgramDescriptors); err != nil { + return + } + + if err = WriteUInt(w, 0xf<<12|uint(bw.Len()), 2); err != nil { + return + } + if _, err = w.Write(bw.Bytes()); err != nil { return } @@ -401,20 +413,8 @@ func WritePMT(w io.Writer, self PMT) (err error) { return } - writeAll := func(w io.Writer) (err error) { - if err = WriteUInt(w, self.PCRPID|7<<13, 2); err != nil { - return - } - - bw := &bytes.Buffer{} - if err = writeBody(bw); err != nil { - return - } - return - } - bw := &bytes.Buffer{} - if err = writeAll(bw); err != nil { + if err = writeBody(bw); err != nil { return } From c1b42164ff045cfc14f9ffe7cba4c5f551726a79 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 11:37:07 +0800 Subject: [PATCH 13/82] add test --- checksum_test.go | 14 ++++++++++++++ writer_test.go | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 checksum_test.go create mode 100644 writer_test.go diff --git a/checksum_test.go b/checksum_test.go new file mode 100644 index 0000000..ecadf32 --- /dev/null +++ b/checksum_test.go @@ -0,0 +1,14 @@ + +package ts + +import ( + "testing" +) + +func TestChecksum(t *testing.T) { + b := []byte("hello world") + b = append(b, []byte{0xbb,0x08,0xec,0x87}...) + crc := updateIeeeCrc32(0xffffffff, b) + t.Logf("%x", crc) +} + diff --git a/writer_test.go b/writer_test.go new file mode 100644 index 0000000..3439b87 --- /dev/null +++ b/writer_test.go @@ -0,0 +1,19 @@ + +package ts + +import ( + "testing" + "encoding/hex" + "bytes" +) + +func TestWriteTSHeader(t *testing.T) { + bw := &bytes.Buffer{} + w := &TSWriter{ + W: bw, + PCR: 0x12345678, + } + w.Write([]byte{'h','e','l','o'}[:], false) + t.Logf("\n%s", hex.Dump(bw.Bytes())) +} + From 460813a749d73212caa2ba662d52108994867b51 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 13:11:40 +0800 Subject: [PATCH 14/82] bugfix: WriteTSHeader() missing PCR flag --- example/test.go | 31 ++++++++++++++++++++++++++++--- reader.go | 4 ++++ ts.go | 5 +++++ writer.go | 20 ++++++++++++-------- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/example/test.go b/example/test.go index f305a34..e97bb2e 100644 --- a/example/test.go +++ b/example/test.go @@ -195,23 +195,48 @@ func main() { return } + var w *ts.TSWriter + var sample Sample + writePES := func() (err error) { + pes := ts.PESHeader{ + StreamId: ts.StreamIdH264, + PTS: sample.PTS, + DTS: sample.DTS, + } + w.PCR = sample.PCR + bw := &bytes.Buffer{} + if err = ts.WritePES(bw, pes, sample.Data); err != nil { + return + } + if err = w.Write(bw.Bytes(), false); err != nil { + return + } + return + } + if file != nil { writePAT() writePMT() - file.Close() + w = &ts.TSWriter{ + W: file, + PID: 0x100, + } } for { - var sample Sample var ok bool if sample, ok = <-ch; !ok { break } if sample.Type == ts.ElementaryStreamTypeH264 { - if false { + if true { fmt.Println("sample: ", len(sample.Data), "PCR", sample.PCR) //fmt.Print(hex.Dump(sample.Data)) } + + if file != nil { + writePES() + } } } } diff --git a/reader.go b/reader.go index 23972de..648eff7 100644 --- a/reader.go +++ b/reader.go @@ -449,6 +449,10 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } + if DebugReader { + fmt.Printf("pes: StreamId=%x\n", self.StreamId) + } + if length, err = ReadUInt(r, 2); err != nil { return } diff --git a/ts.go b/ts.go index c0e943e..da6e546 100644 --- a/ts.go +++ b/ts.go @@ -53,6 +53,11 @@ type PSI struct { LastSecNum uint } +const ( + StreamIdH264 = 0xe0 + StreamIdAAC = 0xc0 +) + type PESHeader struct { StreamId uint // H264=0xe0 AAC=0xc0 DataLength uint diff --git a/writer.go b/writer.go index d3945b5..368bb24 100644 --- a/writer.go +++ b/writer.go @@ -46,25 +46,29 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { flags |= (self.PID&0x1fff00)<<8 flags |= self.ContinuityCounter&0xf + const PCR = 0x10 + const OPCR = 0x08 + const EXT = 0x20 + if self.PCR != 0 { - extFlags |= 0x20 + extFlags |= PCR } if self.OPCR != 0 { - extFlags |= 0x08 + extFlags |= OPCR } if self.RandomAccessIndicator { extFlags |= 0x40 } if extFlags != 0 { - flags |= 0x20 + flags |= EXT } if err = WriteUInt(w, flags, 4); err != nil { return } - if flags & 0x20 != 0 { + if flags & EXT != 0 { var length uint // Discontinuity indicator 1 0x80 @@ -74,10 +78,10 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { // OPCR flag 1 0x08 length = 1 - if extFlags & 0x10 != 0 { + if extFlags & PCR != 0 { length += 6 } - if extFlags & 0x08 != 0 { + if extFlags & OPCR != 0 { length += 6 } @@ -88,13 +92,13 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { return } - if extFlags & 0x10 != 0 { + if extFlags & PCR != 0 { if err = WriteUInt64(w, PCRToUInt(self.PCR), 6); err != nil { return } } - if extFlags & 0x08 != 0 { + if extFlags & OPCR != 0 { if err = WriteUInt64(w, PCRToUInt(self.OPCR), 6); err != nil { return } From 02ec81cd7a9cb65db19d1bfa676b06cf9e1e6dbb Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 21:03:51 +0800 Subject: [PATCH 15/82] bugfix: TSWriter not padding header length when data length is small --- example/test.go | 16 +++++++++--- reader.go | 23 +++++++++-------- ts.go | 1 + writer.go | 67 +++++++++++++++++++++++++++++++++---------------- 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/example/test.go b/example/test.go index e97bb2e..556baf7 100644 --- a/example/test.go +++ b/example/test.go @@ -68,6 +68,8 @@ func readSamples(filename string, ch chan Sample) { return } + debugStream := true + onStreamPayload := func() (err error) { stream := findOrCreateStream(header.PID) r := bytes.NewReader(payload) @@ -82,6 +84,11 @@ func readSamples(filename string, ch chan Sample) { if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { return } + + if debugStream { + fmt.Printf("stream: %d/%d\n", stream.Data.Len(), stream.Header.DataLength) + } + if stream.Data.Len() == int(stream.Header.DataLength) { if debug { fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") @@ -197,7 +204,7 @@ func main() { var w *ts.TSWriter var sample Sample - writePES := func() (err error) { + writeSample := func() (err error) { pes := ts.PESHeader{ StreamId: ts.StreamIdH264, PTS: sample.PTS, @@ -230,12 +237,15 @@ func main() { } if sample.Type == ts.ElementaryStreamTypeH264 { if true { - fmt.Println("sample: ", len(sample.Data), "PCR", sample.PCR) + fmt.Println("sample: ", len(sample.Data), + "PCR", sample.PCR, "PTS", sample.PTS, + "DTS", sample.DTS, + ) //fmt.Print(hex.Dump(sample.Data)) } if file != nil { - writePES() + writeSample() } } } diff --git a/reader.go b/reader.go index 648eff7..a28940a 100644 --- a/reader.go +++ b/reader.go @@ -165,15 +165,15 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { // Adaptation extension if lr.N > 0 { - data := make([]byte, lr.N) - if _, err = lr.Read(data); err != nil { + if DebugReader { + // rubish + fmt.Println("ts: skip", lr.N) + } + + if err = ReadDummy(lr, int(lr.N)); err != nil { return } - if DebugReader { - // rubish - //fmt.Println("ts: ", data) - } } } @@ -185,6 +185,9 @@ func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { if self, err = ReadTSHeader(lr); err != nil { return } + if DebugReader { + fmt.Println("ts: data", lr.N) + } if n, err = lr.Read(data[:lr.N]); err != nil { return } @@ -449,16 +452,16 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } - if DebugReader { - fmt.Printf("pes: StreamId=%x\n", self.StreamId) - } - if length, err = ReadUInt(r, 2); err != nil { return } lrAll := &io.LimitedReader{R: r, N: int64(length)} lr := lrAll + if DebugReader { + fmt.Printf("pes: StreamId=%x length=%d\n", self.StreamId, length) + } + // 10(2) // PES scrambling control(2) // PES priority(1) diff --git a/ts.go b/ts.go index da6e546..5babab6 100644 --- a/ts.go +++ b/ts.go @@ -17,6 +17,7 @@ type TSHeader struct { ContinuityCounter uint PayloadUnitStart bool RandomAccessIndicator bool + HeaderLength uint } type PATEntry struct { diff --git a/writer.go b/writer.go index 368bb24..28e5205 100644 --- a/writer.go +++ b/writer.go @@ -25,7 +25,16 @@ func WriteUInt(w io.Writer, val uint, n int) (err error) { return WriteUInt64(w, uint64(val), n) } -func WriteTSHeader(w io.Writer, self TSHeader) (err error) { +func WriteRepeatVal(w io.Writer, val byte, n int) (err error) { + b := make([]byte, n) + for i := range b { + b[i] = val + } + _, err = w.Write(b) + return +} + +func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { var flags, extFlags uint // sync(8) @@ -64,6 +73,11 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { flags |= EXT } + // need padding + if dataLength < 184 { + flags |= EXT + } + if err = WriteUInt(w, flags, 4); err != nil { return } @@ -77,7 +91,7 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { // PCR flag 1 0x10 // OPCR flag 1 0x08 - length = 1 + length = 1 // extFlags if extFlags & PCR != 0 { length += 6 } @@ -85,6 +99,17 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { length += 6 } + paddingLength := 0 + // need padding + if int(length) + 5 + dataLength < 188 { + paddingLength = 188 - dataLength - 5 - int(length) + length = 188 - uint(dataLength) - 5 + } + + if DebugWriter { + fmt.Printf("tsw: dataLength=%d paddingLength=%d\n", dataLength, paddingLength) + } + if err = WriteUInt(w, length, 1); err != nil { return } @@ -103,6 +128,12 @@ func WriteTSHeader(w io.Writer, self TSHeader) (err error) { return } } + + if paddingLength > 0 { + if err = WriteRepeatVal(w, 0xff, paddingLength); err != nil { + return + } + } } return @@ -120,36 +151,26 @@ func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { for i := 0; len(b) > 0; i++ { header := TSHeader{ PID: self.PID, - PCR: self.PCR, - OPCR: self.OPCR, ContinuityCounter: self.ContinuityCounter, - RandomAccessIndicator: RandomAccessIndicator, } + if i == 0 { header.PayloadUnitStart = true + header.PCR = self.PCR + header.OPCR = self.OPCR + header.RandomAccessIndicator = RandomAccessIndicator } + bw := &bytes.Buffer{} - if err = WriteTSHeader(bw, header); err != nil { + if err = WriteTSHeader(bw, header, len(b)); err != nil { return } - var data []byte - dataLen := 188-bw.Len() + data := b[:188-bw.Len()] + b = b[len(data):] if DebugWriter { - fmt.Printf("tsw: datalen=%d blen=%d\n", dataLen, len(b)) - } - - if len(b) > dataLen { - data = b[:dataLen] - b = b[dataLen:] - } else { - data = make([]byte, dataLen) - copy(data, b) - for i := len(b); i < dataLen; i++ { - data[i] = 0xff - } - b = b[len(b):] + fmt.Printf("tsw: datalen=%d blen=%d\n", len(data), len(b)) } if _, err = self.W.Write(bw.Bytes()); err != nil { @@ -281,6 +302,10 @@ func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { } packet_length = 3+header_length+uint(len(data)) + if DebugWriter { + fmt.Printf("pesw: packet_length=%d\n", packet_length) + } + // packet_length(16) if err = WriteUInt(w, packet_length, 2); err != nil { return From b929b7a5eb39af2486fc8b3de6cb89828050becd Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 21:19:09 +0800 Subject: [PATCH 16/82] bugfix: PESTsToUInt --- example/test.go | 2 ++ reader.go | 5 ++--- ts.go | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/example/test.go b/example/test.go index 556baf7..37ecf58 100644 --- a/example/test.go +++ b/example/test.go @@ -74,6 +74,7 @@ func readSamples(filename string, ch chan Sample) { stream := findOrCreateStream(header.PID) r := bytes.NewReader(payload) lr := &io.LimitedReader{R: r, N: int64(len(payload))} + if header.PayloadUnitStart { stream.Data = bytes.Buffer{} if stream.Header, err = ts.ReadPESHeader(lr); err != nil { @@ -81,6 +82,7 @@ func readSamples(filename string, ch chan Sample) { } stream.PCR = header.PCR } + if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { return } diff --git a/reader.go b/reader.go index a28940a..64e3340 100644 --- a/reader.go +++ b/reader.go @@ -533,8 +533,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: pts %x=>%x %f\n", - v, self.PTS, float64(self.PTS)/90000) + fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/90000) } } @@ -545,7 +544,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } self.DTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: dts %d\n", self.PTS) + fmt.Printf("pes: dts %d %f\n", self.DTS, float64(self.DTS)/90000) } } diff --git a/ts.go b/ts.go index 5babab6..56a909f 100644 --- a/ts.go +++ b/ts.go @@ -74,7 +74,7 @@ func PESUIntToTs(v uint64) (ts uint64) { func PESTsToUInt(ts uint64) (v uint64) { // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 - return ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (v&0x7fff)<<1 | 0x100010001 + return ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (ts&0x7fff)<<1 | 0x100010001 } func UIntToPCR(v uint64) uint64 { From 178e22d8c18a1c794bcab30e4d134a33f545dea9 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 21:19:23 +0800 Subject: [PATCH 17/82] add test --- ts_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ts_test.go diff --git a/ts_test.go b/ts_test.go new file mode 100644 index 0000000..0c6e84b --- /dev/null +++ b/ts_test.go @@ -0,0 +1,11 @@ + +package ts + +import ( + "testing" +) + +func TestPESTsConv(t *testing.T) { + t.Logf("%x", PESTsToUInt(0x123)) +} + From 1c7da5485a2adca956b30e4a14d832c3752c0f5a Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 21:49:17 +0800 Subject: [PATCH 18/82] add DisableHeaderPadding option for TSWriter --- example/test.go | 2 ++ writer.go | 27 +++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/example/test.go b/example/test.go index 37ecf58..3a033fd 100644 --- a/example/test.go +++ b/example/test.go @@ -167,6 +167,7 @@ func main() { w := &ts.TSWriter{ W: file, PID: 0, + DisableHeaderPadding: true, } pat := ts.PAT{ Entries: []ts.PATEntry{ @@ -187,6 +188,7 @@ func main() { w := &ts.TSWriter{ W: file, PID: 0x1000, + DisableHeaderPadding: true, } pmt := ts.PMT{ PCRPID: 0x100, diff --git a/writer.go b/writer.go index 28e5205..fee996e 100644 --- a/writer.go +++ b/writer.go @@ -25,12 +25,16 @@ func WriteUInt(w io.Writer, val uint, n int) (err error) { return WriteUInt64(w, uint64(val), n) } -func WriteRepeatVal(w io.Writer, val byte, n int) (err error) { +func makeRepeatValBytes(val byte, n int) []byte { b := make([]byte, n) for i := range b { b[i] = val } - _, err = w.Write(b) + return b +} + +func WriteRepeatVal(w io.Writer, val byte, n int) (err error) { + _, err = w.Write(makeRepeatValBytes(val, n)) return } @@ -145,6 +149,7 @@ type TSWriter struct { PCR uint64 OPCR uint64 ContinuityCounter uint + DisableHeaderPadding bool } func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { @@ -161,16 +166,26 @@ func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { header.RandomAccessIndicator = RandomAccessIndicator } + requestLength := len(b) + if self.DisableHeaderPadding { + requestLength = 188 + } + bw := &bytes.Buffer{} - if err = WriteTSHeader(bw, header, len(b)); err != nil { + if err = WriteTSHeader(bw, header, requestLength); err != nil { return } - data := b[:188-bw.Len()] - b = b[len(data):] + dataLen := 188-bw.Len() + if self.DisableHeaderPadding && len(b) < dataLen { + b = append(b, makeRepeatValBytes(0xff, dataLen - len(b))...) + } + + data := b[:dataLen] + b = b[dataLen:] if DebugWriter { - fmt.Printf("tsw: datalen=%d blen=%d\n", len(data), len(b)) + fmt.Printf("tsw: datalen=%d blen=%d\n", dataLen, len(b)) } if _, err = self.W.Write(bw.Bytes()); err != nil { From 4f3a0082d706cb65f7632d4552bec09b0e877a09 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 22:29:04 +0800 Subject: [PATCH 19/82] bugfix: WritePSI() invalid section_syntax_indicator and private_bit field --- writer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/writer.go b/writer.go index fee996e..c949603 100644 --- a/writer.go +++ b/writer.go @@ -206,7 +206,7 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { // table_id(8) // reserved(4)=0xb,section_length(10) // Table ID extension(16) - // resverd(2)=3,version(5),Current_next_indicator(1) + // section_syntax_indicator(1)=1,private_bit(1)=1,reserved(2)=3,unused(2)=0,section_length(10) // section_number(8) // last_section_number(8) // data @@ -224,16 +224,16 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { return } - // reserved(4)=0xb,section_length(10) + // section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10) var flags, length uint length = 2+3+4+uint(len(data)) - flags = 0xb<<10|length + flags = 0xa<<12|length if err = WriteUInt(cw, flags, 2); err != nil { return } if DebugWriter { - fmt.Printf("wpsi: flags=%x\n", flags) + fmt.Printf("wpsi: length=%d\n", length) } // Table ID extension(16) From e134dea4f4043df16210a85793ae1ee47051af46 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 23:30:43 +0800 Subject: [PATCH 20/82] add multiReadSeeker --- vecio.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 vecio.go diff --git a/vecio.go b/vecio.go new file mode 100644 index 0000000..fe5857b --- /dev/null +++ b/vecio.go @@ -0,0 +1,40 @@ + +package ts + +import ( + "io" +) + +func getSeekerLength(data io.Seeker) (length int64) { + length, _ = data.Seek(0, 2) + data.Seek(0, 0) + return +} + +type multiReadSeeker struct { + readers []io.ReadSeeker +} + +func (mr *multiReadSeeker) Seek(offset int64, whence int) (n int64, err error) { + if whence == 2 { + for _, reader := range mr.readers { + n += getSeekerLength(reader) + } + } + return +} + +func (mr *multiReadSeeker) Read(p []byte) (n int, err error) { + for len(mr.readers) > 0 { + n, err = mr.readers[0].Read(p) + if n > 0 || err != io.EOF { + if err == io.EOF { + err = nil + } + return + } + mr.readers = mr.readers[1:] + } + return 0, io.EOF +} + From 058b2a08571e7519abd4d01eb274c4d6cf7b3fdb Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 23:32:06 +0800 Subject: [PATCH 21/82] add PCR_HZ PTS_HZ --- reader.go | 6 +++--- ts.go | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/reader.go b/reader.go index 64e3340..ad43826 100644 --- a/reader.go +++ b/reader.go @@ -130,7 +130,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { // clock is 27MHz self.PCR = UIntToPCR(v) if DebugReader { - fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/27000000) + fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) } } @@ -533,7 +533,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/90000) + fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/float64(PTS_HZ)) } } @@ -544,7 +544,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } self.DTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: dts %d %f\n", self.DTS, float64(self.DTS)/90000) + fmt.Printf("pes: dts %d %f\n", self.DTS, float64(self.DTS)/float64(PTS_HZ)) } } diff --git a/ts.go b/ts.go index 56a909f..10fda37 100644 --- a/ts.go +++ b/ts.go @@ -77,6 +77,11 @@ func PESTsToUInt(ts uint64) (v uint64) { return ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (ts&0x7fff)<<1 | 0x100010001 } +const ( + PTS_HZ = 90000 + PCR_HZ = 27000000 +) + func UIntToPCR(v uint64) uint64 { // base(33)+resverd(6)+ext(9) base := v>>15 From a3225ee3df4bfb6e7925262cb20b5db05dc297f7 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Dec 2015 23:32:47 +0800 Subject: [PATCH 22/82] make SimpleH264Writer works --- example/test.go | 3 +- writer.go | 124 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 7 deletions(-) diff --git a/example/test.go b/example/test.go index 3a033fd..0fe5750 100644 --- a/example/test.go +++ b/example/test.go @@ -216,7 +216,7 @@ func main() { } w.PCR = sample.PCR bw := &bytes.Buffer{} - if err = ts.WritePES(bw, pes, sample.Data); err != nil { + if err = ts.WritePES(bw, pes, bytes.NewReader(sample.Data)); err != nil { return } if err = w.Write(bw.Bytes(), false); err != nil { @@ -253,5 +253,6 @@ func main() { } } } + } diff --git a/writer.go b/writer.go index c949603..3ec0d1f 100644 --- a/writer.go +++ b/writer.go @@ -274,11 +274,13 @@ func bswap32(v uint) uint { return (v>>24)|((v>>16)&0xff)<<8|((v>>8)&0xff)<<16|(v&0xff)<<24 } -func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { +func WritePES(w io.Writer, self PESHeader, data io.ReadSeeker) (err error) { // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html var pts_dts_flags, header_length, packet_length uint + dataLen := getSeekerLength(data) + // start code(24) 000001 // StreamId(8) // packet_length(16) @@ -315,7 +317,7 @@ func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { if pts_dts_flags & DTS != 0 { header_length += 5 } - packet_length = 3+header_length+uint(len(data)) + packet_length = 3+header_length+uint(dataLen) if DebugWriter { fmt.Printf("pesw: packet_length=%d\n", packet_length) @@ -359,7 +361,7 @@ func WritePES(w io.Writer, self PESHeader, data []byte) (err error) { } // data - if _, err = w.Write(data); err != nil { + if _, err = io.Copy(w, data); err != nil { return } @@ -475,14 +477,124 @@ func WritePMT(w io.Writer, self PMT) (err error) { type SimpleH264Writer struct { W io.Writer - headerHasWritten bool + TimeScale int + + SPS []byte + PPS []byte + + tsw *TSWriter + pts uint64 + pcr uint64 + prepared bool } -func (self *SimpleH264Writer) WriteSample(data []byte) (err error) { +func (self *SimpleH264Writer) prepare() (err error) { + writePAT := func() (err error) { + w := &TSWriter{ + W: self.W, + PID: 0, + DisableHeaderPadding: true, + } + pat := PAT{ + Entries: []PATEntry{ + {ProgramNumber: 1, ProgramMapPID: 0x1000}, + }, + } + bw := &bytes.Buffer{} + if err = WritePAT(bw, pat); err != nil { + return + } + if err = w.Write(bw.Bytes(), false); err != nil { + return + } + return + } + + writePMT := func() (err error) { + w := &TSWriter{ + W: self.W, + PID: 0x1000, + DisableHeaderPadding: true, + } + pmt := PMT{ + PCRPID: 0x100, + ElementaryStreamInfos: []ElementaryStreamInfo{ + {StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, + }, + } + bw := &bytes.Buffer{} + if err = WritePMT(bw, pmt); err != nil { + return + } + if err = w.Write(bw.Bytes(), false); err != nil { + return + } + return + } + + if err = writePAT(); err != nil { + return + } + + if err = writePMT(); err != nil { + return + } + + self.tsw = &TSWriter{ + W: self.W, + PID: 0x100, + } + self.pts = PTS_HZ + self.pcr = PCR_HZ + return } -func (self *SimpleH264Writer) WriteNALU(data []byte) (err error) { +func (self *SimpleH264Writer) writeData(data io.ReadSeeker, duration int) (err error) { + pes := PESHeader{ + StreamId: StreamIdH264, + PTS: self.pts, + } + self.tsw.PCR = self.pcr + + self.pts += uint64(duration)*PTS_HZ/uint64(self.TimeScale) + self.pcr += uint64(duration)*PCR_HZ/uint64(self.TimeScale) + + bw := &bytes.Buffer{} + if err = WritePES(bw, pes, data); err != nil { + return + } + if err = self.tsw.Write(bw.Bytes(), false); err != nil { + return + } + return } +func (self *SimpleH264Writer) writeNALUs(nalus [][]byte, duration int) (err error) { + readers := []io.ReadSeeker{} + for _, nalu := range nalus { + startCode := bytes.NewReader([]byte{0,0,1}) + readers = append(readers, startCode) + readers = append(readers, bytes.NewReader(nalu)) + } + return self.writeData(&multiReadSeeker{readers: readers}, duration) +} + +func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (err error) { + nalus := [][]byte{} + + if !self.prepared { + if err = self.prepare(); err != nil { + return + } + self.prepared = true + nalus = append(nalus, self.SPS) + nalus = append(nalus, self.PPS) + } + + nalus = append(nalus, nalu) + + return self.writeNALUs(nalus, duration) +} + From ff9f22f46d0cddeed06019da4323395ec25875f5 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 8 Dec 2015 20:33:40 +0800 Subject: [PATCH 23/82] fix pmt ES Info length field reserved value, cause quicktime cant play --- writer.go | 62 +++++++++++++++++++++++++------------------------------ 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/writer.go b/writer.go index 3ec0d1f..e331ebc 100644 --- a/writer.go +++ b/writer.go @@ -7,7 +7,7 @@ import ( "bytes" ) -const DebugWriter = true +const DebugWriter = false func WriteUInt64(w io.Writer, val uint64, n int) (err error) { var b [8]byte @@ -447,7 +447,7 @@ func WritePMT(w io.Writer, self PMT) (err error) { // Reserved(6) // ES Info length length(10) - if err = WriteUInt(w, uint(bw.Len())|0x3f<<10, 2); err != nil { + if err = WriteUInt(w, uint(bw.Len())|0x3c<<10, 2); err != nil { return } @@ -550,37 +550,6 @@ func (self *SimpleH264Writer) prepare() (err error) { return } -func (self *SimpleH264Writer) writeData(data io.ReadSeeker, duration int) (err error) { - pes := PESHeader{ - StreamId: StreamIdH264, - PTS: self.pts, - } - self.tsw.PCR = self.pcr - - self.pts += uint64(duration)*PTS_HZ/uint64(self.TimeScale) - self.pcr += uint64(duration)*PCR_HZ/uint64(self.TimeScale) - - bw := &bytes.Buffer{} - if err = WritePES(bw, pes, data); err != nil { - return - } - if err = self.tsw.Write(bw.Bytes(), false); err != nil { - return - } - - return -} - -func (self *SimpleH264Writer) writeNALUs(nalus [][]byte, duration int) (err error) { - readers := []io.ReadSeeker{} - for _, nalu := range nalus { - startCode := bytes.NewReader([]byte{0,0,1}) - readers = append(readers, startCode) - readers = append(readers, bytes.NewReader(nalu)) - } - return self.writeData(&multiReadSeeker{readers: readers}, duration) -} - func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (err error) { nalus := [][]byte{} @@ -595,6 +564,31 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e nalus = append(nalus, nalu) - return self.writeNALUs(nalus, duration) + readers := []io.ReadSeeker{} + for _, nalu := range nalus { + startCode := bytes.NewReader([]byte{0,0,1}) + readers = append(readers, startCode) + readers = append(readers, bytes.NewReader(nalu)) + } + data := &multiReadSeeker{readers: readers} + + pes := PESHeader{ + StreamId: StreamIdH264, + PTS: self.pts, + } + self.tsw.PCR = self.pcr + + self.pts += uint64(duration)*PTS_HZ/uint64(self.TimeScale) + self.pcr += uint64(duration)*PCR_HZ/uint64(self.TimeScale) + + bw := &bytes.Buffer{} + if err = WritePES(bw, pes, data); err != nil { + return + } + if err = self.tsw.Write(bw.Bytes(), sync); err != nil { + return + } + + return } From 59d52825d6b111d4675bf46ccdd81a81ebac0c23 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 8 Dec 2015 20:33:51 +0800 Subject: [PATCH 24/82] add/remove debug --- example/convts.sh | 4 ++-- example/test.go | 25 ++++++++++++++----------- reader.go | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/example/convts.sh b/example/convts.sh index 7623ed2..b88444a 100644 --- a/example/convts.sh +++ b/example/convts.sh @@ -1,4 +1,4 @@ -./avconv -loglevel debug -y -i /tmp/tiny2.mov \ - -bsf h264_mp4toannexb -acodec copy -vcodec copy -strict experimental /tmp/out.ts 2>&1 +avconv -loglevel debug -y -i /tmp/tiny2.mov \ + -bsf h264_mp4toannexb -an -vcodec copy -strict experimental /tmp/out.ts 2>&1 diff --git a/example/test.go b/example/test.go index 0fe5750..c2e3bf6 100644 --- a/example/test.go +++ b/example/test.go @@ -13,7 +13,8 @@ import ( type Stream struct { PID uint - Header *ts.PESHeader + PESHeader *ts.PESHeader + FirstTSHeader ts.TSHeader Title string Data bytes.Buffer Type uint @@ -26,6 +27,7 @@ type Sample struct { PTS uint64 DTS uint64 Data []byte + RandomAccessIndicator bool } func readSamples(filename string, ch chan Sample) { @@ -68,7 +70,7 @@ func readSamples(filename string, ch chan Sample) { return } - debugStream := true + debugStream := false onStreamPayload := func() (err error) { stream := findOrCreateStream(header.PID) @@ -77,10 +79,10 @@ func readSamples(filename string, ch chan Sample) { if header.PayloadUnitStart { stream.Data = bytes.Buffer{} - if stream.Header, err = ts.ReadPESHeader(lr); err != nil { + if stream.PESHeader, err = ts.ReadPESHeader(lr); err != nil { return } - stream.PCR = header.PCR + stream.FirstTSHeader = header } if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { @@ -88,10 +90,10 @@ func readSamples(filename string, ch chan Sample) { } if debugStream { - fmt.Printf("stream: %d/%d\n", stream.Data.Len(), stream.Header.DataLength) + fmt.Printf("stream: %d/%d\n", stream.Data.Len(), stream.PESHeader.DataLength) } - if stream.Data.Len() == int(stream.Header.DataLength) { + if stream.Data.Len() == int(stream.PESHeader.DataLength) { if debug { fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") fmt.Println(hex.Dump(stream.Data.Bytes())) @@ -99,9 +101,10 @@ func readSamples(filename string, ch chan Sample) { ch <- Sample{ Type: stream.Type, Data: stream.Data.Bytes(), - PTS: stream.Header.PTS, - DTS: stream.Header.DTS, - PCR: stream.PCR, + PTS: stream.PESHeader.PTS, + DTS: stream.PESHeader.DTS, + PCR: stream.FirstTSHeader.PCR, + RandomAccessIndicator: stream.FirstTSHeader.RandomAccessIndicator, } } return @@ -219,7 +222,7 @@ func main() { if err = ts.WritePES(bw, pes, bytes.NewReader(sample.Data)); err != nil { return } - if err = w.Write(bw.Bytes(), false); err != nil { + if err = w.Write(bw.Bytes(), sample.RandomAccessIndicator); err != nil { return } return @@ -243,7 +246,7 @@ func main() { if true { fmt.Println("sample: ", len(sample.Data), "PCR", sample.PCR, "PTS", sample.PTS, - "DTS", sample.DTS, + "DTS", sample.DTS, "sync", sample.RandomAccessIndicator, ) //fmt.Print(hex.Dump(sample.Data)) } diff --git a/reader.go b/reader.go index ad43826..8d61a77 100644 --- a/reader.go +++ b/reader.go @@ -121,6 +121,11 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { }) } + // random_access_indicator + if flags & 0x40 != 0 { + self.RandomAccessIndicator = true + } + // PCR if flags & 0x10 != 0 { var v uint64 @@ -201,6 +206,12 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err if pointer, err = ReadUInt(r, 1); err != nil { return } + + if DebugReader { + fmt.Printf("psi: pointer=%d\n", pointer) + } + + if pointer != 0 { if err = ReadDummy(r, int(pointer)); err != nil { return @@ -360,13 +371,41 @@ func ReadPMT(r io.Reader) (self PMT, err error) { } info.ElementaryPID = flags & 0x1fff + if DebugReader { + fmt.Printf("pmt: info1 %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {3, "reserved"}, + {13, "elementary_pid"}, + }, + Val: flags, + Length: 16, + }) + } + // Reserved(6) - // ES Info length length(10) + // ES Info length(10) if flags, err = ReadUInt(lr, 2); err != nil { return } length = flags & 0x3ff + if DebugReader { + fmt.Printf("pmt: info2 %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {6, "reserved"}, + {10, "es_info_length"}, + }, + Val: flags, + Length: 16, + }) + } + if length > 0 { lr := &io.LimitedReader{R: lr, N: int64(length)} if info.Descriptors, err = readDescs(lr); err != nil { From b81a2b22b4f4b6464365133893a9c1c3137a7e82 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 8 Dec 2015 22:59:46 +0800 Subject: [PATCH 25/82] add doc --- doc.txt | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 doc.txt diff --git a/doc.txt b/doc.txt new file mode 100644 index 0000000..484f07c --- /dev/null +++ b/doc.txt @@ -0,0 +1,50 @@ + +The first h264 packet + +00000000 00 00 00 01 09 f0 00 00 00 01 06 00 07 86 96 7f |................| + ^ ^ +00000010 80 00 00 40 80 00 00 01 06 05 11 03 87 f4 4e cd |...@..........N.| + ^ +00000020 0a 4b dc a1 94 3a c3 d4 9b 17 1f 00 80 00 00 00 |.K...:..........| +00000030 01 27 4d 40 0a a9 19 bf 2c b8 0b 50 10 10 13 0a |.'M@....,..P....| + ^ +00000040 d7 bd f0 10 00 00 00 01 28 fe 09 88 00 00 01 25 |........(......%| + ^ ^ +00000050 b8 20 20 bf ed 7e de 3b 56 7f 03 c5 d1 06 9a c3 |. ..~.;V.......| +00000060 74 e5 9d 18 e6 3f 9d 63 19 cc 46 fd 36 13 c5 b8 |t....?.c..F.6...| +00000070 7f 3e d9 39 1b 21 74 61 6d 9d b7 5b 71 e6 a2 de |.>.9.!tam..[q...| +00000080 68 0b bf 7e b8 d4 9d 2f 49 19 3b 11 6e 40 a3 0a |h..~.../I.;.n@..| +00000090 3f 02 c5 a3 47 1d ed 2a 3a a1 33 0e 15 2f 36 d8 |?...G..*:.3../6.| +000000a0 c7 2a eb c6 bf 0d 95 e4 42 3e 7b 5f 44 3f b9 a5 |.*......B>{_D?..| +000000b0 d7 a2 c6 f4 41 e8 82 b5 cc cd c9 a4 ec 29 6f 58 |....A........)oX| +000000c0 6c 78 38 7c b5 5b 27 97 9d 56 5f e0 00 00 01 25 |lx8|.['..V_....%| + ^ +000000d0 2b 82 02 0b af 43 64 9c f1 eb 03 7e 39 a0 b6 f8 |+....Cd....~9...| +000000e0 eb c4 88 d2 ef 79 1f 7c d6 4e 1a d3 5d 27 39 ca |.....y.|.N..]'9.| +000000f0 c6 0b a9 63 78 e2 19 41 71 d4 47 26 bc 58 e4 45 |...cx..Aq.G&.X.E| +00000100 db 77 7b 60 7f d9 e4 11 03 3d 05 0d 72 5e 0d a0 |.w{`.....=..r^..| +00000110 56 4f 1a a9 9e 4f 80 |VO...O.| + +[h264 @ 0x7fdf01802200] NAL 9(AUD) at 4/279 length 1 +[h264 @ 0x7fdf01802200] NAL 6(SEI) at 10/279 length 10 +[h264 @ 0x7fdf01802200] NAL 6(SEI) at 24/279 length 20 +[h264 @ 0x7fdf01802200] NAL 7(SPS) at 49/279 length 18 +[h264 @ 0x7fdf01802200] NAL 8(PPS) at 72/279 length 3 +[h264 @ 0x7fdf01802200] NAL 5(IDR) at 79/279 length 124 +[h264 @ 0x7fdf01802200] NAL 5(IDR) at 207/279 length 71 + +NAL_SLICE = 1, +NAL_DPA = 2, +NAL_DPB = 3, +NAL_DPC = 4, +NAL_IDR_SLICE = 5, +NAL_SEI = 6, +NAL_SPS = 7, +NAL_PPS = 8, +NAL_AUD = 9, +NAL_END_SEQUENCE = 10, +NAL_END_STREAM = 11, +NAL_FILLER_DATA = 12, +NAL_SPS_EXT = 13, +NAL_AUXILIARY_SLICE = 19, + From bf89f36b1a11f394d0f7c92472f8cf8f900c446d Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 8 Dec 2015 23:35:25 +0800 Subject: [PATCH 26/82] add startcode+AUD before nalu make quicktime works --- writer.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/writer.go b/writer.go index e331ebc..4eef070 100644 --- a/writer.go +++ b/writer.go @@ -565,9 +565,14 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e nalus = append(nalus, nalu) readers := []io.ReadSeeker{} - for _, nalu := range nalus { - startCode := bytes.NewReader([]byte{0,0,1}) - readers = append(readers, startCode) + for i, nalu := range nalus { + var startCode []byte + if i == 0 { + startCode = []byte{0,0,0,1,0x9,0xf0,0,0,0,1} // AUD + } else { + startCode = []byte{0,0,1} + } + readers = append(readers, bytes.NewReader(startCode)) readers = append(readers, bytes.NewReader(nalu)) } data := &multiReadSeeker{readers: readers} From 767bba0224f4142de45ab9a1c9faad018e270c30 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 8 Dec 2015 23:42:55 +0800 Subject: [PATCH 27/82] add comment --- doc.txt | 2 ++ example/test.go | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/doc.txt b/doc.txt index 484f07c..0d698ce 100644 --- a/doc.txt +++ b/doc.txt @@ -33,6 +33,8 @@ The first h264 packet [h264 @ 0x7fdf01802200] NAL 5(IDR) at 79/279 length 124 [h264 @ 0x7fdf01802200] NAL 5(IDR) at 207/279 length 71 +AUD is a must for QuickTime player. + NAL_SLICE = 1, NAL_DPA = 2, NAL_DPB = 3, diff --git a/example/test.go b/example/test.go index c2e3bf6..5f93a95 100644 --- a/example/test.go +++ b/example/test.go @@ -35,7 +35,8 @@ func readSamples(filename string, ch chan Sample) { close(ch) }() - debug := false + debugData := true + debugStream := false var file *os.File var err error @@ -70,8 +71,6 @@ func readSamples(filename string, ch chan Sample) { return } - debugStream := false - onStreamPayload := func() (err error) { stream := findOrCreateStream(header.PID) r := bytes.NewReader(payload) @@ -94,7 +93,7 @@ func readSamples(filename string, ch chan Sample) { } if stream.Data.Len() == int(stream.PESHeader.DataLength) { - if debug { + if debugData { fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") fmt.Println(hex.Dump(stream.Data.Bytes())) } @@ -114,7 +113,7 @@ func readSamples(filename string, ch chan Sample) { if header, n, err = ts.ReadTSPacket(file, data[:]); err != nil { return } - if debug { + if debugData { fmt.Println(header, n) } @@ -134,9 +133,6 @@ func readSamples(filename string, ch chan Sample) { return } //fmt.Println("pmt", pmt) - if debug { - fmt.Println(hex.Dump(payload)) - } } } From 910337cf3908adf8634421846bd95b8b3f8aecee Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 02:25:07 +0800 Subject: [PATCH 28/82] change WritePES() to write zero packet_length in order to fit big frame size > 64K --- writer.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/writer.go b/writer.go index 4eef070..445f5b3 100644 --- a/writer.go +++ b/writer.go @@ -277,9 +277,7 @@ func bswap32(v uint) uint { func WritePES(w io.Writer, self PESHeader, data io.ReadSeeker) (err error) { // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html - var pts_dts_flags, header_length, packet_length uint - - dataLen := getSeekerLength(data) + var pts_dts_flags, header_length uint // start code(24) 000001 // StreamId(8) @@ -317,14 +315,9 @@ func WritePES(w io.Writer, self PESHeader, data io.ReadSeeker) (err error) { if pts_dts_flags & DTS != 0 { header_length += 5 } - packet_length = 3+header_length+uint(dataLen) - if DebugWriter { - fmt.Printf("pesw: packet_length=%d\n", packet_length) - } - - // packet_length(16) - if err = WriteUInt(w, packet_length, 2); err != nil { + // packet_length(16) if zero then variable length + if err = WriteUInt(w, 0, 2); err != nil { return } From 36825b62324faea6f61d5b9ab7b9ada88b00270a Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 11:48:55 +0800 Subject: [PATCH 29/82] use zero copy IO when writing PES packets --- example/test.go | 71 +++++++++++------- vecio.go | 49 ++++++------ writer.go | 192 +++++++++++++++++++++++++----------------------- 3 files changed, 175 insertions(+), 137 deletions(-) diff --git a/example/test.go b/example/test.go index 5f93a95..dbcc347 100644 --- a/example/test.go +++ b/example/test.go @@ -8,9 +8,23 @@ import ( ts "../" "fmt" "encoding/hex" + "encoding/gob" "flag" ) +type GobAllSamples struct { + TimeScale int + SPS []byte + PPS []byte + Samples []GobSample +} + +type GobSample struct { + Duration int + Data []byte + Sync bool +} + type Stream struct { PID uint PESHeader *ts.PESHeader @@ -145,11 +159,39 @@ func readSamples(filename string, ch chan Sample) { } } +func testInputGob(pathGob string, pathOut string) { + gobfile, _ := os.Open(pathGob) + outfile, _ := os.Create(pathOut) + dec := gob.NewDecoder(gobfile) + var allSamples GobAllSamples + dec.Decode(&allSamples) + + w := ts.SimpleH264Writer{ + W: outfile, + SPS: allSamples.SPS, + PPS: allSamples.PPS, + TimeScale: allSamples.TimeScale, + } + + for _, sample := range allSamples.Samples { + w.WriteNALU(sample.Sync, sample.Duration, sample.Data) + } + + outfile.Close() + fmt.Println("written to", pathOut) +} + func main() { input := flag.String("i", "", "input file") output := flag.String("o", "", "output file") + inputGob := flag.String("g", "", "input gob file") flag.Parse() + if *inputGob != "" && *output != "" { + testInputGob(*inputGob, *output) + return + } + var file *os.File var err error @@ -163,43 +205,25 @@ func main() { } writePAT := func() (err error) { - w := &ts.TSWriter{ - W: file, - PID: 0, - DisableHeaderPadding: true, - } pat := ts.PAT{ Entries: []ts.PATEntry{ {ProgramNumber: 1, ProgramMapPID: 0x1000}, }, } - bw := &bytes.Buffer{} - if err = ts.WritePAT(bw, pat); err != nil { - return - } - if err = w.Write(bw.Bytes(), false); err != nil { + if err = ts.WritePATPacket(file, pat); err != nil { return } return } writePMT := func() (err error) { - w := &ts.TSWriter{ - W: file, - PID: 0x1000, - DisableHeaderPadding: true, - } pmt := ts.PMT{ PCRPID: 0x100, ElementaryStreamInfos: []ts.ElementaryStreamInfo{ {StreamType: ts.ElementaryStreamTypeH264, ElementaryPID: 0x100}, }, } - bw := &bytes.Buffer{} - if err = ts.WritePMT(bw, pmt); err != nil { - return - } - if err = w.Write(bw.Bytes(), false); err != nil { + if err = ts.WritePMTPacket(file, pmt, 0x1000); err != nil { return } return @@ -214,11 +238,8 @@ func main() { DTS: sample.DTS, } w.PCR = sample.PCR - bw := &bytes.Buffer{} - if err = ts.WritePES(bw, pes, bytes.NewReader(sample.Data)); err != nil { - return - } - if err = w.Write(bw.Bytes(), sample.RandomAccessIndicator); err != nil { + w.RandomAccessIndicator = sample.RandomAccessIndicator + if err = ts.WritePESPacket(w, pes, sample.Data); err != nil { return } return diff --git a/vecio.go b/vecio.go index fe5857b..57bdd92 100644 --- a/vecio.go +++ b/vecio.go @@ -5,36 +5,41 @@ import ( "io" ) -func getSeekerLength(data io.Seeker) (length int64) { - length, _ = data.Seek(0, 2) - data.Seek(0, 0) - return +type iovec struct { + data [][]byte + Len int } -type multiReadSeeker struct { - readers []io.ReadSeeker +func (self *iovec) Append(b []byte) { + self.data = append(self.data, b) + self.Len += len(b) } -func (mr *multiReadSeeker) Seek(offset int64, whence int) (n int64, err error) { - if whence == 2 { - for _, reader := range mr.readers { - n += getSeekerLength(reader) +func (self *iovec) WriteTo(w io.Writer, n int) (written int, err error) { + for n > 0 && self.Len > 0 { + data := self.data[0] + + var b []byte + if n > len(data) { + b = data + } else { + b = data[:n] } - } - return -} -func (mr *multiReadSeeker) Read(p []byte) (n int, err error) { - for len(mr.readers) > 0 { - n, err = mr.readers[0].Read(p) - if n > 0 || err != io.EOF { - if err == io.EOF { - err = nil - } + data = data[len(b):] + if len(data) == 0 { + self.data = self.data[1:] + } else { + self.data[0] = data + } + self.Len -= len(b) + n -= len(b) + written += len(b) + + if _, err = w.Write(b); err != nil { return } - mr.readers = mr.readers[1:] } - return 0, io.EOF + return } diff --git a/writer.go b/writer.go index 445f5b3..14b1be2 100644 --- a/writer.go +++ b/writer.go @@ -7,7 +7,7 @@ import ( "bytes" ) -const DebugWriter = false +const DebugWriter = true func WriteUInt64(w io.Writer, val uint64, n int) (err error) { var b [8]byte @@ -38,7 +38,7 @@ func WriteRepeatVal(w io.Writer, val byte, n int) (err error) { return } -func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { +func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err error) { var flags, extFlags uint // sync(8) @@ -85,6 +85,7 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { if err = WriteUInt(w, flags, 4); err != nil { return } + written += 4 if flags & EXT != 0 { var length uint @@ -111,7 +112,7 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { } if DebugWriter { - fmt.Printf("tsw: dataLength=%d paddingLength=%d\n", dataLength, paddingLength) + fmt.Printf("tsw: header padding=%d\n", paddingLength) } if err = WriteUInt(w, length, 1); err != nil { @@ -138,6 +139,8 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { return } } + + written += int(length)+1 } return @@ -146,14 +149,12 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (err error) { type TSWriter struct { W io.Writer PID uint - PCR uint64 - OPCR uint64 - ContinuityCounter uint + TSHeader DisableHeaderPadding bool } -func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { - for i := 0; len(b) > 0; i++ { +func (self *TSWriter) WriteIovec(data *iovec) (err error) { + for i := 0; data.Len > 0; i++ { header := TSHeader{ PID: self.PID, ContinuityCounter: self.ContinuityCounter, @@ -161,37 +162,29 @@ func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { if i == 0 { header.PayloadUnitStart = true + header.RandomAccessIndicator = self.RandomAccessIndicator header.PCR = self.PCR header.OPCR = self.OPCR - header.RandomAccessIndicator = RandomAccessIndicator } - requestLength := len(b) + requestLength := data.Len if self.DisableHeaderPadding { requestLength = 188 } - - bw := &bytes.Buffer{} - if err = WriteTSHeader(bw, header, requestLength); err != nil { + var headerLength int + if headerLength, err = WriteTSHeader(self.W, header, requestLength); err != nil { return } - - dataLen := 188-bw.Len() - if self.DisableHeaderPadding && len(b) < dataLen { - b = append(b, makeRepeatValBytes(0xff, dataLen - len(b))...) + payloadLength := 188 - headerLength + if self.DisableHeaderPadding && data.Len < payloadLength { + data.Append(makeRepeatValBytes(0xff, payloadLength - data.Len)) } - data := b[:dataLen] - b = b[dataLen:] - if DebugWriter { - fmt.Printf("tsw: datalen=%d blen=%d\n", dataLen, len(b)) + fmt.Printf("tsw: payloadLength=%d dataLength=%d\n", payloadLength, data.Len) } - if _, err = self.W.Write(bw.Bytes()); err != nil { - return - } - if _, err = self.W.Write(data); err != nil { + if _, err = data.WriteTo(self.W, payloadLength); err != nil { return } @@ -201,6 +194,12 @@ func (self *TSWriter) Write(b []byte, RandomAccessIndicator bool) (err error) { return } +func (self *TSWriter) Write(data []byte) (err error) { + iov := &iovec{} + iov.Append(data) + return self.WriteIovec(iov) +} + func WritePSI(w io.Writer, self PSI, data []byte) (err error) { // pointer(8) // table_id(8) @@ -274,7 +273,7 @@ func bswap32(v uint) uint { return (v>>24)|((v>>16)&0xff)<<8|((v>>8)&0xff)<<16|(v&0xff)<<24 } -func WritePES(w io.Writer, self PESHeader, data io.ReadSeeker) (err error) { +func WritePESHeader(w io.Writer, self PESHeader) (err error) { // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html var pts_dts_flags, header_length uint @@ -353,11 +352,20 @@ func WritePES(w io.Writer, self PESHeader, data io.ReadSeeker) (err error) { } } - // data - if _, err = io.Copy(w, data); err != nil { + return +} + +func WritePESPacket(w *TSWriter, header PESHeader, data []byte) (err error) { + bw := &bytes.Buffer{} + if err = WritePESHeader(bw, header); err != nil { + return + } + iov := &iovec{} + iov.Append(bw.Bytes()) + iov.Append(data) + if err = w.WriteIovec(iov); err != nil { return } - return } @@ -389,6 +397,22 @@ func WritePAT(w io.Writer, self PAT) (err error) { return } +func WritePATPacket(w io.Writer, pat PAT) (err error) { + tsw := &TSWriter{ + W: w, + PID: 0, + DisableHeaderPadding: true, + } + bw := &bytes.Buffer{} + if err = WritePAT(bw, pat); err != nil { + return + } + if err = tsw.Write(bw.Bytes()); err != nil { + return + } + return +} + func WritePMT(w io.Writer, self PMT) (err error) { writeDescs := func(w io.Writer, descs []Descriptor) (err error) { for _, desc := range descs { @@ -468,6 +492,22 @@ func WritePMT(w io.Writer, self PMT) (err error) { return } +func WritePMTPacket(w io.Writer, pmt PMT, pid uint) (err error) { + tsw := &TSWriter{ + W: w, + PID: pid, + DisableHeaderPadding: true, + } + bw := &bytes.Buffer{} + if err = WritePMT(bw, pmt); err != nil { + return + } + if err = tsw.Write(bw.Bytes()); err != nil { + return + } + return +} + type SimpleH264Writer struct { W io.Writer TimeScale int @@ -479,57 +519,26 @@ type SimpleH264Writer struct { pts uint64 pcr uint64 prepared bool + pesBuf *bytes.Buffer } func (self *SimpleH264Writer) prepare() (err error) { - writePAT := func() (err error) { - w := &TSWriter{ - W: self.W, - PID: 0, - DisableHeaderPadding: true, - } - pat := PAT{ - Entries: []PATEntry{ - {ProgramNumber: 1, ProgramMapPID: 0x1000}, - }, - } - bw := &bytes.Buffer{} - if err = WritePAT(bw, pat); err != nil { - return - } - if err = w.Write(bw.Bytes(), false); err != nil { - return - } + pat := PAT{ + Entries: []PATEntry{ + {ProgramNumber: 1, ProgramMapPID: 0x1000}, + }, + } + if err = WritePATPacket(self.W, pat); err != nil { return } - writePMT := func() (err error) { - w := &TSWriter{ - W: self.W, - PID: 0x1000, - DisableHeaderPadding: true, - } - pmt := PMT{ - PCRPID: 0x100, - ElementaryStreamInfos: []ElementaryStreamInfo{ - {StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, - }, - } - bw := &bytes.Buffer{} - if err = WritePMT(bw, pmt); err != nil { - return - } - if err = w.Write(bw.Bytes(), false); err != nil { - return - } - return + pmt := PMT{ + PCRPID: 0x100, + ElementaryStreamInfos: []ElementaryStreamInfo{ + {StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, + }, } - - if err = writePAT(); err != nil { - return - } - - if err = writePMT(); err != nil { + if err = WritePMTPacket(self.W, pmt, 0x1000); err != nil { return } @@ -540,6 +549,8 @@ func (self *SimpleH264Writer) prepare() (err error) { self.pts = PTS_HZ self.pcr = PCR_HZ + self.pesBuf = &bytes.Buffer{} + return } @@ -554,10 +565,18 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e nalus = append(nalus, self.SPS) nalus = append(nalus, self.PPS) } - nalus = append(nalus, nalu) - readers := []io.ReadSeeker{} + pes := PESHeader{ + StreamId: StreamIdH264, + PTS: self.pts, + } + if err = WritePESHeader(self.pesBuf, pes); err != nil { + return + } + + data := &iovec{} + data.Append(self.pesBuf.Bytes()) for i, nalu := range nalus { var startCode []byte if i == 0 { @@ -565,27 +584,20 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e } else { startCode = []byte{0,0,1} } - readers = append(readers, bytes.NewReader(startCode)) - readers = append(readers, bytes.NewReader(nalu)) + data.Append(startCode) + data.Append(nalu) } - data := &multiReadSeeker{readers: readers} - pes := PESHeader{ - StreamId: StreamIdH264, - PTS: self.pts, - } + self.tsw.RandomAccessIndicator = sync self.tsw.PCR = self.pcr + if err = self.tsw.WriteIovec(data); err != nil { + return + } + self.pts += uint64(duration)*PTS_HZ/uint64(self.TimeScale) self.pcr += uint64(duration)*PCR_HZ/uint64(self.TimeScale) - - bw := &bytes.Buffer{} - if err = WritePES(bw, pes, data); err != nil { - return - } - if err = self.tsw.Write(bw.Bytes(), sync); err != nil { - return - } + self.pesBuf.Reset() return } From 08f9332704db273db62689cc6fe522b50981186b Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 12:09:36 +0800 Subject: [PATCH 30/82] bugfix: ReadPESHeader now supported zero payload length --- example/test.go | 41 ++++++++++++++++++++++++++++------------- reader.go | 10 ++++++---- writer.go | 2 +- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/example/test.go b/example/test.go index dbcc347..e9d752b 100644 --- a/example/test.go +++ b/example/test.go @@ -50,7 +50,7 @@ func readSamples(filename string, ch chan Sample) { }() debugData := true - debugStream := false + debugStream := true var file *os.File var err error @@ -85,17 +85,39 @@ func readSamples(filename string, ch chan Sample) { return } + onStreamPayloadUnitEnd := func(stream *Stream) { + if debugData { + fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") + fmt.Println(hex.Dump(stream.Data.Bytes())) + } + ch <- Sample{ + Type: stream.Type, + Data: stream.Data.Bytes(), + PTS: stream.PESHeader.PTS, + DTS: stream.PESHeader.DTS, + PCR: stream.FirstTSHeader.PCR, + RandomAccessIndicator: stream.FirstTSHeader.RandomAccessIndicator, + } + } + onStreamPayload := func() (err error) { stream := findOrCreateStream(header.PID) r := bytes.NewReader(payload) lr := &io.LimitedReader{R: r, N: int64(len(payload))} + if header.PayloadUnitStart && stream.PESHeader != nil && stream.PESHeader.DataLength == 0 { + onStreamPayloadUnitEnd(stream) + } + if header.PayloadUnitStart { stream.Data = bytes.Buffer{} if stream.PESHeader, err = ts.ReadPESHeader(lr); err != nil { return } stream.FirstTSHeader = header + if debugStream { + fmt.Printf("stream: start\n") + } } if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { @@ -107,19 +129,9 @@ func readSamples(filename string, ch chan Sample) { } if stream.Data.Len() == int(stream.PESHeader.DataLength) { - if debugData { - fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") - fmt.Println(hex.Dump(stream.Data.Bytes())) - } - ch <- Sample{ - Type: stream.Type, - Data: stream.Data.Bytes(), - PTS: stream.PESHeader.PTS, - DTS: stream.PESHeader.DTS, - PCR: stream.FirstTSHeader.PCR, - RandomAccessIndicator: stream.FirstTSHeader.RandomAccessIndicator, - } + onStreamPayloadUnitEnd(stream) } + return } @@ -187,6 +199,9 @@ func main() { inputGob := flag.String("g", "", "input gob file") flag.Parse() + ts.DebugReader = true + ts.DebugWriter = true + if *inputGob != "" && *output != "" { testInputGob(*inputGob, *output) return diff --git a/reader.go b/reader.go index 8d61a77..81f2309 100644 --- a/reader.go +++ b/reader.go @@ -7,7 +7,7 @@ import ( "io/ioutil" ) -var DebugReader = true +var DebugReader = false func ReadUInt(r io.Reader, n int) (res uint, err error) { var b [4]byte @@ -494,6 +494,9 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { if length, err = ReadUInt(r, 2); err != nil { return } + if length == 0 { + length = 1<<31 + } lrAll := &io.LimitedReader{R: r, N: int64(length)} lr := lrAll @@ -667,14 +670,13 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } } - if lr.N > 0 { + if lr.N > 0 && lr.N < 65536 { if err = ReadDummy(lr, int(lr.N)); err != nil { return } + self.DataLength = uint(lrAll.N) } - self.DataLength = uint(lrAll.N) - res = self return } diff --git a/writer.go b/writer.go index 14b1be2..b5e9f04 100644 --- a/writer.go +++ b/writer.go @@ -7,7 +7,7 @@ import ( "bytes" ) -const DebugWriter = true +var DebugWriter = false func WriteUInt64(w io.Writer, val uint64, n int) (err error) { var b [8]byte From 86e41350b3be5c51da08e8c33241af67fc41d752 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 14:24:16 +0800 Subject: [PATCH 31/82] add cpuprofile --- example/test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/example/test.go b/example/test.go index e9d752b..24be502 100644 --- a/example/test.go +++ b/example/test.go @@ -9,6 +9,7 @@ import ( "fmt" "encoding/hex" "encoding/gob" + "runtime/pprof" "flag" ) @@ -197,8 +198,19 @@ func main() { input := flag.String("i", "", "input file") output := flag.String("o", "", "output file") inputGob := flag.String("g", "", "input gob file") + cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") flag.Parse() + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + return + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + ts.DebugReader = true ts.DebugWriter = true From bc93d48788d25ff4ac9883331741dd04a8e75da5 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 14:30:30 +0800 Subject: [PATCH 32/82] add debug options for example/test --- example/test.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/example/test.go b/example/test.go index 24be502..a508053 100644 --- a/example/test.go +++ b/example/test.go @@ -45,14 +45,16 @@ type Sample struct { RandomAccessIndicator bool } +var ( + debugData = true + debugStream = true +) + func readSamples(filename string, ch chan Sample) { defer func() { close(ch) }() - debugData := true - debugStream := true - var file *os.File var err error if file, err = os.Open(filename); err != nil { @@ -191,7 +193,9 @@ func testInputGob(pathGob string, pathOut string) { } outfile.Close() - fmt.Println("written to", pathOut) + if debugStream { + fmt.Println("written to", pathOut) + } } func main() { @@ -199,6 +203,10 @@ func main() { output := flag.String("o", "", "output file") inputGob := flag.String("g", "", "input gob file") cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") + flag.BoolVar(&debugData, "vd", false, "debug data") + flag.BoolVar(&debugStream, "vs", false, "debug stream") + flag.BoolVar(&ts.DebugReader, "vr", false, "debug reader") + flag.BoolVar(&ts.DebugWriter, "vw", false, "debug writer") flag.Parse() if *cpuprofile != "" { @@ -210,10 +218,6 @@ func main() { defer pprof.StopCPUProfile() } - - ts.DebugReader = true - ts.DebugWriter = true - if *inputGob != "" && *output != "" { testInputGob(*inputGob, *output) return @@ -287,7 +291,7 @@ func main() { break } if sample.Type == ts.ElementaryStreamTypeH264 { - if true { + if debugStream { fmt.Println("sample: ", len(sample.Data), "PCR", sample.PCR, "PTS", sample.PTS, "DTS", sample.DTS, "sync", sample.RandomAccessIndicator, From 8cf764967d04e444422ca6b73a5555d21498a2a5 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Dec 2015 18:02:33 +0800 Subject: [PATCH 33/82] add vecWriter --- example/test.go | 1 + vecio.go | 93 +++++++++++++++++++++++++++++++++++++++++++++++-- writer.go | 30 ++++++++++++++-- 3 files changed, 118 insertions(+), 6 deletions(-) diff --git a/example/test.go b/example/test.go index a508053..36c0016 100644 --- a/example/test.go +++ b/example/test.go @@ -283,6 +283,7 @@ func main() { W: file, PID: 0x100, } + w.EnableVecWriter() } for { diff --git a/vecio.go b/vecio.go index 57bdd92..8256d03 100644 --- a/vecio.go +++ b/vecio.go @@ -3,11 +3,18 @@ package ts import ( "io" + "os" + "net" + "fmt" + "unsafe" + "syscall" ) type iovec struct { data [][]byte Len int + pos int + idx int } func (self *iovec) Append(b []byte) { @@ -17,7 +24,7 @@ func (self *iovec) Append(b []byte) { func (self *iovec) WriteTo(w io.Writer, n int) (written int, err error) { for n > 0 && self.Len > 0 { - data := self.data[0] + data := self.data[self.idx] var b []byte if n > len(data) { @@ -28,9 +35,9 @@ func (self *iovec) WriteTo(w io.Writer, n int) (written int, err error) { data = data[len(b):] if len(data) == 0 { - self.data = self.data[1:] + self.idx++ } else { - self.data[0] = data + self.data[self.idx] = data } self.Len -= len(b) n -= len(b) @@ -43,3 +50,83 @@ func (self *iovec) WriteTo(w io.Writer, n int) (written int, err error) { return } +type sysiovec struct { + Base uintptr + Len uint64 +} + +type vecWriter struct { + fd uintptr + smallBytesBuf []byte + iov []sysiovec +} + +func (self *vecWriter) Write(p []byte) (written int, err error) { + iov := sysiovec{ + Len: uint64(len(p)), + } + + if len(p) < 16 { + iov.Base = uintptr(len(self.smallBytesBuf)) + self.smallBytesBuf = append(self.smallBytesBuf, p...) + } else { + iov.Base = uintptr(unsafe.Pointer(&p[0])) + } + + self.iov = append(self.iov, iov) + return +} + +func (self *vecWriter) Flush() (err error) { + for i := range self.iov { + iov := &self.iov[i] + if iov.Base < uintptr(len(self.smallBytesBuf)) { + iov.Base = uintptr(unsafe.Pointer(&self.smallBytesBuf[iov.Base])) + } + } + + N := 1024 + for i := 0; i < len(self.iov); i += N { + n := len(self.iov) - i + if n > N { + n = N + } + _, _, errno := syscall.Syscall(syscall.SYS_WRITEV, self.fd, uintptr(unsafe.Pointer(&self.iov[i])), uintptr(n)) + if errno != 0 { + err = fmt.Errorf("writev failed with error: %d", errno) + return + } + } + + if DebugWriter { + fmt.Printf("vecw: smallBytesBuf=%d iovNr=%d\n", len(self.smallBytesBuf), len(self.iov)) + } + + self.iov = self.iov[:0] + self.smallBytesBuf = self.smallBytesBuf[:0] + + return +} + +func newVecWriter(w io.Writer) (vecw *vecWriter) { + var err error + var f *os.File + + switch obj := w.(type) { + case *net.TCPConn: + f, err = obj.File() + if err != nil { + return + } + case *os.File: + f = obj + default: + return + } + + vecw = &vecWriter{ + fd: f.Fd(), + } + return +} + diff --git a/writer.go b/writer.go index b5e9f04..dfe7d40 100644 --- a/writer.go +++ b/writer.go @@ -151,9 +151,26 @@ type TSWriter struct { PID uint TSHeader DisableHeaderPadding bool + + vecw *vecWriter +} + +func (self *TSWriter) EnableVecWriter() { + if self.vecw == nil { + self.vecw = newVecWriter(self.W) + + if DebugWriter && self.vecw != nil { + fmt.Println("tsw: enabled vec writer") + } + } } func (self *TSWriter) WriteIovec(data *iovec) (err error) { + w := self.W + if self.vecw != nil { + w = self.vecw + } + for i := 0; data.Len > 0; i++ { header := TSHeader{ PID: self.PID, @@ -172,7 +189,7 @@ func (self *TSWriter) WriteIovec(data *iovec) (err error) { requestLength = 188 } var headerLength int - if headerLength, err = WriteTSHeader(self.W, header, requestLength); err != nil { + if headerLength, err = WriteTSHeader(w, header, requestLength); err != nil { return } payloadLength := 188 - headerLength @@ -184,13 +201,19 @@ func (self *TSWriter) WriteIovec(data *iovec) (err error) { fmt.Printf("tsw: payloadLength=%d dataLength=%d\n", payloadLength, data.Len) } - if _, err = data.WriteTo(self.W, payloadLength); err != nil { + if _, err = data.WriteTo(w, payloadLength); err != nil { return } self.ContinuityCounter++ } + if self.vecw != nil { + if err = self.vecw.Flush(); err != nil { + return + } + } + return } @@ -232,7 +255,7 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { } if DebugWriter { - fmt.Printf("wpsi: length=%d\n", length) + fmt.Printf("psiw: length=%d\n", length) } // Table ID extension(16) @@ -546,6 +569,7 @@ func (self *SimpleH264Writer) prepare() (err error) { W: self.W, PID: 0x100, } + self.tsw.EnableVecWriter() self.pts = PTS_HZ self.pcr = PCR_HZ From 2230137839628d9e03c7a1e7e6b78258f3865f23 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 10 Dec 2015 10:32:10 +0800 Subject: [PATCH 34/82] add set SetPTSPCR/LastPTSPCR for SimpleH264Writer --- writer.go | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/writer.go b/writer.go index dfe7d40..8fd05a2 100644 --- a/writer.go +++ b/writer.go @@ -570,8 +570,11 @@ func (self *SimpleH264Writer) prepare() (err error) { PID: 0x100, } self.tsw.EnableVecWriter() - self.pts = PTS_HZ - self.pcr = PCR_HZ + + if self.pts == 0 { + self.pts = PTS_HZ + self.pcr = PCR_HZ + } self.pesBuf = &bytes.Buffer{} @@ -626,3 +629,13 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e return } +func (self *SimpleH264Writer) LastPTSPCR() (pts, pcr uint64) { + return self.pts, self.pcr +} + +func (self *SimpleH264Writer) SetPTSPCR(pts, pcr uint64) { + self.pts = pts + self.pcr = pcr + return +} + From 62476ce8e051b4e47b3458b3ce2b550c60977f09 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 10 Dec 2015 18:02:57 +0800 Subject: [PATCH 35/82] add WriteHeader() to SimpleH264Writer for convenient segment big ts file into small ones --- example/test.go | 30 ++++++++++++++++--- writer.go | 79 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/example/test.go b/example/test.go index 36c0016..ef29816 100644 --- a/example/test.go +++ b/example/test.go @@ -174,7 +174,7 @@ func readSamples(filename string, ch chan Sample) { } } -func testInputGob(pathGob string, pathOut string) { +func testInputGob(pathGob string, pathOut string, testSeg bool) { gobfile, _ := os.Open(pathGob) outfile, _ := os.Create(pathOut) dec := gob.NewDecoder(gobfile) @@ -187,14 +187,34 @@ func testInputGob(pathGob string, pathOut string) { PPS: allSamples.PPS, TimeScale: allSamples.TimeScale, } + //w.WriteHeader() - for _, sample := range allSamples.Samples { + syncCount := 0 + segCount := 0 + + for i, sample := range allSamples.Samples { + if debugStream { + fmt.Println("stream: write sample #", i) + } + if sample.Sync { + syncCount++ + if testSeg { + if syncCount % 3 == 0 { + outfile.Close() + segCount++ + outfile, _ = os.Create(fmt.Sprintf("%s.seg%d.ts", pathOut, segCount)) + w.W = outfile + w.WriteHeader() + fmt.Println("seg", segCount, "sync", syncCount) + } + } + } w.WriteNALU(sample.Sync, sample.Duration, sample.Data) } outfile.Close() if debugStream { - fmt.Println("written to", pathOut) + fmt.Println("stream: written to", pathOut) } } @@ -202,7 +222,9 @@ func main() { input := flag.String("i", "", "input file") output := flag.String("o", "", "output file") inputGob := flag.String("g", "", "input gob file") + testSegment := flag.Bool("seg", false, "test segment") cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") + flag.BoolVar(&debugData, "vd", false, "debug data") flag.BoolVar(&debugStream, "vs", false, "debug stream") flag.BoolVar(&ts.DebugReader, "vr", false, "debug reader") @@ -219,7 +241,7 @@ func main() { } if *inputGob != "" && *output != "" { - testInputGob(*inputGob, *output) + testInputGob(*inputGob, *output, *testSegment) return } diff --git a/writer.go b/writer.go index 8fd05a2..cc11598 100644 --- a/writer.go +++ b/writer.go @@ -538,11 +538,19 @@ type SimpleH264Writer struct { SPS []byte PPS []byte - tsw *TSWriter + tswPAT *TSWriter + tswPMT *TSWriter + tswH264 *TSWriter + pts uint64 pcr uint64 + prepared bool + writeSPS bool + pesBuf *bytes.Buffer + patBuf []byte + pmtBuf []byte } func (self *SimpleH264Writer) prepare() (err error) { @@ -551,9 +559,11 @@ func (self *SimpleH264Writer) prepare() (err error) { {ProgramNumber: 1, ProgramMapPID: 0x1000}, }, } - if err = WritePATPacket(self.W, pat); err != nil { + bw := &bytes.Buffer{} + if err = WritePAT(bw, pat); err != nil { return } + self.patBuf = bw.Bytes() pmt := PMT{ PCRPID: 0x100, @@ -561,34 +571,62 @@ func (self *SimpleH264Writer) prepare() (err error) { {StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, }, } - if err = WritePMTPacket(self.W, pmt, 0x1000); err != nil { + bw = &bytes.Buffer{} + if err = WritePMT(bw, pmt); err != nil { return } + self.pmtBuf = bw.Bytes() - self.tsw = &TSWriter{ - W: self.W, + self.tswPMT = &TSWriter{ + PID: 0x1000, + } + self.tswPAT = &TSWriter{ + PID: 0, + } + self.tswH264 = &TSWriter{ PID: 0x100, } - self.tsw.EnableVecWriter() - if self.pts == 0 { - self.pts = PTS_HZ - self.pcr = PCR_HZ - } + self.tswH264.EnableVecWriter() + + self.pts = PTS_HZ + self.pcr = PCR_HZ self.pesBuf = &bytes.Buffer{} return } +func (self *SimpleH264Writer) WriteHeader() (err error) { + if !self.prepared { + if err = self.prepare(); err != nil { + return + } + self.prepared = true + } + self.tswPAT.W = self.W + if err = self.tswPAT.Write(self.patBuf); err != nil { + return + } + self.tswPMT.W = self.W + if err = self.tswPMT.Write(self.pmtBuf); err != nil { + return + } + self.writeSPS = true + return +} + func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (err error) { nalus := [][]byte{} if !self.prepared { - if err = self.prepare(); err != nil { + if err = self.WriteHeader(); err != nil { return } - self.prepared = true + } + + if self.writeSPS { + self.writeSPS = false nalus = append(nalus, self.SPS) nalus = append(nalus, self.PPS) } @@ -615,10 +653,11 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e data.Append(nalu) } - self.tsw.RandomAccessIndicator = sync - self.tsw.PCR = self.pcr + self.tswH264.RandomAccessIndicator = sync + self.tswH264.PCR = self.pcr - if err = self.tsw.WriteIovec(data); err != nil { + self.tswH264.W = self.W + if err = self.tswH264.WriteIovec(data); err != nil { return } @@ -629,13 +668,3 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e return } -func (self *SimpleH264Writer) LastPTSPCR() (pts, pcr uint64) { - return self.pts, self.pcr -} - -func (self *SimpleH264Writer) SetPTSPCR(pts, pcr uint64) { - self.pts = pts - self.pcr = pcr - return -} - From b0b956fc267ce8d4ff92430441f1cb3784d869f4 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 11 Dec 2015 16:21:57 +0800 Subject: [PATCH 36/82] expose PTS,PCR SimpleH264Writer --- writer.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/writer.go b/writer.go index cc11598..d11ce8d 100644 --- a/writer.go +++ b/writer.go @@ -542,8 +542,8 @@ type SimpleH264Writer struct { tswPMT *TSWriter tswH264 *TSWriter - pts uint64 - pcr uint64 + PTS uint64 + PCR uint64 prepared bool writeSPS bool @@ -589,11 +589,16 @@ func (self *SimpleH264Writer) prepare() (err error) { self.tswH264.EnableVecWriter() - self.pts = PTS_HZ - self.pcr = PCR_HZ + if self.PTS == 0 { + self.PTS = PTS_HZ + } + if self.PCR == 0 { + self.PCR = PCR_HZ + } self.pesBuf = &bytes.Buffer{} + self.prepared = true return } @@ -602,7 +607,6 @@ func (self *SimpleH264Writer) WriteHeader() (err error) { if err = self.prepare(); err != nil { return } - self.prepared = true } self.tswPAT.W = self.W if err = self.tswPAT.Write(self.patBuf); err != nil { @@ -634,7 +638,7 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e pes := PESHeader{ StreamId: StreamIdH264, - PTS: self.pts, + PTS: self.PTS, } if err = WritePESHeader(self.pesBuf, pes); err != nil { return @@ -654,15 +658,15 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e } self.tswH264.RandomAccessIndicator = sync - self.tswH264.PCR = self.pcr + self.tswH264.PCR = self.PCR self.tswH264.W = self.W if err = self.tswH264.WriteIovec(data); err != nil { return } - self.pts += uint64(duration)*PTS_HZ/uint64(self.TimeScale) - self.pcr += uint64(duration)*PCR_HZ/uint64(self.TimeScale) + self.PTS += uint64(duration)*PTS_HZ/uint64(self.TimeScale) + self.PCR += uint64(duration)*PCR_HZ/uint64(self.TimeScale) self.pesBuf.Reset() return From 20551c6a1ddf9e81a8f905c478b9d9750c9e6ed8 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 12 Dec 2015 10:58:21 +0800 Subject: [PATCH 37/82] change PTS to timeScale unit in SimpleH264Writer --- writer.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/writer.go b/writer.go index d11ce8d..684b604 100644 --- a/writer.go +++ b/writer.go @@ -542,8 +542,8 @@ type SimpleH264Writer struct { tswPMT *TSWriter tswH264 *TSWriter - PTS uint64 - PCR uint64 + PTS int64 + PCR int64 prepared bool writeSPS bool @@ -590,10 +590,12 @@ func (self *SimpleH264Writer) prepare() (err error) { self.tswH264.EnableVecWriter() if self.PTS == 0 { - self.PTS = PTS_HZ + // 1s + self.PTS = int64(self.TimeScale) } if self.PCR == 0 { - self.PCR = PCR_HZ + // 1s + self.PCR = int64(self.TimeScale) } self.pesBuf = &bytes.Buffer{} @@ -638,7 +640,7 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e pes := PESHeader{ StreamId: StreamIdH264, - PTS: self.PTS, + PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), } if err = WritePESHeader(self.pesBuf, pes); err != nil { return @@ -658,15 +660,15 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e } self.tswH264.RandomAccessIndicator = sync - self.tswH264.PCR = self.PCR + self.tswH264.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.TimeScale) self.tswH264.W = self.W if err = self.tswH264.WriteIovec(data); err != nil { return } - self.PTS += uint64(duration)*PTS_HZ/uint64(self.TimeScale) - self.PCR += uint64(duration)*PCR_HZ/uint64(self.TimeScale) + self.PTS += int64(duration) + self.PCR += int64(duration) self.pesBuf.Reset() return From 98ca49970524d66cfee2498a10e42c044f9ad10f Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 12 Dec 2015 18:41:05 +0800 Subject: [PATCH 38/82] add DiscontinuityIndicator --- example/test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++---- ts.go | 1 + writer.go | 8 ++++++- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/example/test.go b/example/test.go index ef29816..319f49c 100644 --- a/example/test.go +++ b/example/test.go @@ -174,19 +174,48 @@ func readSamples(filename string, ch chan Sample) { } } -func testInputGob(pathGob string, pathOut string, testSeg bool) { +func writeM3U8Header(w io.Writer) { + fmt.Fprintln(w, `#EXTM3U +#EXT-X-ALLOW-CACHE:YES +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-TARGETDURATION:9 +#EXT-X-VERSION:3 +#EXT-X-MEDIA-SEQUENCE:0`) +} + +func writeM3U8Item(w io.Writer, filename string, size int64, duration float64) { + fmt.Fprintf(w, `#EXT-X-BYTE-SIZE:%d +#EXTINF:%f, +%s +`, size, duration, filename) +} + +func writeM3U8Footer(w io.Writer) { + fmt.Fprintln(w, `#EXT-X-ENDLIST`) +} + +func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) { + var m3u8file *os.File + lastFilename := pathOut + gobfile, _ := os.Open(pathGob) outfile, _ := os.Create(pathOut) dec := gob.NewDecoder(gobfile) var allSamples GobAllSamples dec.Decode(&allSamples) + if writeM3u8 { + m3u8file, _ = os.Create("index.m3u8") + writeM3U8Header(m3u8file) + } + w := ts.SimpleH264Writer{ W: outfile, SPS: allSamples.SPS, PPS: allSamples.PPS, TimeScale: allSamples.TimeScale, } + lastPCR := int64(0) //w.WriteHeader() syncCount := 0 @@ -200,18 +229,38 @@ func testInputGob(pathGob string, pathOut string, testSeg bool) { syncCount++ if testSeg { if syncCount % 3 == 0 { + filename := fmt.Sprintf("%s.seg%d.ts", pathOut, segCount) + + if debugStream { + fmt.Println("stream:", "seg", segCount, "sync", syncCount, w.PCR) + } + + if m3u8file != nil { + info, _ := outfile.Stat() + size := info.Size() + dur := float64(w.PCR - lastPCR) / float64(allSamples.TimeScale) + writeM3U8Item(m3u8file, lastFilename, size, dur) + } + + lastFilename = filename outfile.Close() segCount++ - outfile, _ = os.Create(fmt.Sprintf("%s.seg%d.ts", pathOut, segCount)) + outfile, _ = os.Create(filename) w.W = outfile w.WriteHeader() - fmt.Println("seg", segCount, "sync", syncCount) + lastPCR = w.PCR } } + } w.WriteNALU(sample.Sync, sample.Duration, sample.Data) } + if m3u8file != nil { + writeM3U8Footer(m3u8file) + m3u8file.Close() + } + outfile.Close() if debugStream { fmt.Println("stream: written to", pathOut) @@ -223,6 +272,7 @@ func main() { output := flag.String("o", "", "output file") inputGob := flag.String("g", "", "input gob file") testSegment := flag.Bool("seg", false, "test segment") + writeM3u8 := flag.Bool("m3u8", false, "write m3u8 file") cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") flag.BoolVar(&debugData, "vd", false, "debug data") @@ -241,7 +291,7 @@ func main() { } if *inputGob != "" && *output != "" { - testInputGob(*inputGob, *output, *testSegment) + testInputGob(*inputGob, *output, *testSegment, *writeM3u8) return } diff --git a/ts.go b/ts.go index 10fda37..3c9ec2d 100644 --- a/ts.go +++ b/ts.go @@ -16,6 +16,7 @@ type TSHeader struct { OPCR uint64 ContinuityCounter uint PayloadUnitStart bool + DiscontinuityIndicator bool RandomAccessIndicator bool HeaderLength uint } diff --git a/writer.go b/writer.go index 684b604..995bc87 100644 --- a/writer.go +++ b/writer.go @@ -72,6 +72,9 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err if self.RandomAccessIndicator { extFlags |= 0x40 } + if self.DiscontinuityIndicator { + extFlags |= 0x80 + } if extFlags != 0 { flags |= EXT @@ -151,6 +154,7 @@ type TSWriter struct { PID uint TSHeader DisableHeaderPadding bool + DiscontinuityIndicator bool vecw *vecWriter } @@ -175,6 +179,7 @@ func (self *TSWriter) WriteIovec(data *iovec) (err error) { header := TSHeader{ PID: self.PID, ContinuityCounter: self.ContinuityCounter, + DiscontinuityIndicator: self.DiscontinuityIndicator, } if i == 0 { @@ -410,7 +415,7 @@ func WritePAT(w io.Writer, self PAT) (err error) { } } - psi := PSI { + psi := PSI{ TableIdExtension: 1, } if err = WritePSI(w, psi, bw.Bytes()); err != nil { @@ -582,6 +587,7 @@ func (self *SimpleH264Writer) prepare() (err error) { } self.tswPAT = &TSWriter{ PID: 0, + DiscontinuityIndicator: true, } self.tswH264 = &TSWriter{ PID: 0x100, From bb5bbdce1c6f64e0fc297cb425d0c0e7cdf0e52f Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 12 Dec 2015 19:23:10 +0800 Subject: [PATCH 39/82] add DiscontinuityIndicator for all PMT/H264 in SimpleH264Writer --- writer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/writer.go b/writer.go index 995bc87..8cb364d 100644 --- a/writer.go +++ b/writer.go @@ -584,6 +584,7 @@ func (self *SimpleH264Writer) prepare() (err error) { self.tswPMT = &TSWriter{ PID: 0x1000, + DiscontinuityIndicator: true, } self.tswPAT = &TSWriter{ PID: 0, @@ -591,6 +592,7 @@ func (self *SimpleH264Writer) prepare() (err error) { } self.tswH264 = &TSWriter{ PID: 0x100, + DiscontinuityIndicator: true, } self.tswH264.EnableVecWriter() From d892f58a486a11753ee600b52f116de85d2e6cf5 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 2 Mar 2016 14:57:49 +0800 Subject: [PATCH 40/82] fix WriteTSHeader PID bug --- writer.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/writer.go b/writer.go index 8cb364d..abb258b 100644 --- a/writer.go +++ b/writer.go @@ -56,9 +56,13 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err if self.PayloadUnitStart { flags |= 0x400000 } - flags |= (self.PID&0x1fff00)<<8 + flags |= (self.PID&0x1fff)<<8 flags |= self.ContinuityCounter&0xf + if DebugWriter { + fmt.Printf("tsw: pid=%x\n", self.PID) + } + const PCR = 0x10 const OPCR = 0x08 const EXT = 0x20 From 80338a4803a16c1e1ac38ecf3803bf3b5661c000 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 2 Mar 2016 14:58:58 +0800 Subject: [PATCH 41/82] fix ReadPESHeader DataLength zero bug --- reader.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/reader.go b/reader.go index 81f2309..72e5e85 100644 --- a/reader.go +++ b/reader.go @@ -191,7 +191,7 @@ func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { return } if DebugReader { - fmt.Println("ts: data", lr.N) + fmt.Println("ts: data len", lr.N) } if n, err = lr.Read(data[:lr.N]); err != nil { return @@ -494,16 +494,16 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { if length, err = ReadUInt(r, 2); err != nil { return } + if DebugReader { + fmt.Printf("pes: StreamId=%x length=%d\n", self.StreamId, length) + } + if length == 0 { length = 1<<31 } lrAll := &io.LimitedReader{R: r, N: int64(length)} lr := lrAll - if DebugReader { - fmt.Printf("pes: StreamId=%x length=%d\n", self.StreamId, length) - } - // 10(2) // PES scrambling control(2) // PES priority(1) @@ -670,10 +670,13 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } } - if lr.N > 0 && lr.N < 65536 { + if lr.N > 0 { if err = ReadDummy(lr, int(lr.N)); err != nil { return } + } + + if lrAll.N < 65536 { self.DataLength = uint(lrAll.N) } From f080de035067175eb4c16e76131d31cb05b7e8f2 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 2 Mar 2016 14:59:25 +0800 Subject: [PATCH 42/82] allow multiple stream copy --- example/test.go | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/example/test.go b/example/test.go index 319f49c..96900fb 100644 --- a/example/test.go +++ b/example/test.go @@ -89,6 +89,9 @@ func readSamples(filename string, ch chan Sample) { } onStreamPayloadUnitEnd := func(stream *Stream) { + if debugStream { + fmt.Printf("stream: %s end\n", stream.Title) + } if debugData { fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") fmt.Println(hex.Dump(stream.Data.Bytes())) @@ -119,16 +122,15 @@ func readSamples(filename string, ch chan Sample) { } stream.FirstTSHeader = header if debugStream { - fmt.Printf("stream: start\n") + fmt.Printf("stream: %s start\n", stream.Title) } } if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { return } - if debugStream { - fmt.Printf("stream: %d/%d\n", stream.Data.Len(), stream.PESHeader.DataLength) + fmt.Printf("stream: %s %d/%d\n", stream.Title, stream.Data.Len(), stream.PESHeader.DataLength) } if stream.Data.Len() == int(stream.PESHeader.DataLength) { @@ -143,7 +145,7 @@ func readSamples(filename string, ch chan Sample) { return } if debugData { - fmt.Println(header, n) + fmt.Println("header:", header, n) } payload = data[:n] @@ -324,6 +326,7 @@ func main() { PCRPID: 0x100, ElementaryStreamInfos: []ts.ElementaryStreamInfo{ {StreamType: ts.ElementaryStreamTypeH264, ElementaryPID: 0x100}, + {StreamType: ts.ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, }, } if err = ts.WritePMTPacket(file, pmt, 0x1000); err != nil { @@ -335,11 +338,24 @@ func main() { var w *ts.TSWriter var sample Sample writeSample := func() (err error) { + var streamId uint + var pid uint + + switch sample.Type { + case ts.ElementaryStreamTypeH264: + streamId = ts.StreamIdH264 + pid = 0x100 + case ts.ElementaryStreamTypeAdtsAAC: + streamId = ts.StreamIdAAC + pid = 0x101 + } + pes := ts.PESHeader{ - StreamId: ts.StreamIdH264, + StreamId: streamId, PTS: sample.PTS, DTS: sample.DTS, } + w.PID = pid w.PCR = sample.PCR w.RandomAccessIndicator = sample.RandomAccessIndicator if err = ts.WritePESPacket(w, pes, sample.Data); err != nil { @@ -363,18 +379,17 @@ func main() { if sample, ok = <-ch; !ok { break } - if sample.Type == ts.ElementaryStreamTypeH264 { - if debugStream { - fmt.Println("sample: ", len(sample.Data), - "PCR", sample.PCR, "PTS", sample.PTS, - "DTS", sample.DTS, "sync", sample.RandomAccessIndicator, - ) - //fmt.Print(hex.Dump(sample.Data)) - } - if file != nil { - writeSample() - } + if debugStream { + fmt.Println("sample: ", sample.Type, len(sample.Data), + "PCR", sample.PCR, "PTS", sample.PTS, + "DTS", sample.DTS, "sync", sample.RandomAccessIndicator, + ) + //fmt.Print(hex.Dump(sample.Data)) + } + + if file != nil { + writeSample() } } From 4b857a6eedf41a43e3997a8dcc6a307f89b95e13 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 6 Mar 2016 16:10:40 +0800 Subject: [PATCH 43/82] add Muxer --- muxer.go | 166 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 muxer.go diff --git a/muxer.go b/muxer.go new file mode 100644 index 0000000..aaa7017 --- /dev/null +++ b/muxer.go @@ -0,0 +1,166 @@ + +package ts + +import ( + "bytes" + "io" +) + +type Track struct { + timeScale int64 + writeSPS bool + SPS []byte + PPS []byte + spsHasWritten bool + tsw *TSWriter + PTS int64 + PCR int64 + pesBuf *bytes.Buffer +} + +func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err error) { + nalus := [][]byte{} + + if !self.spsHasWritten { + nalus = append(nalus, self.SPS) + nalus = append(nalus, self.PPS) + self.spsHasWritten = true + } + nalus = append(nalus, nalu) + + pes := PESHeader{ + StreamId: StreamIdH264, + PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), + } + if err = WritePESHeader(self.pesBuf, pes); err != nil { + return + } + + data := &iovec{} + data.Append(self.pesBuf.Bytes()) + for i, nalu := range nalus { + var startCode []byte + if i == 0 { + startCode = []byte{0,0,0,1,0x9,0xf0,0,0,0,1} // AUD + } else { + startCode = []byte{0,0,1} + } + data.Append(startCode) + data.Append(nalu) + } + + self.tsw.RandomAccessIndicator = sync + self.tsw.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.timeScale) + + if err = self.tsw.WriteIovec(data); err != nil { + return + } + + self.PTS += int64(duration) + self.PCR += int64(duration) + self.pesBuf.Reset() + + return +} + +func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { + pes := PESHeader{ + StreamId: StreamIdAAC, + PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), + } + if err = WritePESHeader(self.pesBuf, pes); err != nil { + return + } + + data := &iovec{} + data.Append(self.pesBuf.Bytes()) + data.Append(frame) + + self.tsw.RandomAccessIndicator = true + self.tsw.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.timeScale) + if err = self.tsw.WriteIovec(data); err != nil { + return + } + + self.PTS += int64(duration) + self.PCR += int64(duration) + self.pesBuf.Reset() + + return +} + +func newTrack(w io.Writer, pid uint, timeScale int64) (track *Track) { + track = &Track{ + tsw: &TSWriter{ + W: w, + PID: pid, + DiscontinuityIndicator: true, + }, + timeScale: timeScale, + pesBuf: &bytes.Buffer{}, + } + track.tsw.EnableVecWriter() + track.PTS = timeScale + track.PCR = timeScale + return +} + +type Muxer struct { + W io.Writer + TimeScale int64 + tswPAT *TSWriter + tswPMT *TSWriter + elemStreams []ElementaryStreamInfo +} + +func (self *Muxer) AddAACTrack() (track *Track) { + self.elemStreams = append( + self.elemStreams, + ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, + ) + return newTrack(self.W, 0x101, self.TimeScale) +} + +func (self *Muxer) AddH264Track() (track *Track) { + self.elemStreams = append( + self.elemStreams, + ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, + ) + return newTrack(self.W, 0x100, self.TimeScale) +} + +func (self *Muxer) WriteHeader() (err error) { + bufPAT := &bytes.Buffer{} + bufPMT := &bytes.Buffer{} + + pat := PAT{ + Entries: []PATEntry{ + {ProgramNumber: 1, ProgramMapPID: 0x1000}, + }, + } + WritePAT(bufPAT, pat) + pmt := PMT{ + PCRPID: 0x100, + ElementaryStreamInfos: self.elemStreams, + } + WritePMT(bufPMT, pmt) + + tswPMT := &TSWriter{ + W: self.W, + PID: 0x1000, + DiscontinuityIndicator: true, + } + tswPAT := &TSWriter{ + W: self.W, + PID: 0, + DiscontinuityIndicator: true, + } + if err = tswPAT.Write(bufPAT.Bytes()); err != nil { + return + } + if err = tswPMT.Write(bufPMT.Bytes()); err != nil { + return + } + return +} + From 443a7e486917863ccb9b8f7eb169e4bbdb78223e Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 7 Mar 2016 17:19:12 +0800 Subject: [PATCH 44/82] adjust PTS/PCR/TimeScale in Muxer --- example/test.go | 27 ++++++------ muxer.go | 114 +++++++++++++++++++++++++++--------------------- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/example/test.go b/example/test.go index 96900fb..d86cdaa 100644 --- a/example/test.go +++ b/example/test.go @@ -211,15 +211,16 @@ func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) writeM3U8Header(m3u8file) } - w := ts.SimpleH264Writer{ + muxer := &ts.Muxer{ W: outfile, - SPS: allSamples.SPS, - PPS: allSamples.PPS, - TimeScale: allSamples.TimeScale, } - lastPCR := int64(0) - //w.WriteHeader() + trackH264 := muxer.AddH264Track() + trackH264.SPS = allSamples.SPS + trackH264.PPS = allSamples.PPS + trackH264.TimeScale = int64(allSamples.TimeScale) + muxer.WriteHeader() + lastPTS := int64(0) syncCount := 0 segCount := 0 @@ -234,13 +235,13 @@ func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) filename := fmt.Sprintf("%s.seg%d.ts", pathOut, segCount) if debugStream { - fmt.Println("stream:", "seg", segCount, "sync", syncCount, w.PCR) + fmt.Println("stream:", "seg", segCount, "sync", syncCount, trackH264.PTS) } if m3u8file != nil { info, _ := outfile.Stat() size := info.Size() - dur := float64(w.PCR - lastPCR) / float64(allSamples.TimeScale) + dur := float64(trackH264.PTS - lastPTS) / float64(allSamples.TimeScale) writeM3U8Item(m3u8file, lastFilename, size, dur) } @@ -248,14 +249,14 @@ func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) outfile.Close() segCount++ outfile, _ = os.Create(filename) - w.W = outfile - w.WriteHeader() - lastPCR = w.PCR + muxer.W = outfile + muxer.WriteHeader() + lastPTS = trackH264.PTS } } - } - w.WriteNALU(sample.Sync, sample.Duration, sample.Data) + + trackH264.WriteH264NALU(sample.Sync, sample.Duration, sample.Data) } if m3u8file != nil { diff --git a/muxer.go b/muxer.go index aaa7017..fbec002 100644 --- a/muxer.go +++ b/muxer.go @@ -7,15 +7,46 @@ import ( ) type Track struct { - timeScale int64 - writeSPS bool SPS []byte PPS []byte - spsHasWritten bool - tsw *TSWriter + PTS int64 - PCR int64 - pesBuf *bytes.Buffer + TimeScale int64 + + writeSPS bool + spsHasWritten bool + pcrHasWritten bool + + streamId uint + tsw *TSWriter + dataBuf *iovec + cacheSize int +} + +func (self *Track) setPCR() { + if !self.pcrHasWritten { + self.tsw.PCR = 24300000 + self.pcrHasWritten = true + } else { + self.tsw.PCR = 0 + } +} + +func (self *Track) getPesHeader() (data []byte){ + if self.PTS == 0 { + self.PTS = self.TimeScale + } + buf := &bytes.Buffer{} + pes := PESHeader{ + StreamId: self.streamId, + PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), + } + WritePESHeader(buf, pes) + return buf.Bytes() +} + +func (self *Track) incPTS(delta int) { + self.PTS += int64(delta) } func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err error) { @@ -28,16 +59,8 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro } nalus = append(nalus, nalu) - pes := PESHeader{ - StreamId: StreamIdH264, - PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), - } - if err = WritePESHeader(self.pesBuf, pes); err != nil { - return - } - data := &iovec{} - data.Append(self.pesBuf.Bytes()) + data.Append(self.getPesHeader()) for i, nalu := range nalus { var startCode []byte if i == 0 { @@ -50,64 +73,51 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro } self.tsw.RandomAccessIndicator = sync - self.tsw.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.timeScale) - + self.setPCR() if err = self.tsw.WriteIovec(data); err != nil { return } - self.PTS += int64(duration) - self.PCR += int64(duration) - self.pesBuf.Reset() - + self.incPTS(duration) return } func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { - pes := PESHeader{ - StreamId: StreamIdAAC, - PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), - } - if err = WritePESHeader(self.pesBuf, pes); err != nil { - return + if self.dataBuf != nil && self.dataBuf.Len > self.cacheSize { + self.tsw.RandomAccessIndicator = true + self.setPCR() + if err = self.tsw.WriteIovec(self.dataBuf); err != nil { + return + } + self.dataBuf = nil } - data := &iovec{} - data.Append(self.pesBuf.Bytes()) - data.Append(frame) - - self.tsw.RandomAccessIndicator = true - self.tsw.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.timeScale) - if err = self.tsw.WriteIovec(data); err != nil { - return + if self.dataBuf == nil { + self.dataBuf = &iovec{} + self.dataBuf.Append(self.getPesHeader()) + } else { + self.dataBuf.Append(frame) } - self.PTS += int64(duration) - self.PCR += int64(duration) - self.pesBuf.Reset() - + self.incPTS(duration) return } -func newTrack(w io.Writer, pid uint, timeScale int64) (track *Track) { +func newTrack(w io.Writer, pid uint, streamId uint) (track *Track) { track = &Track{ tsw: &TSWriter{ W: w, PID: pid, - DiscontinuityIndicator: true, + //DiscontinuityIndicator: true, }, - timeScale: timeScale, - pesBuf: &bytes.Buffer{}, + streamId: streamId, } track.tsw.EnableVecWriter() - track.PTS = timeScale - track.PCR = timeScale return } type Muxer struct { W io.Writer - TimeScale int64 tswPAT *TSWriter tswPMT *TSWriter elemStreams []ElementaryStreamInfo @@ -118,7 +128,10 @@ func (self *Muxer) AddAACTrack() (track *Track) { self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, ) - return newTrack(self.W, 0x101, self.TimeScale) + track = newTrack(self.W, 0x101, StreamIdAAC) + track.pcrHasWritten = true + track.cacheSize = 3000 + return } func (self *Muxer) AddH264Track() (track *Track) { @@ -126,7 +139,8 @@ func (self *Muxer) AddH264Track() (track *Track) { self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, ) - return newTrack(self.W, 0x100, self.TimeScale) + track = newTrack(self.W, 0x100, StreamIdH264) + return } func (self *Muxer) WriteHeader() (err error) { @@ -148,12 +162,12 @@ func (self *Muxer) WriteHeader() (err error) { tswPMT := &TSWriter{ W: self.W, PID: 0x1000, - DiscontinuityIndicator: true, + //DiscontinuityIndicator: true, } tswPAT := &TSWriter{ W: self.W, PID: 0, - DiscontinuityIndicator: true, + //DiscontinuityIndicator: true, } if err = tswPAT.Write(bufPAT.Bytes()); err != nil { return From a623b754708bbe861a6b88297f71536c88016b7f Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Mar 2016 01:25:20 +0800 Subject: [PATCH 45/82] add packet_length field for PES Header --- writer.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/writer.go b/writer.go index abb258b..4d237f3 100644 --- a/writer.go +++ b/writer.go @@ -305,10 +305,10 @@ func bswap32(v uint) uint { return (v>>24)|((v>>16)&0xff)<<8|((v>>8)&0xff)<<16|(v&0xff)<<24 } -func WritePESHeader(w io.Writer, self PESHeader) (err error) { +func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html - var pts_dts_flags, header_length uint + var pts_dts_flags, header_length, packet_length uint // start code(24) 000001 // StreamId(8) @@ -347,8 +347,14 @@ func WritePESHeader(w io.Writer, self PESHeader) (err error) { header_length += 5 } + if dataLength > 0 { + packet_length = uint(dataLength) + header_length + 3 + } // packet_length(16) if zero then variable length - if err = WriteUInt(w, 0, 2); err != nil { + // Specifies the number of bytes remaining in the packet after this field. Can be zero. + // If the PES packet length is set to zero, the PES packet can be of any length. + // A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream. + if err = WriteUInt(w, packet_length, 2); err != nil { return } @@ -389,7 +395,7 @@ func WritePESHeader(w io.Writer, self PESHeader) (err error) { func WritePESPacket(w *TSWriter, header PESHeader, data []byte) (err error) { bw := &bytes.Buffer{} - if err = WritePESHeader(bw, header); err != nil { + if err = WritePESHeader(bw, header, len(data)); err != nil { return } iov := &iovec{} @@ -654,7 +660,7 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e StreamId: StreamIdH264, PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), } - if err = WritePESHeader(self.pesBuf, pes); err != nil { + if err = WritePESHeader(self.pesBuf, pes, 0); err != nil { return } From 7a1c3ed2cc24df85303f66f23830bdaf8cd9dced Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Mar 2016 01:26:25 +0800 Subject: [PATCH 46/82] add Prepend() for iovec --- vecio.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vecio.go b/vecio.go index 8256d03..2334188 100644 --- a/vecio.go +++ b/vecio.go @@ -17,6 +17,11 @@ type iovec struct { idx int } +func (self *iovec) Prepend(b []byte) { + self.data = append([][]byte{b}, self.data...) + self.Len += len(b) +} + func (self *iovec) Append(b []byte) { self.data = append(self.data, b) self.Len += len(b) From 4aa58ada6f4748785b73835f420e28a3ec5cd420 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 9 Mar 2016 01:27:37 +0800 Subject: [PATCH 47/82] write DataLength for PESHeader in Muxer --- muxer.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/muxer.go b/muxer.go index fbec002..73b5af8 100644 --- a/muxer.go +++ b/muxer.go @@ -32,7 +32,7 @@ func (self *Track) setPCR() { } } -func (self *Track) getPesHeader() (data []byte){ +func (self *Track) getPesHeader(dataLength int) (data []byte){ if self.PTS == 0 { self.PTS = self.TimeScale } @@ -41,7 +41,7 @@ func (self *Track) getPesHeader() (data []byte){ StreamId: self.streamId, PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), } - WritePESHeader(buf, pes) + WritePESHeader(buf, pes, dataLength) return buf.Bytes() } @@ -60,7 +60,6 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro nalus = append(nalus, nalu) data := &iovec{} - data.Append(self.getPesHeader()) for i, nalu := range nalus { var startCode []byte if i == 0 { @@ -72,6 +71,7 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro data.Append(nalu) } + data.Prepend(self.getPesHeader(data.Len)) self.tsw.RandomAccessIndicator = sync self.setPCR() if err = self.tsw.WriteIovec(data); err != nil { @@ -84,6 +84,7 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { if self.dataBuf != nil && self.dataBuf.Len > self.cacheSize { + self.dataBuf.Prepend(self.getPesHeader(self.dataBuf.Len)) self.tsw.RandomAccessIndicator = true self.setPCR() if err = self.tsw.WriteIovec(self.dataBuf); err != nil { @@ -91,14 +92,10 @@ func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { } self.dataBuf = nil } - if self.dataBuf == nil { self.dataBuf = &iovec{} - self.dataBuf.Append(self.getPesHeader()) - } else { - self.dataBuf.Append(frame) } - + self.dataBuf.Append(frame) self.incPTS(duration) return } @@ -108,7 +105,7 @@ func newTrack(w io.Writer, pid uint, streamId uint) (track *Track) { tsw: &TSWriter{ W: w, PID: pid, - //DiscontinuityIndicator: true, + DiscontinuityIndicator: true, }, streamId: streamId, } @@ -162,12 +159,12 @@ func (self *Muxer) WriteHeader() (err error) { tswPMT := &TSWriter{ W: self.W, PID: 0x1000, - //DiscontinuityIndicator: true, + DiscontinuityIndicator: true, } tswPAT := &TSWriter{ W: self.W, PID: 0, - //DiscontinuityIndicator: true, + DiscontinuityIndicator: true, } if err = tswPAT.Write(bufPAT.Bytes()); err != nil { return From d067b228ec83a8a208cd6837fe6ad03bc0323cd0 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 26 Mar 2016 14:27:56 +0800 Subject: [PATCH 48/82] don't write audio PES packet length --- muxer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/muxer.go b/muxer.go index 73b5af8..2663fff 100644 --- a/muxer.go +++ b/muxer.go @@ -71,7 +71,7 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro data.Append(nalu) } - data.Prepend(self.getPesHeader(data.Len)) + data.Prepend(self.getPesHeader(0)) self.tsw.RandomAccessIndicator = sync self.setPCR() if err = self.tsw.WriteIovec(data); err != nil { From 6164f70ade824771ef66923d6b2bdc49a51ffc87 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 28 Mar 2016 15:09:03 +0800 Subject: [PATCH 49/82] add TSMuxer.WriteTo func --- muxer.go | 30 ++++++++++++++++++++---------- writer.go | 28 +++++++++++++++++++--------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/muxer.go b/muxer.go index 2663fff..3727ec1 100644 --- a/muxer.go +++ b/muxer.go @@ -17,6 +17,7 @@ type Track struct { spsHasWritten bool pcrHasWritten bool + mux *Muxer streamId uint tsw *TSWriter dataBuf *iovec @@ -74,7 +75,7 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro data.Prepend(self.getPesHeader(0)) self.tsw.RandomAccessIndicator = sync self.setPCR() - if err = self.tsw.WriteIovec(data); err != nil { + if err = self.tsw.WriteIovecTo(self.mux.W, data); err != nil { return } @@ -87,7 +88,7 @@ func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { self.dataBuf.Prepend(self.getPesHeader(self.dataBuf.Len)) self.tsw.RandomAccessIndicator = true self.setPCR() - if err = self.tsw.WriteIovec(self.dataBuf); err != nil { + if err = self.tsw.WriteIovecTo(self.mux.W, self.dataBuf); err != nil { return } self.dataBuf = nil @@ -100,10 +101,10 @@ func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { return } -func newTrack(w io.Writer, pid uint, streamId uint) (track *Track) { +func newTrack(mux *Muxer, pid uint, streamId uint) (track *Track) { track = &Track{ + mux: mux, tsw: &TSWriter{ - W: w, PID: pid, DiscontinuityIndicator: true, }, @@ -118,6 +119,8 @@ type Muxer struct { tswPAT *TSWriter tswPMT *TSWriter elemStreams []ElementaryStreamInfo + TrackH264 *Track + Tracks []*Track } func (self *Muxer) AddAACTrack() (track *Track) { @@ -125,9 +128,10 @@ func (self *Muxer) AddAACTrack() (track *Track) { self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, ) - track = newTrack(self.W, 0x101, StreamIdAAC) + track = newTrack(self, 0x101, StreamIdAAC) track.pcrHasWritten = true track.cacheSize = 3000 + self.Tracks = append(self.Tracks, track) return } @@ -136,7 +140,9 @@ func (self *Muxer) AddH264Track() (track *Track) { self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, ) - track = newTrack(self.W, 0x100, StreamIdH264) + track = newTrack(self, 0x100, StreamIdH264) + self.TrackH264 = track + self.Tracks = append(self.Tracks, track) return } @@ -157,21 +163,25 @@ func (self *Muxer) WriteHeader() (err error) { WritePMT(bufPMT, pmt) tswPMT := &TSWriter{ - W: self.W, PID: 0x1000, DiscontinuityIndicator: true, } tswPAT := &TSWriter{ - W: self.W, PID: 0, DiscontinuityIndicator: true, } - if err = tswPAT.Write(bufPAT.Bytes()); err != nil { + if err = tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil { return } - if err = tswPMT.Write(bufPMT.Bytes()); err != nil { + if err = tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil { return } + + for _, track := range(self.Tracks) { + track.spsHasWritten = false + track.pcrHasWritten = false + } + return } diff --git a/writer.go b/writer.go index 4d237f3..3e7468f 100644 --- a/writer.go +++ b/writer.go @@ -174,11 +174,22 @@ func (self *TSWriter) EnableVecWriter() { } func (self *TSWriter) WriteIovec(data *iovec) (err error) { - w := self.W if self.vecw != nil { - w = self.vecw + if err = self.WriteIovecTo(self.vecw, data); err != nil { + return + } + if err = self.vecw.Flush(); err != nil { + return + } + } else { + if err = self.WriteIovecTo(self.W, data); err != nil { + return + } } + return +} +func (self *TSWriter) WriteIovecTo(w io.Writer, data *iovec) (err error) { for i := 0; data.Len > 0; i++ { header := TSHeader{ PID: self.PID, @@ -216,16 +227,15 @@ func (self *TSWriter) WriteIovec(data *iovec) (err error) { self.ContinuityCounter++ } - - if self.vecw != nil { - if err = self.vecw.Flush(); err != nil { - return - } - } - return } +func (self *TSWriter) WriteTo(w io.Writer, data []byte) (err error) { + iov := &iovec{} + iov.Append(data) + return self.WriteIovecTo(w, iov) +} + func (self *TSWriter) Write(data []byte) (err error) { iov := &iovec{} iov.Append(data) From 431a02254da99b227bb19d3d9cf7acf22ac443e8 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 28 Mar 2016 15:21:24 +0800 Subject: [PATCH 50/82] set PCR before write ts header --- muxer.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/muxer.go b/muxer.go index 3727ec1..8da020f 100644 --- a/muxer.go +++ b/muxer.go @@ -15,7 +15,6 @@ type Track struct { writeSPS bool spsHasWritten bool - pcrHasWritten bool mux *Muxer streamId uint @@ -25,12 +24,7 @@ type Track struct { } func (self *Track) setPCR() { - if !self.pcrHasWritten { - self.tsw.PCR = 24300000 - self.pcrHasWritten = true - } else { - self.tsw.PCR = 0 - } + self.tsw.PCR = uint64(self.PTS)*PCR_HZ/uint64(self.TimeScale) } func (self *Track) getPesHeader(dataLength int) (data []byte){ @@ -129,7 +123,6 @@ func (self *Muxer) AddAACTrack() (track *Track) { ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, ) track = newTrack(self, 0x101, StreamIdAAC) - track.pcrHasWritten = true track.cacheSize = 3000 self.Tracks = append(self.Tracks, track) return @@ -179,7 +172,6 @@ func (self *Muxer) WriteHeader() (err error) { for _, track := range(self.Tracks) { track.spsHasWritten = false - track.pcrHasWritten = false } return From a524111ed68909a55d6678aae4baaf2ac264d931 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 2 Apr 2016 18:38:15 +0800 Subject: [PATCH 51/82] add demuxer --- demuxer.go | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++ muxer.go | 17 ----- reader.go | 149 ++++++++++++++++++------------------ track.go | 39 ++++++++++ 4 files changed, 335 insertions(+), 91 deletions(-) create mode 100644 demuxer.go create mode 100644 track.go diff --git a/demuxer.go b/demuxer.go new file mode 100644 index 0000000..b6b18b9 --- /dev/null +++ b/demuxer.go @@ -0,0 +1,221 @@ + +package ts + +import ( + "io" + "bytes" + "fmt" + "github.com/nareix/mp4/isom" +) + +type Demuxer struct { + R io.Reader + + pat PAT + pmt *PMT + Tracks []*Track + TrackH264 *Track + TrackAAC *Track +} + +// ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) +// WritePayload(pid, pts, dts, isKeyFrame, payloads, isVideoFrame) + +func (self *Demuxer) TimeScale() int64 { + return PTS_HZ +} + +func (self *Demuxer) ReadHeader() (err error) { + self.Tracks = []*Track{} + self.TrackH264 = nil + self.TrackAAC = nil + + for { + if self.pmt != nil { + n := 0 + for _, track := range(self.Tracks) { + if track.payloadReady { + n++ + } + } + if n == len(self.Tracks) { + break + } + } + + if err = self.readPacket(); err != nil { + return + } + } + + return +} + +func (self *Demuxer) ReadSample() (track *Track, err error) { + if len(self.Tracks) == 0 { + err = fmt.Errorf("no track") + return + } + + for { + for _, _track := range(self.Tracks) { + if _track.payloadReady { + track = _track + return + } + } + + if err = self.readPacket(); err != nil { + return + } + } +} + +func (self *Demuxer) readPacket() (err error) { + var header TSHeader + var n int + var data [188]byte + + if header, n, err = ReadTSPacket(self.R, data[:]); err != nil { + return + } + payload := data[:n] + + if header.PID == 0 { + if self.pat, err = ReadPAT(bytes.NewReader(payload)); err != nil { + return + } + } else { + if self.pmt == nil { + for _, entry := range(self.pat.Entries) { + if entry.ProgramMapPID == header.PID { + self.pmt = new(PMT) + if *self.pmt, err = ReadPMT(bytes.NewReader(payload)); err != nil { + return + } + for _, info := range(self.pmt.ElementaryStreamInfos) { + track := &Track{} + + track.demuxer = self + track.pid = info.ElementaryPID + switch info.StreamType { + case ElementaryStreamTypeH264: + track.Type = H264 + self.TrackH264 = track + self.Tracks = append(self.Tracks, track) + case ElementaryStreamTypeAdtsAAC: + track.Type = AAC + self.TrackAAC = track + self.Tracks = append(self.Tracks, track) + } + } + } + } + } else { + + for _, track := range(self.Tracks) { + if header.PID == track.pid { + if err = track.appendPacket(header, payload); err != nil { + return + } + } + } + } + } + + return +} + +func (self *Track) GetMPEG4AudioConfig() isom.MPEG4AudioConfig { + return self.mpeg4AudioConfig +} + +func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { + for !self.payloadReady { + if err = self.demuxer.readPacket(); err != nil { + return + } + } + + if self.Type == AAC { + var n int + if _, data, n, self.payload, err = isom.ReadADTSPayload(self.payload); err != nil { + return + } + pts = self.PTS + dts = pts + self.PTS += int64(PTS_HZ*n)/int64(self.mpeg4AudioConfig.SampleRate) + if len(self.payload) == 0 { + self.payloadReady = false + } + } else { + dts = int64(self.peshdr.DTS) + pts = int64(self.peshdr.PTS) + isKeyFrame = self.tshdr.RandomAccessIndicator + data = self.payload + self.payloadReady = false + } + + if dts == 0 { + dts = pts + } + return +} + +func (self *Track) appendPayload() (err error) { + self.payload = self.buf.Bytes() + if len(self.payload) == 0 { + err = fmt.Errorf("empty payload") + return + } + + if self.Type == AAC { + if !self.mpeg4AudioConfig.IsValid() { + if self.mpeg4AudioConfig, _, _, _, err = isom.ReadADTSPayload(self.payload); err != nil { + return + } + self.mpeg4AudioConfig = self.mpeg4AudioConfig.Complete() + if !self.mpeg4AudioConfig.IsValid() { + err = fmt.Errorf("invalid MPEG4AudioConfig") + return + } + } + self.PTS = int64(self.peshdr.PTS) + } + + self.payloadReady = true + return +} + +func (self *Track) appendPacket(header TSHeader, payload []byte) (err error) { + r := bytes.NewReader(payload) + lr := &io.LimitedReader{R: r, N: int64(len(payload))} + + if header.PayloadUnitStart && self.peshdr != nil && self.peshdr.DataLength == 0 { + if err = self.appendPayload(); err != nil { + return + } + } + + if header.PayloadUnitStart { + self.payloadReady = false + self.buf = bytes.Buffer{} + if self.peshdr, err = ReadPESHeader(lr); err != nil { + return + } + self.tshdr = header + } + + if _, err = io.CopyN(&self.buf, lr, lr.N); err != nil { + return + } + + if self.buf.Len() == int(self.peshdr.DataLength) { + if err = self.appendPayload(); err != nil { + return + } + } + + return +} + diff --git a/muxer.go b/muxer.go index 8da020f..f8a634b 100644 --- a/muxer.go +++ b/muxer.go @@ -6,23 +6,6 @@ import ( "io" ) -type Track struct { - SPS []byte - PPS []byte - - PTS int64 - TimeScale int64 - - writeSPS bool - spsHasWritten bool - - mux *Muxer - streamId uint - tsw *TSWriter - dataBuf *iovec - cacheSize int -} - func (self *Track) setPCR() { self.tsw.PCR = uint64(self.PTS)*PCR_HZ/uint64(self.TimeScale) } diff --git a/reader.go b/reader.go index 72e5e85..dfd01a6 100644 --- a/reader.go +++ b/reader.go @@ -96,89 +96,90 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { if length, err = ReadUInt(r, 1); err != nil { return } - lr := &io.LimitedReader{R: r, N: int64(length)} - if flags, err = ReadUInt(lr, 1); err != nil { - return - } - - if DebugReader { - fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ - Fields: []struct{ - Length int - Desc string - }{ - {1, "discontinuity_indicator"}, - {1, "random_access_indicator"}, - {1, "elementary_stream_priority_indicator"}, - {1, "pcr_flag"}, - {1, "opcr_flag"}, - {1, "splicing_point_flag"}, - {1, "transport_private_data_flag"}, - {1, "adaptation_field_extension_flag"}, - }, - Val: flags, - Length: 8, - }) - } - - // random_access_indicator - if flags & 0x40 != 0 { - self.RandomAccessIndicator = true - } - - // PCR - if flags & 0x10 != 0 { - var v uint64 - if v, err = ReadUInt64(lr, 6); err != nil { + if length > 0 { + lr := &io.LimitedReader{R: r, N: int64(length)} + if flags, err = ReadUInt(lr, 1); err != nil { return } - // clock is 27MHz - self.PCR = UIntToPCR(v) + if DebugReader { - fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) - } - } - - // OPCR - if flags & 0x08 != 0 { - var v uint64 - if v, err = ReadUInt64(lr, 6); err != nil { - return - } - self.OPCR = UIntToPCR(v) - } - - // Splice countdown - if flags & 0x04 != 0 { - if _, err = ReadUInt(lr, 1); err != nil { - return - } - } - - // Transport private data - if flags & 0x02 != 0 { - var length uint - if length, err = ReadUInt(lr, 1); err != nil { - return + fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ + Fields: []struct{ + Length int + Desc string + }{ + {1, "discontinuity_indicator"}, + {1, "random_access_indicator"}, + {1, "elementary_stream_priority_indicator"}, + {1, "pcr_flag"}, + {1, "opcr_flag"}, + {1, "splicing_point_flag"}, + {1, "transport_private_data_flag"}, + {1, "adaptation_field_extension_flag"}, + }, + Val: flags, + Length: 8, + }) } - b := make([]byte, length) - if _, err = lr.Read(b); err != nil { - return - } - } - - // Adaptation extension - if lr.N > 0 { - if DebugReader { - // rubish - fmt.Println("ts: skip", lr.N) + // random_access_indicator + if flags & 0x40 != 0 { + self.RandomAccessIndicator = true } - if err = ReadDummy(lr, int(lr.N)); err != nil { - return + // PCR + if flags & 0x10 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 6); err != nil { + return + } + // clock is 27MHz + self.PCR = UIntToPCR(v) + if DebugReader { + fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) + } } + // OPCR + if flags & 0x08 != 0 { + var v uint64 + if v, err = ReadUInt64(lr, 6); err != nil { + return + } + self.OPCR = UIntToPCR(v) + } + + // Splice countdown + if flags & 0x04 != 0 { + if _, err = ReadUInt(lr, 1); err != nil { + return + } + } + + // Transport private data + if flags & 0x02 != 0 { + var length uint + if length, err = ReadUInt(lr, 1); err != nil { + return + } + + b := make([]byte, length) + if _, err = lr.Read(b); err != nil { + return + } + } + + // Adaptation extension + if lr.N > 0 { + if DebugReader { + // rubish + fmt.Println("ts: skip", lr.N) + } + + if err = ReadDummy(lr, int(lr.N)); err != nil { + return + } + } } } diff --git a/track.go b/track.go new file mode 100644 index 0000000..a83aa4c --- /dev/null +++ b/track.go @@ -0,0 +1,39 @@ + +package ts + +import ( + "bytes" + "github.com/nareix/mp4/isom" +) + +type Track struct { + SPS []byte + PPS []byte + + Type int + + pid uint + PTS int64 + TimeScale int64 + + mpeg4AudioConfig isom.MPEG4AudioConfig + buf bytes.Buffer + payload []byte + peshdr *PESHeader + tshdr TSHeader + spsHasWritten bool + payloadReady bool + + demuxer *Demuxer + mux *Muxer + streamId uint + tsw *TSWriter + dataBuf *iovec + cacheSize int +} + +const ( + H264 = 1 + AAC = 2 +) + From 42fd228cc459d51e2d3184e41e35c51b630895bd Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 3 Apr 2016 20:19:37 +0800 Subject: [PATCH 52/82] add muxer WriteSample() --- demuxer.go | 13 +-- muxer.go | 252 +++++++++++++++++++++++++++++++++++------------------ track.go | 2 +- 3 files changed, 175 insertions(+), 92 deletions(-) diff --git a/demuxer.go b/demuxer.go index b6b18b9..97bb4cc 100644 --- a/demuxer.go +++ b/demuxer.go @@ -138,10 +138,11 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b } if self.Type == AAC { - var n int - if _, data, n, self.payload, err = isom.ReadADTSPayload(self.payload); err != nil { + var n, framelen int + if _, data, n, framelen, err = isom.ReadADTSFrame(self.payload); err != nil { return } + self.payload = self.payload[framelen:] pts = self.PTS dts = pts self.PTS += int64(PTS_HZ*n)/int64(self.mpeg4AudioConfig.SampleRate) @@ -151,14 +152,14 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b } else { dts = int64(self.peshdr.DTS) pts = int64(self.peshdr.PTS) + if dts == 0 { + dts = pts + } isKeyFrame = self.tshdr.RandomAccessIndicator data = self.payload self.payloadReady = false } - if dts == 0 { - dts = pts - } return } @@ -171,7 +172,7 @@ func (self *Track) appendPayload() (err error) { if self.Type == AAC { if !self.mpeg4AudioConfig.IsValid() { - if self.mpeg4AudioConfig, _, _, _, err = isom.ReadADTSPayload(self.payload); err != nil { + if self.mpeg4AudioConfig, _, _, _, err = isom.ReadADTSFrame(self.payload); err != nil { return } self.mpeg4AudioConfig = self.mpeg4AudioConfig.Complete() diff --git a/muxer.go b/muxer.go index f8a634b..b13d4a0 100644 --- a/muxer.go +++ b/muxer.go @@ -4,20 +4,184 @@ package ts import ( "bytes" "io" + "github.com/nareix/mp4/isom" ) +type Muxer struct { + W io.Writer + tswPAT *TSWriter + tswPMT *TSWriter + elemStreams []ElementaryStreamInfo + TrackH264 *Track + Tracks []*Track +} + +func (self *Muxer) newTrack(pid uint, streamId uint) (track *Track) { + track = &Track{ + mux: self, + tsw: &TSWriter{ + PID: pid, + DiscontinuityIndicator: true, + }, + streamId: streamId, + } + track.tsw.EnableVecWriter() + return +} + +func (self *Muxer) AddAACTrack() (track *Track) { + self.elemStreams = append( + self.elemStreams, + ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, + ) + track = self.newTrack(0x101, StreamIdAAC) + track.Type = AAC + track.cacheSize = 3000 + self.Tracks = append(self.Tracks, track) + return +} + +func (self *Muxer) AddH264Track() (track *Track) { + self.elemStreams = append( + self.elemStreams, + ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, + ) + track = self.newTrack(0x100, StreamIdH264) + track.Type = H264 + self.TrackH264 = track + self.Tracks = append(self.Tracks, track) + return +} + +func (self *Muxer) WriteHeader() (err error) { + bufPAT := &bytes.Buffer{} + bufPMT := &bytes.Buffer{} + + pat := PAT{ + Entries: []PATEntry{ + {ProgramNumber: 1, ProgramMapPID: 0x1000}, + }, + } + WritePAT(bufPAT, pat) + pmt := PMT{ + PCRPID: 0x100, + ElementaryStreamInfos: self.elemStreams, + } + WritePMT(bufPMT, pmt) + + tswPMT := &TSWriter{ + PID: 0x1000, + DiscontinuityIndicator: true, + } + tswPAT := &TSWriter{ + PID: 0, + DiscontinuityIndicator: true, + } + if err = tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil { + return + } + if err = tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil { + return + } + + for _, track := range(self.Tracks) { + track.spsHasWritten = false + } + + return +} + +func (self *Track) SetH264PPSAndSPS(pps []byte, sps []byte) { + self.PPS, self.SPS = pps, sps +} + +func (self *Track) SetTimeScale(timeScale int64) { + self.timeScale = timeScale +} + +func (self *Track) TimeScale() int64 { + return self.timeScale +} + +func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) { + self.mpeg4AudioConfig = config +} + +func (self *Track) tsToPesTs(ts int64) uint64 { + return uint64(ts)*PTS_HZ/uint64(self.timeScale) +} + +func (self *Track) tsToPCR(ts int64) uint64 { + return uint64(ts)*PCR_HZ/uint64(self.timeScale)+PCR_HZ +} + +func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { + if self.Type == AAC { + + if !isom.IsADTSFrame(data) { + data = append(data, isom.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data))...) + } + + buf := &bytes.Buffer{} + pes := PESHeader{ + StreamId: self.streamId, + PTS: self.tsToPesTs(pts), + } + WritePESHeader(buf, pes, len(data)) + buf.Write(data) + + self.tsw.RandomAccessIndicator = true + self.tsw.PCR = self.tsToPCR(dts) + if err = self.tsw.WriteTo(self.mux.W, buf.Bytes()); err != nil { + return + } + } else if self.Type == H264 { + + buf := &bytes.Buffer{} + pes := PESHeader{ + StreamId: self.streamId, + PTS: self.tsToPesTs(pts), + } + if dts != pts { + pes.DTS = self.tsToPesTs(dts) + } + WritePESHeader(buf, pes, 0) + + if isKeyFrame { + buf.Write([]byte{0,0,0,1,0x9,0xf0,0,0,0,1}) // AUD + buf.Write(self.SPS) + buf.Write([]byte{0,0,1}) + buf.Write(self.PPS) + buf.Write([]byte{0,0,1}) + buf.Write(data) + } else { + buf.Write([]byte{0,0,0,1,0x9,0xf0,0,0,0,1}) // AUD + buf.Write(data) + } + + self.tsw.RandomAccessIndicator = isKeyFrame + self.tsw.PCR = self.tsToPCR(dts) + if err = self.tsw.WriteTo(self.mux.W, buf.Bytes()); err != nil { + return + } + } + return +} + +/* about to remove */ + func (self *Track) setPCR() { - self.tsw.PCR = uint64(self.PTS)*PCR_HZ/uint64(self.TimeScale) + self.tsw.PCR = uint64(self.PTS)*PCR_HZ/uint64(self.timeScale) } func (self *Track) getPesHeader(dataLength int) (data []byte){ if self.PTS == 0 { - self.PTS = self.TimeScale + self.PTS = self.timeScale } buf := &bytes.Buffer{} pes := PESHeader{ StreamId: self.streamId, - PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), + PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), } WritePESHeader(buf, pes, dataLength) return buf.Bytes() @@ -78,85 +242,3 @@ func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { return } -func newTrack(mux *Muxer, pid uint, streamId uint) (track *Track) { - track = &Track{ - mux: mux, - tsw: &TSWriter{ - PID: pid, - DiscontinuityIndicator: true, - }, - streamId: streamId, - } - track.tsw.EnableVecWriter() - return -} - -type Muxer struct { - W io.Writer - tswPAT *TSWriter - tswPMT *TSWriter - elemStreams []ElementaryStreamInfo - TrackH264 *Track - Tracks []*Track -} - -func (self *Muxer) AddAACTrack() (track *Track) { - self.elemStreams = append( - self.elemStreams, - ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, - ) - track = newTrack(self, 0x101, StreamIdAAC) - track.cacheSize = 3000 - self.Tracks = append(self.Tracks, track) - return -} - -func (self *Muxer) AddH264Track() (track *Track) { - self.elemStreams = append( - self.elemStreams, - ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, - ) - track = newTrack(self, 0x100, StreamIdH264) - self.TrackH264 = track - self.Tracks = append(self.Tracks, track) - return -} - -func (self *Muxer) WriteHeader() (err error) { - bufPAT := &bytes.Buffer{} - bufPMT := &bytes.Buffer{} - - pat := PAT{ - Entries: []PATEntry{ - {ProgramNumber: 1, ProgramMapPID: 0x1000}, - }, - } - WritePAT(bufPAT, pat) - pmt := PMT{ - PCRPID: 0x100, - ElementaryStreamInfos: self.elemStreams, - } - WritePMT(bufPMT, pmt) - - tswPMT := &TSWriter{ - PID: 0x1000, - DiscontinuityIndicator: true, - } - tswPAT := &TSWriter{ - PID: 0, - DiscontinuityIndicator: true, - } - if err = tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil { - return - } - if err = tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil { - return - } - - for _, track := range(self.Tracks) { - track.spsHasWritten = false - } - - return -} - diff --git a/track.go b/track.go index a83aa4c..cb407cf 100644 --- a/track.go +++ b/track.go @@ -14,7 +14,7 @@ type Track struct { pid uint PTS int64 - TimeScale int64 + timeScale int64 mpeg4AudioConfig isom.MPEG4AudioConfig buf bytes.Buffer From 328a59083c67ac557cde16e33826570c1663ec82 Mon Sep 17 00:00:00 2001 From: nareix Date: Mon, 4 Apr 2016 14:45:10 +0800 Subject: [PATCH 53/82] fix WriteSample isom.MakeADTSHeader append bug, use h264parser write h264 packet --- muxer.go | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/muxer.go b/muxer.go index b13d4a0..3e5dd2f 100644 --- a/muxer.go +++ b/muxer.go @@ -3,8 +3,10 @@ package ts import ( "bytes" + "fmt" "io" "github.com/nareix/mp4/isom" + "github.com/nareix/codec/h264parser" ) type Muxer struct { @@ -84,6 +86,7 @@ func (self *Muxer) WriteHeader() (err error) { return } + // about to remove for _, track := range(self.Tracks) { track.spsHasWritten = false } @@ -108,18 +111,29 @@ func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) { } func (self *Track) tsToPesTs(ts int64) uint64 { - return uint64(ts)*PTS_HZ/uint64(self.timeScale) + return uint64(ts)*PTS_HZ/uint64(self.timeScale)+PTS_HZ } func (self *Track) tsToPCR(ts int64) uint64 { return uint64(ts)*PCR_HZ/uint64(self.timeScale)+PCR_HZ } +func (self *Track) tsToTime(ts int64) float64 { + return float64(ts)/float64(self.timeScale) +} + func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { + if false { + fmt.Println("WriteSample", self.Type, self.tsToTime(dts)) + } + if self.Type == AAC { if !isom.IsADTSFrame(data) { - data = append(data, isom.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data))...) + data = append(isom.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data)), data...) + } + if false { + fmt.Printf("WriteSample=%x\n", data[:5]) } buf := &bytes.Buffer{} @@ -147,17 +161,18 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt } WritePESHeader(buf, pes, 0) - if isKeyFrame { - buf.Write([]byte{0,0,0,1,0x9,0xf0,0,0,0,1}) // AUD - buf.Write(self.SPS) - buf.Write([]byte{0,0,1}) - buf.Write(self.PPS) - buf.Write([]byte{0,0,1}) - buf.Write(data) + var nalus [][]byte + if ok, _nalus := h264parser.SplitNALUs(data); ok { + nalus = _nalus } else { - buf.Write([]byte{0,0,0,1,0x9,0xf0,0,0,0,1}) // AUD - buf.Write(data) + nalus = [][]byte{data} } + if isKeyFrame { + nalus = append([][]byte{self.SPS, self.PPS}, nalus...) + } + h264parser.WalkNALUsAnnexb(nalus, func(b []byte) { + buf.Write(b) + }) self.tsw.RandomAccessIndicator = isKeyFrame self.tsw.PCR = self.tsToPCR(dts) From 7e695b3d90b2209d3b12eb3729d714306204d94e Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 19 Apr 2016 11:59:49 +0800 Subject: [PATCH 54/82] don't split ADTS continuous aac frames in to single ones --- demuxer.go | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/demuxer.go b/demuxer.go index 97bb4cc..03b05ff 100644 --- a/demuxer.go +++ b/demuxer.go @@ -137,38 +137,20 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b } } - if self.Type == AAC { - var n, framelen int - if _, data, n, framelen, err = isom.ReadADTSFrame(self.payload); err != nil { - return - } - self.payload = self.payload[framelen:] - pts = self.PTS + dts = int64(self.peshdr.DTS) + pts = int64(self.peshdr.PTS) + if dts == 0 { dts = pts - self.PTS += int64(PTS_HZ*n)/int64(self.mpeg4AudioConfig.SampleRate) - if len(self.payload) == 0 { - self.payloadReady = false - } - } else { - dts = int64(self.peshdr.DTS) - pts = int64(self.peshdr.PTS) - if dts == 0 { - dts = pts - } - isKeyFrame = self.tshdr.RandomAccessIndicator - data = self.payload - self.payloadReady = false } + isKeyFrame = self.tshdr.RandomAccessIndicator + data = self.payload + self.payloadReady = false return } func (self *Track) appendPayload() (err error) { self.payload = self.buf.Bytes() - if len(self.payload) == 0 { - err = fmt.Errorf("empty payload") - return - } if self.Type == AAC { if !self.mpeg4AudioConfig.IsValid() { @@ -181,7 +163,6 @@ func (self *Track) appendPayload() (err error) { return } } - self.PTS = int64(self.peshdr.PTS) } self.payloadReady = true From a5ec0acea084dc7a885f49b5b5b1fbc11bb48c72 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 19 Apr 2016 12:00:39 +0800 Subject: [PATCH 55/82] use new SplitNALUs api --- muxer.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/muxer.go b/muxer.go index 3e5dd2f..94b4641 100644 --- a/muxer.go +++ b/muxer.go @@ -161,12 +161,7 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt } WritePESHeader(buf, pes, 0) - var nalus [][]byte - if ok, _nalus := h264parser.SplitNALUs(data); ok { - nalus = _nalus - } else { - nalus = [][]byte{data} - } + nalus, _ := h264parser.SplitNALUs(data) if isKeyFrame { nalus = append([][]byte{self.SPS, self.PPS}, nalus...) } @@ -180,6 +175,7 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt return } } + return } From dfd71d04bf4fe539eead88dbab7c76a8c2ac4d03 Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 19 Apr 2016 21:33:07 +0800 Subject: [PATCH 56/82] rename isom to aacparser --- checksum.go | 14 +++---- checksum_test.go | 4 +- demuxer.go | 28 +++++++------- example/test.go | 70 +++++++++++++++++------------------ muxer.go | 46 +++++++++++------------ reader.go | 95 +++++++++++++++++++++++------------------------- track.go | 34 ++++++++--------- ts.go | 66 ++++++++++++++++----------------- ts_test.go | 2 - vecio.go | 22 +++++------ writer.go | 94 +++++++++++++++++++++++------------------------ writer_test.go | 10 ++--- 12 files changed, 230 insertions(+), 255 deletions(-) diff --git a/checksum.go b/checksum.go index 61fe192..c5ac537 100644 --- a/checksum.go +++ b/checksum.go @@ -1,13 +1,12 @@ - package ts import ( - "io" - "fmt" "encoding/hex" + "fmt" + "io" ) -var ieeeCrc32Tbl = []uint32 { +var ieeeCrc32Tbl = []uint32{ 0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517, 0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B, 0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048, @@ -55,13 +54,13 @@ var ieeeCrc32Tbl = []uint32 { func updateIeeeCrc32(crc uint32, data []byte) uint32 { for _, b := range data { - crc = ieeeCrc32Tbl[b^byte(crc)]^(crc>>8) + crc = ieeeCrc32Tbl[b^byte(crc)] ^ (crc >> 8) } return crc } type Crc32Reader struct { - R io.Reader + R io.Reader Crc32 uint32 } @@ -90,7 +89,7 @@ func (self *Crc32Reader) ReadCrc32UIntAndCheck() (err error) { } type Crc32Writer struct { - W io.Writer + W io.Writer Crc32 uint32 } @@ -101,4 +100,3 @@ func (self *Crc32Writer) Write(b []byte) (n int, err error) { self.Crc32 = updateIeeeCrc32(self.Crc32, b) return } - diff --git a/checksum_test.go b/checksum_test.go index ecadf32..fc4316e 100644 --- a/checksum_test.go +++ b/checksum_test.go @@ -1,4 +1,3 @@ - package ts import ( @@ -7,8 +6,7 @@ import ( func TestChecksum(t *testing.T) { b := []byte("hello world") - b = append(b, []byte{0xbb,0x08,0xec,0x87}...) + b = append(b, []byte{0xbb, 0x08, 0xec, 0x87}...) crc := updateIeeeCrc32(0xffffffff, b) t.Logf("%x", crc) } - diff --git a/demuxer.go b/demuxer.go index 03b05ff..1af05a3 100644 --- a/demuxer.go +++ b/demuxer.go @@ -1,21 +1,20 @@ - package ts import ( - "io" "bytes" "fmt" - "github.com/nareix/mp4/isom" + "github.com/nareix/codec/aacparser" + "io" ) type Demuxer struct { R io.Reader - pat PAT - pmt *PMT - Tracks []*Track + pat PAT + pmt *PMT + Tracks []*Track TrackH264 *Track - TrackAAC *Track + TrackAAC *Track } // ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) @@ -33,7 +32,7 @@ func (self *Demuxer) ReadHeader() (err error) { for { if self.pmt != nil { n := 0 - for _, track := range(self.Tracks) { + for _, track := range self.Tracks { if track.payloadReady { n++ } @@ -58,7 +57,7 @@ func (self *Demuxer) ReadSample() (track *Track, err error) { } for { - for _, _track := range(self.Tracks) { + for _, _track := range self.Tracks { if _track.payloadReady { track = _track return @@ -87,13 +86,13 @@ func (self *Demuxer) readPacket() (err error) { } } else { if self.pmt == nil { - for _, entry := range(self.pat.Entries) { + for _, entry := range self.pat.Entries { if entry.ProgramMapPID == header.PID { self.pmt = new(PMT) if *self.pmt, err = ReadPMT(bytes.NewReader(payload)); err != nil { return } - for _, info := range(self.pmt.ElementaryStreamInfos) { + for _, info := range self.pmt.ElementaryStreamInfos { track := &Track{} track.demuxer = self @@ -113,7 +112,7 @@ func (self *Demuxer) readPacket() (err error) { } } else { - for _, track := range(self.Tracks) { + for _, track := range self.Tracks { if header.PID == track.pid { if err = track.appendPacket(header, payload); err != nil { return @@ -126,7 +125,7 @@ func (self *Demuxer) readPacket() (err error) { return } -func (self *Track) GetMPEG4AudioConfig() isom.MPEG4AudioConfig { +func (self *Track) GetMPEG4AudioConfig() aacparser.MPEG4AudioConfig { return self.mpeg4AudioConfig } @@ -154,7 +153,7 @@ func (self *Track) appendPayload() (err error) { if self.Type == AAC { if !self.mpeg4AudioConfig.IsValid() { - if self.mpeg4AudioConfig, _, _, _, err = isom.ReadADTSFrame(self.payload); err != nil { + if self.mpeg4AudioConfig, _, _, _, err = aacparser.ReadADTSFrame(self.payload); err != nil { return } self.mpeg4AudioConfig = self.mpeg4AudioConfig.Complete() @@ -200,4 +199,3 @@ func (self *Track) appendPacket(header TSHeader, payload []byte) (err error) { return } - diff --git a/example/test.go b/example/test.go index d86cdaa..5156ac5 100644 --- a/example/test.go +++ b/example/test.go @@ -1,52 +1,51 @@ - package main import ( - "bytes" - "os" - "io" ts "../" - "fmt" - "encoding/hex" + "bytes" "encoding/gob" - "runtime/pprof" + "encoding/hex" "flag" + "fmt" + "io" + "os" + "runtime/pprof" ) type GobAllSamples struct { TimeScale int - SPS []byte - PPS []byte - Samples []GobSample + SPS []byte + PPS []byte + Samples []GobSample } type GobSample struct { Duration int - Data []byte - Sync bool + Data []byte + Sync bool } type Stream struct { - PID uint - PESHeader *ts.PESHeader + PID uint + PESHeader *ts.PESHeader FirstTSHeader ts.TSHeader - Title string - Data bytes.Buffer - Type uint - PCR uint64 + Title string + Data bytes.Buffer + Type uint + PCR uint64 } type Sample struct { - Type uint - PCR uint64 - PTS uint64 - DTS uint64 - Data []byte + Type uint + PCR uint64 + PTS uint64 + DTS uint64 + Data []byte RandomAccessIndicator bool } var ( - debugData = true + debugData = true debugStream = true ) @@ -75,7 +74,7 @@ func readSamples(filename string, ch chan Sample) { stream, _ = streams[pid] if stream == nil { stream = &Stream{ - PID: pid, + PID: pid, Type: info.StreamType, } if stream.Type == ts.ElementaryStreamTypeH264 { @@ -99,9 +98,9 @@ func readSamples(filename string, ch chan Sample) { ch <- Sample{ Type: stream.Type, Data: stream.Data.Bytes(), - PTS: stream.PESHeader.PTS, - DTS: stream.PESHeader.DTS, - PCR: stream.FirstTSHeader.PCR, + PTS: stream.PESHeader.PTS, + DTS: stream.PESHeader.DTS, + PCR: stream.FirstTSHeader.PCR, RandomAccessIndicator: stream.FirstTSHeader.RandomAccessIndicator, } } @@ -157,7 +156,7 @@ func readSamples(filename string, ch chan Sample) { } } - for _, entry := range(pat.Entries) { + for _, entry := range pat.Entries { if entry.ProgramMapPID == header.PID { //fmt.Println("matchs", entry) if pmt, err = ts.ReadPMT(pr); err != nil { @@ -167,7 +166,7 @@ func readSamples(filename string, ch chan Sample) { } } - for _, info = range(pmt.ElementaryStreamInfos) { + for _, info = range pmt.ElementaryStreamInfos { if info.ElementaryPID == header.PID { onStreamPayload() } @@ -231,7 +230,7 @@ func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) if sample.Sync { syncCount++ if testSeg { - if syncCount % 3 == 0 { + if syncCount%3 == 0 { filename := fmt.Sprintf("%s.seg%d.ts", pathOut, segCount) if debugStream { @@ -241,7 +240,7 @@ func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) if m3u8file != nil { info, _ := outfile.Stat() size := info.Size() - dur := float64(trackH264.PTS - lastPTS) / float64(allSamples.TimeScale) + dur := float64(trackH264.PTS-lastPTS) / float64(allSamples.TimeScale) writeM3U8Item(m3u8file, lastFilename, size, dur) } @@ -353,8 +352,8 @@ func main() { pes := ts.PESHeader{ StreamId: streamId, - PTS: sample.PTS, - DTS: sample.DTS, + PTS: sample.PTS, + DTS: sample.DTS, } w.PID = pid w.PCR = sample.PCR @@ -369,7 +368,7 @@ func main() { writePAT() writePMT() w = &ts.TSWriter{ - W: file, + W: file, PID: 0x100, } w.EnableVecWriter() @@ -395,4 +394,3 @@ func main() { } } - diff --git a/muxer.go b/muxer.go index 94b4641..2503933 100644 --- a/muxer.go +++ b/muxer.go @@ -1,21 +1,20 @@ - package ts import ( "bytes" "fmt" - "io" - "github.com/nareix/mp4/isom" "github.com/nareix/codec/h264parser" + "github.com/nareix/codec/aacparser" + "io" ) type Muxer struct { - W io.Writer - tswPAT *TSWriter - tswPMT *TSWriter + W io.Writer + tswPAT *TSWriter + tswPMT *TSWriter elemStreams []ElementaryStreamInfo - TrackH264 *Track - Tracks []*Track + TrackH264 *Track + Tracks []*Track } func (self *Muxer) newTrack(pid uint, streamId uint) (track *Track) { @@ -66,7 +65,7 @@ func (self *Muxer) WriteHeader() (err error) { } WritePAT(bufPAT, pat) pmt := PMT{ - PCRPID: 0x100, + PCRPID: 0x100, ElementaryStreamInfos: self.elemStreams, } WritePMT(bufPMT, pmt) @@ -87,7 +86,7 @@ func (self *Muxer) WriteHeader() (err error) { } // about to remove - for _, track := range(self.Tracks) { + for _, track := range self.Tracks { track.spsHasWritten = false } @@ -106,20 +105,20 @@ func (self *Track) TimeScale() int64 { return self.timeScale } -func (self *Track) SetMPEG4AudioConfig(config isom.MPEG4AudioConfig) { +func (self *Track) SetMPEG4AudioConfig(config aacparser.MPEG4AudioConfig) { self.mpeg4AudioConfig = config } func (self *Track) tsToPesTs(ts int64) uint64 { - return uint64(ts)*PTS_HZ/uint64(self.timeScale)+PTS_HZ + return uint64(ts)*PTS_HZ/uint64(self.timeScale) + PTS_HZ } func (self *Track) tsToPCR(ts int64) uint64 { - return uint64(ts)*PCR_HZ/uint64(self.timeScale)+PCR_HZ + return uint64(ts)*PCR_HZ/uint64(self.timeScale) + PCR_HZ } func (self *Track) tsToTime(ts int64) float64 { - return float64(ts)/float64(self.timeScale) + return float64(ts) / float64(self.timeScale) } func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { @@ -129,8 +128,8 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt if self.Type == AAC { - if !isom.IsADTSFrame(data) { - data = append(isom.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data)), data...) + if !aacparser.IsADTSFrame(data) { + data = append(aacparser.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data)), data...) } if false { fmt.Printf("WriteSample=%x\n", data[:5]) @@ -139,7 +138,7 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt buf := &bytes.Buffer{} pes := PESHeader{ StreamId: self.streamId, - PTS: self.tsToPesTs(pts), + PTS: self.tsToPesTs(pts), } WritePESHeader(buf, pes, len(data)) buf.Write(data) @@ -154,7 +153,7 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt buf := &bytes.Buffer{} pes := PESHeader{ StreamId: self.streamId, - PTS: self.tsToPesTs(pts), + PTS: self.tsToPesTs(pts), } if dts != pts { pes.DTS = self.tsToPesTs(dts) @@ -182,17 +181,17 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt /* about to remove */ func (self *Track) setPCR() { - self.tsw.PCR = uint64(self.PTS)*PCR_HZ/uint64(self.timeScale) + self.tsw.PCR = uint64(self.PTS) * PCR_HZ / uint64(self.timeScale) } -func (self *Track) getPesHeader(dataLength int) (data []byte){ +func (self *Track) getPesHeader(dataLength int) (data []byte) { if self.PTS == 0 { self.PTS = self.timeScale } buf := &bytes.Buffer{} pes := PESHeader{ StreamId: self.streamId, - PTS: uint64(self.PTS)*PTS_HZ/uint64(self.timeScale), + PTS: uint64(self.PTS) * PTS_HZ / uint64(self.timeScale), } WritePESHeader(buf, pes, dataLength) return buf.Bytes() @@ -216,9 +215,9 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro for i, nalu := range nalus { var startCode []byte if i == 0 { - startCode = []byte{0,0,0,1,0x9,0xf0,0,0,0,1} // AUD + startCode = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD } else { - startCode = []byte{0,0,1} + startCode = []byte{0, 0, 1} } data.Append(startCode) data.Append(nalu) @@ -252,4 +251,3 @@ func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { self.incPTS(duration) return } - diff --git a/reader.go b/reader.go index dfd01a6..a4d36c9 100644 --- a/reader.go +++ b/reader.go @@ -1,4 +1,3 @@ - package ts import ( @@ -32,7 +31,7 @@ func ReadUInt64(r io.Reader, n int) (res uint64, err error) { if res32, err = ReadUInt(r, n-4); err != nil { return } - res |= uint64(res32)<<32 + res |= uint64(res32) << 32 n = 4 } if res32, err = ReadUInt(r, n); err != nil { @@ -59,9 +58,9 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { if DebugReader { fmt.Printf("ts: flags %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {8, "sync"}, {1, "transport_error_indicator"}, @@ -73,17 +72,17 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { {1, "payload_flag"}, {4, "continuity_counter"}, }, - Val: flags, + Val: flags, Length: 32, }) } - if flags & 0x400000 != 0 { + if flags&0x400000 != 0 { // When set to '1' it indicates that this TS packet contains the first PES packet. self.PayloadUnitStart = true } - if (flags & 0xff000000) >> 24 != 0x47 { + if (flags&0xff000000)>>24 != 0x47 { err = fmt.Errorf("invalid sync") return } @@ -91,7 +90,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { self.PID = (flags & 0x1fff00) >> 8 self.ContinuityCounter = flags & 0xf - if flags & 0x20 != 0 { + if flags&0x20 != 0 { var flags, length uint if length, err = ReadUInt(r, 1); err != nil { return @@ -104,9 +103,9 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { if DebugReader { fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {1, "discontinuity_indicator"}, {1, "random_access_indicator"}, @@ -117,18 +116,18 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { {1, "transport_private_data_flag"}, {1, "adaptation_field_extension_flag"}, }, - Val: flags, + Val: flags, Length: 8, }) } // random_access_indicator - if flags & 0x40 != 0 { + if flags&0x40 != 0 { self.RandomAccessIndicator = true } // PCR - if flags & 0x10 != 0 { + if flags&0x10 != 0 { var v uint64 if v, err = ReadUInt64(lr, 6); err != nil { return @@ -141,7 +140,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } // OPCR - if flags & 0x08 != 0 { + if flags&0x08 != 0 { var v uint64 if v, err = ReadUInt64(lr, 6); err != nil { return @@ -150,14 +149,14 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } // Splice countdown - if flags & 0x04 != 0 { + if flags&0x04 != 0 { if _, err = ReadUInt(lr, 1); err != nil { return } } // Transport private data - if flags & 0x02 != 0 { + if flags&0x02 != 0 { var length uint if length, err = ReadUInt(lr, 1); err != nil { return @@ -212,7 +211,6 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err fmt.Printf("psi: pointer=%d\n", pointer) } - if pointer != 0 { if err = ReadDummy(r, int(pointer)); err != nil { return @@ -253,15 +251,15 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err if DebugReader { fmt.Printf("psi: %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {2, "resverd"}, {5, "version"}, {1, "current_next_indicator"}, }, - Val: flags, + Val: flags, Length: 8, }) } @@ -328,14 +326,14 @@ func ReadPMT(r io.Reader) (self PMT, err error) { if DebugReader { fmt.Printf("pmt: %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {3, "reserved"}, {13, "pcrpid"}, }, - Val: flags, + Val: flags, Length: 16, }) } @@ -374,14 +372,14 @@ func ReadPMT(r io.Reader) (self PMT, err error) { if DebugReader { fmt.Printf("pmt: info1 %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {3, "reserved"}, {13, "elementary_pid"}, }, - Val: flags, + Val: flags, Length: 16, }) } @@ -395,14 +393,14 @@ func ReadPMT(r io.Reader) (self PMT, err error) { if DebugReader { fmt.Printf("pmt: info2 %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {6, "reserved"}, {10, "es_info_length"}, }, - Val: flags, + Val: flags, Length: 16, }) } @@ -500,7 +498,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } if length == 0 { - length = 1<<31 + length = 1 << 31 } lrAll := &io.LimitedReader{R: r, N: int64(length)} lr := lrAll @@ -517,9 +515,9 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { if DebugReader { fmt.Printf("pes: %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {2, "scrambling_control"}, {1, "priority"}, @@ -527,7 +525,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { {1, "copyright"}, {1, "original_or_copy"}, }, - Val: flags, + Val: flags, Length: 6, }) } @@ -545,9 +543,9 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { if DebugReader { fmt.Printf("pes: %s\n", FieldsDumper{ - Fields: []struct{ + Fields: []struct { Length int - Desc string + Desc string }{ {2, "pts_dts_flags"}, {1, "escr_flag"}, @@ -557,7 +555,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { {1, "pes_crc_flag"}, {1, "pes_extension_flag"}, }, - Val: flags, + Val: flags, Length: 8, }) } @@ -568,7 +566,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } lr = &io.LimitedReader{R: lr, N: int64(length)} - if flags & 0x80 != 0 { + if flags&0x80 != 0 { var v uint64 if v, err = ReadUInt64(lr, 5); err != nil { return @@ -580,7 +578,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } } - if flags & 0x40 != 0 && flags & 0x80 != 0 { + if flags&0x40 != 0 && flags&0x80 != 0 { var v uint64 if v, err = ReadUInt64(lr, 5); err != nil { return @@ -592,35 +590,35 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } // ESCR flag - if flags & 0x20 != 0 { + if flags&0x20 != 0 { if _, err = ReadUInt64(lr, 6); err != nil { return } } // ES rate flag - if flags & 0x10 != 0 { + if flags&0x10 != 0 { if _, err = ReadUInt64(lr, 3); err != nil { return } } // additional copy info flag - if flags & 0x04 != 0 { + if flags&0x04 != 0 { if _, err = ReadUInt(lr, 1); err != nil { return } } // PES CRC flag - if flags & 0x02 != 0 { + if flags&0x02 != 0 { if _, err = ReadUInt(lr, 2); err != nil { return } } // PES extension flag - if flags & 0x01 != 0 { + if flags&0x01 != 0 { var flags uint // PES private data flag(1) @@ -634,7 +632,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } // PES private data flag(1) - if flags & 0x80 != 0 { + if flags&0x80 != 0 { // if set to 1 16 bytes of user defined data is appended to the header data field if err = ReadDummy(lr, 16); err != nil { return @@ -642,7 +640,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } // pack header field flag(1) - if flags & 0x40 != 0 { + if flags&0x40 != 0 { // if set to 1 the 8-bit pack field length value is appended to the header data field if err = ReadDummy(lr, 1); err != nil { return @@ -650,21 +648,21 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } // program packet sequence counter flag(1) - if flags & 0x20 != 0 { + if flags&0x20 != 0 { if err = ReadDummy(lr, 2); err != nil { return } } // P-STD buffer flag(1) - if flags & 0x10 != 0 { + if flags&0x10 != 0 { if err = ReadDummy(lr, 2); err != nil { return } } // PES extension flag 2(1) - if flags & 0x01 != 0 { + if flags&0x01 != 0 { if err = ReadDummy(lr, 2); err != nil { return } @@ -684,4 +682,3 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { res = self return } - diff --git a/track.go b/track.go index cb407cf..23e34d8 100644 --- a/track.go +++ b/track.go @@ -1,9 +1,8 @@ - package ts import ( "bytes" - "github.com/nareix/mp4/isom" + "github.com/nareix/codec/aacparser" ) type Track struct { @@ -12,28 +11,27 @@ type Track struct { Type int - pid uint - PTS int64 + pid uint + PTS int64 timeScale int64 - mpeg4AudioConfig isom.MPEG4AudioConfig - buf bytes.Buffer - payload []byte - peshdr *PESHeader - tshdr TSHeader - spsHasWritten bool - payloadReady bool + mpeg4AudioConfig aacparser.MPEG4AudioConfig + buf bytes.Buffer + payload []byte + peshdr *PESHeader + tshdr TSHeader + spsHasWritten bool + payloadReady bool - demuxer *Demuxer - mux *Muxer - streamId uint - tsw *TSWriter - dataBuf *iovec + demuxer *Demuxer + mux *Muxer + streamId uint + tsw *TSWriter + dataBuf *iovec cacheSize int } const ( H264 = 1 - AAC = 2 + AAC = 2 ) - diff --git a/ts.go b/ts.go index 3c9ec2d..1019f14 100644 --- a/ts.go +++ b/ts.go @@ -1,4 +1,3 @@ - package ts import ( @@ -6,24 +5,24 @@ import ( ) const ( - ElementaryStreamTypeH264 = 0x1B + ElementaryStreamTypeH264 = 0x1B ElementaryStreamTypeAdtsAAC = 0x0F ) type TSHeader struct { - PID uint - PCR uint64 - OPCR uint64 - ContinuityCounter uint - PayloadUnitStart bool + PID uint + PCR uint64 + OPCR uint64 + ContinuityCounter uint + PayloadUnitStart bool DiscontinuityIndicator bool - RandomAccessIndicator bool - HeaderLength uint + RandomAccessIndicator bool + HeaderLength uint } type PATEntry struct { ProgramNumber uint - NetworkPID uint + NetworkPID uint ProgramMapPID uint } @@ -32,45 +31,45 @@ type PAT struct { } type PMT struct { - PCRPID uint - ProgramDescriptors []Descriptor + PCRPID uint + ProgramDescriptors []Descriptor ElementaryStreamInfos []ElementaryStreamInfo } type Descriptor struct { - Tag uint + Tag uint Data []byte } type ElementaryStreamInfo struct { - StreamType uint + StreamType uint ElementaryPID uint - Descriptors []Descriptor + Descriptors []Descriptor } type PSI struct { TableIdExtension uint - TableId uint - SecNum uint - LastSecNum uint + TableId uint + SecNum uint + LastSecNum uint } const ( StreamIdH264 = 0xe0 - StreamIdAAC = 0xc0 + StreamIdAAC = 0xc0 ) type PESHeader struct { - StreamId uint // H264=0xe0 AAC=0xc0 + StreamId uint // H264=0xe0 AAC=0xc0 DataLength uint - PTS uint64 - DTS uint64 - ESCR uint64 + PTS uint64 + DTS uint64 + ESCR uint64 } func PESUIntToTs(v uint64) (ts uint64) { // 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1 - return (((v>>33)&0x7)<<30) | (((v>>17)&0x7fff)<<15) | ((v>>1)&0x7fff) + return (((v >> 33) & 0x7) << 30) | (((v >> 17) & 0x7fff) << 15) | ((v >> 1) & 0x7fff) } func PESTsToUInt(ts uint64) (v uint64) { @@ -85,23 +84,23 @@ const ( func UIntToPCR(v uint64) uint64 { // base(33)+resverd(6)+ext(9) - base := v>>15 - ext := v&0x1ff - return base*300+ext + base := v >> 15 + ext := v & 0x1ff + return base*300 + ext } func PCRToUInt(pcr uint64) uint64 { - base := pcr/300 - ext := pcr%300 - return base<<15|0x3f<<9|ext + base := pcr / 300 + ext := pcr % 300 + return base<<15 | 0x3f<<9 | ext } type FieldsDumper struct { Fields []struct { Length int - Desc string + Desc string } - Val uint + Val uint Length uint } @@ -109,11 +108,10 @@ func (self FieldsDumper) String() (res string) { pos := uint(self.Length) for _, field := range self.Fields { pos -= uint(field.Length) - val := (self.Val>>pos)&(1<> pos) & (1<= 0; i-- { + for i := n - 1; i >= 0; i-- { b[i] = byte(val) val >>= 8 } @@ -51,13 +50,13 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err // Payload flag(1) 0x10 // Continuity counter(4) - flags = 0x47<<24 + flags = 0x47 << 24 flags |= 0x10 if self.PayloadUnitStart { flags |= 0x400000 } - flags |= (self.PID&0x1fff)<<8 - flags |= self.ContinuityCounter&0xf + flags |= (self.PID & 0x1fff) << 8 + flags |= self.ContinuityCounter & 0xf if DebugWriter { fmt.Printf("tsw: pid=%x\n", self.PID) @@ -94,26 +93,26 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err } written += 4 - if flags & EXT != 0 { + if flags&EXT != 0 { var length uint - // Discontinuity indicator 1 0x80 - // Random Access indicator 1 0x40 - // Elementary stream priority indicator 1 0x20 + // Discontinuity indicator 1 0x80 + // Random Access indicator 1 0x40 + // Elementary stream priority indicator 1 0x20 // PCR flag 1 0x10 - // OPCR flag 1 0x08 + // OPCR flag 1 0x08 length = 1 // extFlags - if extFlags & PCR != 0 { + if extFlags&PCR != 0 { length += 6 } - if extFlags & OPCR != 0 { + if extFlags&OPCR != 0 { length += 6 } paddingLength := 0 // need padding - if int(length) + 5 + dataLength < 188 { + if int(length)+5+dataLength < 188 { paddingLength = 188 - dataLength - 5 - int(length) length = 188 - uint(dataLength) - 5 } @@ -129,13 +128,13 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err return } - if extFlags & PCR != 0 { + if extFlags&PCR != 0 { if err = WriteUInt64(w, PCRToUInt(self.PCR), 6); err != nil { return } } - if extFlags & OPCR != 0 { + if extFlags&OPCR != 0 { if err = WriteUInt64(w, PCRToUInt(self.OPCR), 6); err != nil { return } @@ -147,17 +146,17 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err } } - written += int(length)+1 + written += int(length) + 1 } return } type TSWriter struct { - W io.Writer + W io.Writer PID uint TSHeader - DisableHeaderPadding bool + DisableHeaderPadding bool DiscontinuityIndicator bool vecw *vecWriter @@ -192,8 +191,8 @@ func (self *TSWriter) WriteIovec(data *iovec) (err error) { func (self *TSWriter) WriteIovecTo(w io.Writer, data *iovec) (err error) { for i := 0; data.Len > 0; i++ { header := TSHeader{ - PID: self.PID, - ContinuityCounter: self.ContinuityCounter, + PID: self.PID, + ContinuityCounter: self.ContinuityCounter, DiscontinuityIndicator: self.DiscontinuityIndicator, } @@ -214,7 +213,7 @@ func (self *TSWriter) WriteIovecTo(w io.Writer, data *iovec) (err error) { } payloadLength := 188 - headerLength if self.DisableHeaderPadding && data.Len < payloadLength { - data.Append(makeRepeatValBytes(0xff, payloadLength - data.Len)) + data.Append(makeRepeatValBytes(0xff, payloadLength-data.Len)) } if DebugWriter { @@ -267,8 +266,8 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { // section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10) var flags, length uint - length = 2+3+4+uint(len(data)) - flags = 0xa<<12|length + length = 2 + 3 + 4 + uint(len(data)) + flags = 0xa<<12 | length if err = WriteUInt(cw, flags, 2); err != nil { return } @@ -283,7 +282,7 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { } // resverd(2)=3,version(5)=0,Current_next_indicator(1)=1 - flags = 0x3<<6|1 + flags = 0x3<<6 | 1 if err = WriteUInt(cw, flags, 1); err != nil { return } @@ -312,7 +311,7 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { } func bswap32(v uint) uint { - return (v>>24)|((v>>16)&0xff)<<8|((v>>8)&0xff)<<16|(v&0xff)<<24 + return (v >> 24) | ((v>>16)&0xff)<<8 | ((v>>8)&0xff)<<16 | (v&0xff)<<24 } func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { @@ -340,8 +339,8 @@ func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { return } - const PTS = 1<<7 - const DTS = 1<<6 + const PTS = 1 << 7 + const DTS = 1 << 6 if self.PTS != 0 { pts_dts_flags |= PTS @@ -350,10 +349,10 @@ func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { } } - if pts_dts_flags & PTS != 0 { + if pts_dts_flags&PTS != 0 { header_length += 5 } - if pts_dts_flags & DTS != 0 { + if pts_dts_flags&DTS != 0 { header_length += 5 } @@ -361,8 +360,8 @@ func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { packet_length = uint(dataLength) + header_length + 3 } // packet_length(16) if zero then variable length - // Specifies the number of bytes remaining in the packet after this field. Can be zero. - // If the PES packet length is set to zero, the PES packet can be of any length. + // Specifies the number of bytes remaining in the packet after this field. Can be zero. + // If the PES packet length is set to zero, the PES packet can be of any length. // A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream. if err = WriteUInt(w, packet_length, 2); err != nil { return @@ -385,8 +384,8 @@ func WritePESHeader(w io.Writer, self PESHeader, dataLength int) (err error) { // pts(40)? // dts(40)? - if pts_dts_flags & PTS != 0 { - if pts_dts_flags & DTS != 0 { + if pts_dts_flags&PTS != 0 { + if pts_dts_flags&DTS != 0 { if err = WriteUInt64(w, PESTsToUInt(self.PTS)|3<<36, 5); err != nil { return } @@ -447,8 +446,8 @@ func WritePAT(w io.Writer, self PAT) (err error) { func WritePATPacket(w io.Writer, pat PAT) (err error) { tsw := &TSWriter{ - W: w, - PID: 0, + W: w, + PID: 0, DisableHeaderPadding: true, } bw := &bytes.Buffer{} @@ -529,8 +528,8 @@ func WritePMT(w io.Writer, self PMT) (err error) { return } - psi := PSI { - TableId: 2, + psi := PSI{ + TableId: 2, TableIdExtension: 1, } if err = WritePSI(w, psi, bw.Bytes()); err != nil { @@ -542,8 +541,8 @@ func WritePMT(w io.Writer, self PMT) (err error) { func WritePMTPacket(w io.Writer, pmt PMT, pid uint) (err error) { tsw := &TSWriter{ - W: w, - PID: pid, + W: w, + PID: pid, DisableHeaderPadding: true, } bw := &bytes.Buffer{} @@ -557,14 +556,14 @@ func WritePMTPacket(w io.Writer, pmt PMT, pid uint) (err error) { } type SimpleH264Writer struct { - W io.Writer + W io.Writer TimeScale int SPS []byte PPS []byte - tswPAT *TSWriter - tswPMT *TSWriter + tswPAT *TSWriter + tswPMT *TSWriter tswH264 *TSWriter PTS int64 @@ -668,7 +667,7 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e pes := PESHeader{ StreamId: StreamIdH264, - PTS: uint64(self.PTS)*PTS_HZ/uint64(self.TimeScale), + PTS: uint64(self.PTS) * PTS_HZ / uint64(self.TimeScale), } if err = WritePESHeader(self.pesBuf, pes, 0); err != nil { return @@ -679,16 +678,16 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e for i, nalu := range nalus { var startCode []byte if i == 0 { - startCode = []byte{0,0,0,1,0x9,0xf0,0,0,0,1} // AUD + startCode = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD } else { - startCode = []byte{0,0,1} + startCode = []byte{0, 0, 1} } data.Append(startCode) data.Append(nalu) } self.tswH264.RandomAccessIndicator = sync - self.tswH264.PCR = uint64(self.PCR)*PCR_HZ/uint64(self.TimeScale) + self.tswH264.PCR = uint64(self.PCR) * PCR_HZ / uint64(self.TimeScale) self.tswH264.W = self.W if err = self.tswH264.WriteIovec(data); err != nil { @@ -701,4 +700,3 @@ func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (e return } - diff --git a/writer_test.go b/writer_test.go index 3439b87..5450006 100644 --- a/writer_test.go +++ b/writer_test.go @@ -1,19 +1,17 @@ - package ts import ( - "testing" - "encoding/hex" "bytes" + "encoding/hex" + "testing" ) func TestWriteTSHeader(t *testing.T) { bw := &bytes.Buffer{} w := &TSWriter{ - W: bw, + W: bw, PCR: 0x12345678, } - w.Write([]byte{'h','e','l','o'}[:], false) + w.Write([]byte{'h', 'e', 'l', 'o'}[:], false) t.Logf("\n%s", hex.Dump(bw.Bytes())) } - From a4e262dd224d0f18197a7804db7e86692c4dfd71 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 19:21:28 +0800 Subject: [PATCH 57/82] add stream.go and rewrite track to stream --- demuxer.go | 32 ++++++++++++++++---------------- muxer.go | 32 ++++++++++++++++---------------- track.go => stream.go | 0 3 files changed, 32 insertions(+), 32 deletions(-) rename track.go => stream.go (100%) diff --git a/demuxer.go b/demuxer.go index 1af05a3..dd89a0b 100644 --- a/demuxer.go +++ b/demuxer.go @@ -32,8 +32,8 @@ func (self *Demuxer) ReadHeader() (err error) { for { if self.pmt != nil { n := 0 - for _, track := range self.Tracks { - if track.payloadReady { + for _, stream := range self.Tracks { + if stream.payloadReady { n++ } } @@ -50,7 +50,7 @@ func (self *Demuxer) ReadHeader() (err error) { return } -func (self *Demuxer) ReadSample() (track *Track, err error) { +func (self *Demuxer) ReadSample() (stream *Track, err error) { if len(self.Tracks) == 0 { err = fmt.Errorf("no track") return @@ -59,7 +59,7 @@ func (self *Demuxer) ReadSample() (track *Track, err error) { for { for _, _track := range self.Tracks { if _track.payloadReady { - track = _track + stream = _track return } } @@ -93,28 +93,28 @@ func (self *Demuxer) readPacket() (err error) { return } for _, info := range self.pmt.ElementaryStreamInfos { - track := &Track{} + stream := &Track{} - track.demuxer = self - track.pid = info.ElementaryPID + stream.demuxer = self + stream.pid = info.ElementaryPID switch info.StreamType { case ElementaryStreamTypeH264: - track.Type = H264 - self.TrackH264 = track - self.Tracks = append(self.Tracks, track) + stream.Type = H264 + self.TrackH264 = stream + self.Tracks = append(self.Tracks, stream) case ElementaryStreamTypeAdtsAAC: - track.Type = AAC - self.TrackAAC = track - self.Tracks = append(self.Tracks, track) + stream.Type = AAC + self.TrackAAC = stream + self.Tracks = append(self.Tracks, stream) } } } } } else { - for _, track := range self.Tracks { - if header.PID == track.pid { - if err = track.appendPacket(header, payload); err != nil { + for _, stream := range self.Tracks { + if header.PID == stream.pid { + if err = stream.appendPacket(header, payload); err != nil { return } } diff --git a/muxer.go b/muxer.go index 2503933..6f2c161 100644 --- a/muxer.go +++ b/muxer.go @@ -3,8 +3,8 @@ package ts import ( "bytes" "fmt" - "github.com/nareix/codec/h264parser" "github.com/nareix/codec/aacparser" + "github.com/nareix/codec/h264parser" "io" ) @@ -17,8 +17,8 @@ type Muxer struct { Tracks []*Track } -func (self *Muxer) newTrack(pid uint, streamId uint) (track *Track) { - track = &Track{ +func (self *Muxer) newTrack(pid uint, streamId uint) (stream *Track) { + stream = &Track{ mux: self, tsw: &TSWriter{ PID: pid, @@ -26,31 +26,31 @@ func (self *Muxer) newTrack(pid uint, streamId uint) (track *Track) { }, streamId: streamId, } - track.tsw.EnableVecWriter() + stream.tsw.EnableVecWriter() return } -func (self *Muxer) AddAACTrack() (track *Track) { +func (self *Muxer) AddAACTrack() (stream *Track) { self.elemStreams = append( self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, ) - track = self.newTrack(0x101, StreamIdAAC) - track.Type = AAC - track.cacheSize = 3000 - self.Tracks = append(self.Tracks, track) + stream = self.newTrack(0x101, StreamIdAAC) + stream.Type = AAC + stream.cacheSize = 3000 + self.Tracks = append(self.Tracks, stream) return } -func (self *Muxer) AddH264Track() (track *Track) { +func (self *Muxer) AddH264Track() (stream *Track) { self.elemStreams = append( self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, ) - track = self.newTrack(0x100, StreamIdH264) - track.Type = H264 - self.TrackH264 = track - self.Tracks = append(self.Tracks, track) + stream = self.newTrack(0x100, StreamIdH264) + stream.Type = H264 + self.TrackH264 = stream + self.Tracks = append(self.Tracks, stream) return } @@ -86,8 +86,8 @@ func (self *Muxer) WriteHeader() (err error) { } // about to remove - for _, track := range self.Tracks { - track.spsHasWritten = false + for _, stream := range self.Tracks { + stream.spsHasWritten = false } return diff --git a/track.go b/stream.go similarity index 100% rename from track.go rename to stream.go From e01abfd2a64b6467ebce180914b8cc20d3deaac8 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 19:21:52 +0800 Subject: [PATCH 58/82] rewrite Track to Stream --- demuxer.go | 20 ++++++++++---------- muxer.go | 38 +++++++++++++++++++------------------- stream.go | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/demuxer.go b/demuxer.go index dd89a0b..69f47c2 100644 --- a/demuxer.go +++ b/demuxer.go @@ -12,9 +12,9 @@ type Demuxer struct { pat PAT pmt *PMT - Tracks []*Track - TrackH264 *Track - TrackAAC *Track + Tracks []*Stream + TrackH264 *Stream + TrackAAC *Stream } // ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) @@ -25,7 +25,7 @@ func (self *Demuxer) TimeScale() int64 { } func (self *Demuxer) ReadHeader() (err error) { - self.Tracks = []*Track{} + self.Tracks = []*Stream{} self.TrackH264 = nil self.TrackAAC = nil @@ -50,7 +50,7 @@ func (self *Demuxer) ReadHeader() (err error) { return } -func (self *Demuxer) ReadSample() (stream *Track, err error) { +func (self *Demuxer) ReadSample() (stream *Stream, err error) { if len(self.Tracks) == 0 { err = fmt.Errorf("no track") return @@ -93,7 +93,7 @@ func (self *Demuxer) readPacket() (err error) { return } for _, info := range self.pmt.ElementaryStreamInfos { - stream := &Track{} + stream := &Stream{} stream.demuxer = self stream.pid = info.ElementaryPID @@ -125,11 +125,11 @@ func (self *Demuxer) readPacket() (err error) { return } -func (self *Track) GetMPEG4AudioConfig() aacparser.MPEG4AudioConfig { +func (self *Stream) GetMPEG4AudioConfig() aacparser.MPEG4AudioConfig { return self.mpeg4AudioConfig } -func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { +func (self *Stream) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { for !self.payloadReady { if err = self.demuxer.readPacket(); err != nil { return @@ -148,7 +148,7 @@ func (self *Track) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []b return } -func (self *Track) appendPayload() (err error) { +func (self *Stream) appendPayload() (err error) { self.payload = self.buf.Bytes() if self.Type == AAC { @@ -168,7 +168,7 @@ func (self *Track) appendPayload() (err error) { return } -func (self *Track) appendPacket(header TSHeader, payload []byte) (err error) { +func (self *Stream) appendPacket(header TSHeader, payload []byte) (err error) { r := bytes.NewReader(payload) lr := &io.LimitedReader{R: r, N: int64(len(payload))} diff --git a/muxer.go b/muxer.go index 6f2c161..b26e210 100644 --- a/muxer.go +++ b/muxer.go @@ -13,12 +13,12 @@ type Muxer struct { tswPAT *TSWriter tswPMT *TSWriter elemStreams []ElementaryStreamInfo - TrackH264 *Track - Tracks []*Track + TrackH264 *Stream + Tracks []*Stream } -func (self *Muxer) newTrack(pid uint, streamId uint) (stream *Track) { - stream = &Track{ +func (self *Muxer) newTrack(pid uint, streamId uint) (stream *Stream) { + stream = &Stream{ mux: self, tsw: &TSWriter{ PID: pid, @@ -30,7 +30,7 @@ func (self *Muxer) newTrack(pid uint, streamId uint) (stream *Track) { return } -func (self *Muxer) AddAACTrack() (stream *Track) { +func (self *Muxer) AddAACTrack() (stream *Stream) { self.elemStreams = append( self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, @@ -42,7 +42,7 @@ func (self *Muxer) AddAACTrack() (stream *Track) { return } -func (self *Muxer) AddH264Track() (stream *Track) { +func (self *Muxer) AddH264Track() (stream *Stream) { self.elemStreams = append( self.elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, @@ -93,35 +93,35 @@ func (self *Muxer) WriteHeader() (err error) { return } -func (self *Track) SetH264PPSAndSPS(pps []byte, sps []byte) { +func (self *Stream) SetH264PPSAndSPS(pps []byte, sps []byte) { self.PPS, self.SPS = pps, sps } -func (self *Track) SetTimeScale(timeScale int64) { +func (self *Stream) SetTimeScale(timeScale int64) { self.timeScale = timeScale } -func (self *Track) TimeScale() int64 { +func (self *Stream) TimeScale() int64 { return self.timeScale } -func (self *Track) SetMPEG4AudioConfig(config aacparser.MPEG4AudioConfig) { +func (self *Stream) SetMPEG4AudioConfig(config aacparser.MPEG4AudioConfig) { self.mpeg4AudioConfig = config } -func (self *Track) tsToPesTs(ts int64) uint64 { +func (self *Stream) tsToPesTs(ts int64) uint64 { return uint64(ts)*PTS_HZ/uint64(self.timeScale) + PTS_HZ } -func (self *Track) tsToPCR(ts int64) uint64 { +func (self *Stream) tsToPCR(ts int64) uint64 { return uint64(ts)*PCR_HZ/uint64(self.timeScale) + PCR_HZ } -func (self *Track) tsToTime(ts int64) float64 { +func (self *Stream) tsToTime(ts int64) float64 { return float64(ts) / float64(self.timeScale) } -func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { +func (self *Stream) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { if false { fmt.Println("WriteSample", self.Type, self.tsToTime(dts)) } @@ -180,11 +180,11 @@ func (self *Track) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byt /* about to remove */ -func (self *Track) setPCR() { +func (self *Stream) setPCR() { self.tsw.PCR = uint64(self.PTS) * PCR_HZ / uint64(self.timeScale) } -func (self *Track) getPesHeader(dataLength int) (data []byte) { +func (self *Stream) getPesHeader(dataLength int) (data []byte) { if self.PTS == 0 { self.PTS = self.timeScale } @@ -197,11 +197,11 @@ func (self *Track) getPesHeader(dataLength int) (data []byte) { return buf.Bytes() } -func (self *Track) incPTS(delta int) { +func (self *Stream) incPTS(delta int) { self.PTS += int64(delta) } -func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err error) { +func (self *Stream) WriteH264NALU(sync bool, duration int, nalu []byte) (err error) { nalus := [][]byte{} if !self.spsHasWritten { @@ -234,7 +234,7 @@ func (self *Track) WriteH264NALU(sync bool, duration int, nalu []byte) (err erro return } -func (self *Track) WriteADTSAACFrame(duration int, frame []byte) (err error) { +func (self *Stream) WriteADTSAACFrame(duration int, frame []byte) (err error) { if self.dataBuf != nil && self.dataBuf.Len > self.cacheSize { self.dataBuf.Prepend(self.getPesHeader(self.dataBuf.Len)) self.tsw.RandomAccessIndicator = true diff --git a/stream.go b/stream.go index 23e34d8..bfc6225 100644 --- a/stream.go +++ b/stream.go @@ -5,7 +5,7 @@ import ( "github.com/nareix/codec/aacparser" ) -type Track struct { +type Stream struct { SPS []byte PPS []byte From dd4452fb6527019abf3ff148c9f4d365de4c2129 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 20:50:19 +0800 Subject: [PATCH 59/82] rewrite demuxer and muxer --- demuxer.go | 119 +++++++++++++++-------------- muxer.go | 215 ++++++++++++----------------------------------------- stream.go | 39 +++++----- 3 files changed, 128 insertions(+), 245 deletions(-) diff --git a/demuxer.go b/demuxer.go index 69f47c2..ecf0e4e 100644 --- a/demuxer.go +++ b/demuxer.go @@ -3,6 +3,7 @@ package ts import ( "bytes" "fmt" + "github.com/nareix/av" "github.com/nareix/codec/aacparser" "io" ) @@ -10,34 +11,26 @@ import ( type Demuxer struct { R io.Reader - pat PAT - pmt *PMT - Tracks []*Stream - TrackH264 *Stream - TrackAAC *Stream + pat PAT + pmt *PMT + streams []*Stream } // ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) // WritePayload(pid, pts, dts, isKeyFrame, payloads, isVideoFrame) -func (self *Demuxer) TimeScale() int64 { - return PTS_HZ -} - func (self *Demuxer) ReadHeader() (err error) { - self.Tracks = []*Stream{} - self.TrackH264 = nil - self.TrackAAC = nil + self.streams = []*Stream{} for { if self.pmt != nil { n := 0 - for _, stream := range self.Tracks { - if stream.payloadReady { + for _, stream := range self.streams { + if len(stream.pkts) > 0 { n++ } } - if n == len(self.Tracks) { + if n == len(self.streams) { break } } @@ -50,16 +43,18 @@ func (self *Demuxer) ReadHeader() (err error) { return } -func (self *Demuxer) ReadSample() (stream *Stream, err error) { - if len(self.Tracks) == 0 { - err = fmt.Errorf("no track") +func (self *Demuxer) ReadPacket() (streamIndex int, pkt av.Packet, err error) { + if len(self.streams) == 0 { + err = fmt.Errorf("no stream") return } for { - for _, _track := range self.Tracks { - if _track.payloadReady { - stream = _track + for i, stream := range self.streams { + if len(stream.pkts) > 1 { + streamIndex = i + pkt = stream.pkts[0].Packet + stream.pkts = stream.pkts[1:] return } } @@ -99,72 +94,77 @@ func (self *Demuxer) readPacket() (err error) { stream.pid = info.ElementaryPID switch info.StreamType { case ElementaryStreamTypeH264: - stream.Type = H264 - self.TrackH264 = stream - self.Tracks = append(self.Tracks, stream) + stream.SetType(av.H264) + self.streams = append(self.streams, stream) case ElementaryStreamTypeAdtsAAC: - stream.Type = AAC - self.TrackAAC = stream - self.Tracks = append(self.Tracks, stream) + stream.SetType(av.AAC) + self.streams = append(self.streams, stream) } } } } - } else { - for _, stream := range self.Tracks { + } else { + for _, stream := range self.streams { if header.PID == stream.pid { if err = stream.appendPacket(header, payload); err != nil { return } } } + } } return } -func (self *Stream) GetMPEG4AudioConfig() aacparser.MPEG4AudioConfig { - return self.mpeg4AudioConfig -} - -func (self *Stream) ReadSample() (pts int64, dts int64, isKeyFrame bool, data []byte, err error) { - for !self.payloadReady { - if err = self.demuxer.readPacket(); err != nil { - return - } - } - - dts = int64(self.peshdr.DTS) - pts = int64(self.peshdr.PTS) - if dts == 0 { - dts = pts - } - isKeyFrame = self.tshdr.RandomAccessIndicator - data = self.payload - self.payloadReady = false - - return -} - func (self *Stream) appendPayload() (err error) { self.payload = self.buf.Bytes() - if self.Type == AAC { - if !self.mpeg4AudioConfig.IsValid() { - if self.mpeg4AudioConfig, _, _, _, err = aacparser.ReadADTSFrame(self.payload); err != nil { + if self.Type() == av.AAC { + if len(self.CodecData()) == 0 { + var config aacparser.MPEG4AudioConfig + if config, _, _, _, err = aacparser.ReadADTSFrame(self.payload); err != nil { + err = fmt.Errorf("ReadADTSFrame failed: %s", err) return } - self.mpeg4AudioConfig = self.mpeg4AudioConfig.Complete() - if !self.mpeg4AudioConfig.IsValid() { - err = fmt.Errorf("invalid MPEG4AudioConfig") + bw := &bytes.Buffer{} + if err = aacparser.WriteMPEG4AudioConfig(bw, config); err != nil { + err = fmt.Errorf("WriteMPEG4AudioConfig failed: %s", err) + return + } + if err = self.SetCodecData(bw.Bytes()); err != nil { + err = fmt.Errorf("SetCodecData failed: %s", err) return } } } - self.payloadReady = true + dts := self.peshdr.DTS + pts := self.peshdr.PTS + if dts == 0 { + dts = pts + } + + pkt := tsPacket{ + Packet: av.Packet{ + IsKeyFrame: self.tshdr.RandomAccessIndicator, + Data: self.payload, + }, + time: float64(dts)/float64(PTS_HZ), + } + + if pts != dts { + pkt.Duration = float64(pts-dts)/float64(PTS_HZ) + } + + if len(self.pkts) > 0 { + lastPkt := &self.pkts[len(self.pkts)-1] + lastPkt.Duration = pkt.time - lastPkt.time + } + self.pkts = append(self.pkts, pkt) + return } @@ -179,7 +179,6 @@ func (self *Stream) appendPacket(header TSHeader, payload []byte) (err error) { } if header.PayloadUnitStart { - self.payloadReady = false self.buf = bytes.Buffer{} if self.peshdr, err = ReadPESHeader(lr); err != nil { return diff --git a/muxer.go b/muxer.go index b26e210..9bc10d8 100644 --- a/muxer.go +++ b/muxer.go @@ -3,55 +3,27 @@ package ts import ( "bytes" "fmt" + "github.com/nareix/av" "github.com/nareix/codec/aacparser" "github.com/nareix/codec/h264parser" "io" ) type Muxer struct { - W io.Writer - tswPAT *TSWriter - tswPMT *TSWriter - elemStreams []ElementaryStreamInfo - TrackH264 *Stream - Tracks []*Stream + W io.Writer + streams []*Stream } -func (self *Muxer) newTrack(pid uint, streamId uint) (stream *Stream) { - stream = &Stream{ +func (self *Muxer) NewStream() av.Stream { + stream := &Stream{ mux: self, tsw: &TSWriter{ - PID: pid, DiscontinuityIndicator: true, + PID: uint(len(self.streams) + 0x100), }, - streamId: streamId, } - stream.tsw.EnableVecWriter() - return -} - -func (self *Muxer) AddAACTrack() (stream *Stream) { - self.elemStreams = append( - self.elemStreams, - ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, - ) - stream = self.newTrack(0x101, StreamIdAAC) - stream.Type = AAC - stream.cacheSize = 3000 - self.Tracks = append(self.Tracks, stream) - return -} - -func (self *Muxer) AddH264Track() (stream *Stream) { - self.elemStreams = append( - self.elemStreams, - ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, - ) - stream = self.newTrack(0x100, StreamIdH264) - stream.Type = H264 - self.TrackH264 = stream - self.Tracks = append(self.Tracks, stream) - return + self.streams = append(self.streams, stream) + return stream } func (self *Muxer) WriteHeader() (err error) { @@ -64,9 +36,20 @@ func (self *Muxer) WriteHeader() (err error) { }, } WritePAT(bufPAT, pat) + + var elemStreams []ElementaryStreamInfo + for _, stream := range self.streams { + switch stream.Type() { + case av.AAC: + elemStreams = append(elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: stream.tsw.PID}) + case av.H264: + elemStreams = append(elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: stream.tsw.PID}) + } + } + pmt := PMT{ PCRPID: 0x100, - ElementaryStreamInfos: self.elemStreams, + ElementaryStreamInfos: elemStreams, } WritePMT(bufPMT, pmt) @@ -85,169 +68,67 @@ func (self *Muxer) WriteHeader() (err error) { return } - // about to remove - for _, stream := range self.Tracks { - stream.spsHasWritten = false - } - return } -func (self *Stream) SetH264PPSAndSPS(pps []byte, sps []byte) { - self.PPS, self.SPS = pps, sps -} - -func (self *Stream) SetTimeScale(timeScale int64) { - self.timeScale = timeScale -} - -func (self *Stream) TimeScale() int64 { - return self.timeScale -} - -func (self *Stream) SetMPEG4AudioConfig(config aacparser.MPEG4AudioConfig) { - self.mpeg4AudioConfig = config -} - -func (self *Stream) tsToPesTs(ts int64) uint64 { - return uint64(ts)*PTS_HZ/uint64(self.timeScale) + PTS_HZ -} - -func (self *Stream) tsToPCR(ts int64) uint64 { - return uint64(ts)*PCR_HZ/uint64(self.timeScale) + PCR_HZ -} - -func (self *Stream) tsToTime(ts int64) float64 { - return float64(ts) / float64(self.timeScale) -} - -func (self *Stream) WriteSample(pts int64, dts int64, isKeyFrame bool, data []byte) (err error) { - if false { - fmt.Println("WriteSample", self.Type, self.tsToTime(dts)) - } - - if self.Type == AAC { +func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { + stream := self.streams[streamIndex] + if stream.Type() == av.AAC { + data := pkt.Data if !aacparser.IsADTSFrame(data) { - data = append(aacparser.MakeADTSHeader(self.mpeg4AudioConfig, 1024, len(data)), data...) - } - if false { - fmt.Printf("WriteSample=%x\n", data[:5]) + data = append(aacparser.MakeADTSHeader(stream.AACCodecInfo.MPEG4AudioConfig, 1024, len(data)), data...) } buf := &bytes.Buffer{} pes := PESHeader{ - StreamId: self.streamId, - PTS: self.tsToPesTs(pts), + StreamId: StreamIdAAC, + PTS: timeToPesTs(stream.time), } WritePESHeader(buf, pes, len(data)) buf.Write(data) - self.tsw.RandomAccessIndicator = true - self.tsw.PCR = self.tsToPCR(dts) - if err = self.tsw.WriteTo(self.mux.W, buf.Bytes()); err != nil { + stream.tsw.RandomAccessIndicator = true + stream.tsw.PCR = timeToPCR(stream.time) + if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil { return } - } else if self.Type == H264 { + stream.time += pkt.Duration + + } else if stream.Type() == av.H264 { buf := &bytes.Buffer{} pes := PESHeader{ - StreamId: self.streamId, - PTS: self.tsToPesTs(pts), + StreamId: StreamIdH264, + PTS: timeToPesTs(stream.time), } - if dts != pts { - pes.DTS = self.tsToPesTs(dts) + if pkt.CompositionTime > 0.0 { + pes.DTS = timeToPesTs(stream.time + pkt.CompositionTime) } WritePESHeader(buf, pes, 0) - nalus, _ := h264parser.SplitNALUs(data) - if isKeyFrame { - nalus = append([][]byte{self.SPS, self.PPS}, nalus...) + nalus, _ := h264parser.SplitNALUs(pkt.Data) + if pkt.IsKeyFrame { + sps := stream.H264CodecInfo.Record.SPS[0] + pps := stream.H264CodecInfo.Record.PPS[0] + nalus = append([][]byte{sps, pps}, nalus...) } h264parser.WalkNALUsAnnexb(nalus, func(b []byte) { buf.Write(b) }) - self.tsw.RandomAccessIndicator = isKeyFrame - self.tsw.PCR = self.tsToPCR(dts) - if err = self.tsw.WriteTo(self.mux.W, buf.Bytes()); err != nil { + stream.tsw.RandomAccessIndicator = pkt.IsKeyFrame + stream.tsw.PCR = timeToPCR(stream.time) + if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil { return } - } - return -} + stream.time += pkt.Duration -/* about to remove */ - -func (self *Stream) setPCR() { - self.tsw.PCR = uint64(self.PTS) * PCR_HZ / uint64(self.timeScale) -} - -func (self *Stream) getPesHeader(dataLength int) (data []byte) { - if self.PTS == 0 { - self.PTS = self.timeScale - } - buf := &bytes.Buffer{} - pes := PESHeader{ - StreamId: self.streamId, - PTS: uint64(self.PTS) * PTS_HZ / uint64(self.timeScale), - } - WritePESHeader(buf, pes, dataLength) - return buf.Bytes() -} - -func (self *Stream) incPTS(delta int) { - self.PTS += int64(delta) -} - -func (self *Stream) WriteH264NALU(sync bool, duration int, nalu []byte) (err error) { - nalus := [][]byte{} - - if !self.spsHasWritten { - nalus = append(nalus, self.SPS) - nalus = append(nalus, self.PPS) - self.spsHasWritten = true - } - nalus = append(nalus, nalu) - - data := &iovec{} - for i, nalu := range nalus { - var startCode []byte - if i == 0 { - startCode = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD - } else { - startCode = []byte{0, 0, 1} - } - data.Append(startCode) - data.Append(nalu) - } - - data.Prepend(self.getPesHeader(0)) - self.tsw.RandomAccessIndicator = sync - self.setPCR() - if err = self.tsw.WriteIovecTo(self.mux.W, data); err != nil { + } else { + err = fmt.Errorf("unknown stream type=%d", stream.Type()) return } - self.incPTS(duration) - return -} - -func (self *Stream) WriteADTSAACFrame(duration int, frame []byte) (err error) { - if self.dataBuf != nil && self.dataBuf.Len > self.cacheSize { - self.dataBuf.Prepend(self.getPesHeader(self.dataBuf.Len)) - self.tsw.RandomAccessIndicator = true - self.setPCR() - if err = self.tsw.WriteIovecTo(self.mux.W, self.dataBuf); err != nil { - return - } - self.dataBuf = nil - } - if self.dataBuf == nil { - self.dataBuf = &iovec{} - } - self.dataBuf.Append(frame) - self.incPTS(duration) return } diff --git a/stream.go b/stream.go index bfc6225..e7c144c 100644 --- a/stream.go +++ b/stream.go @@ -2,26 +2,26 @@ package ts import ( "bytes" - "github.com/nareix/codec/aacparser" + "github.com/nareix/av" ) +type tsPacket struct { + av.Packet + time float64 +} + type Stream struct { - SPS []byte - PPS []byte + av.StreamCommon - Type int + time float64 - pid uint - PTS int64 - timeScale int64 + pid uint + buf bytes.Buffer + payload []byte + peshdr *PESHeader + tshdr TSHeader - mpeg4AudioConfig aacparser.MPEG4AudioConfig - buf bytes.Buffer - payload []byte - peshdr *PESHeader - tshdr TSHeader - spsHasWritten bool - payloadReady bool + pkts []tsPacket demuxer *Demuxer mux *Muxer @@ -31,7 +31,10 @@ type Stream struct { cacheSize int } -const ( - H264 = 1 - AAC = 2 -) +func timeToPesTs(time float64) uint64 { + return uint64(time*PTS_HZ) + PTS_HZ +} + +func timeToPCR(time float64) uint64 { + return uint64(time*PCR_HZ) + PCR_HZ +} From 902c647841af0abf988e9ba8e0eaa32585a770c0 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 21:21:50 +0800 Subject: [PATCH 60/82] fix muxer cts bug --- muxer.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/muxer.go b/muxer.go index 9bc10d8..14aaa15 100644 --- a/muxer.go +++ b/muxer.go @@ -100,10 +100,8 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { buf := &bytes.Buffer{} pes := PESHeader{ StreamId: StreamIdH264, - PTS: timeToPesTs(stream.time), - } - if pkt.CompositionTime > 0.0 { - pes.DTS = timeToPesTs(stream.time + pkt.CompositionTime) + PTS: timeToPesTs(stream.time + pkt.CompositionTime), + DTS: timeToPesTs(stream.time), } WritePESHeader(buf, pes, 0) From dbbb2bbff3fa34267fbe76c2403cc23094c1687f Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 22:13:32 +0800 Subject: [PATCH 61/82] add WriteTrailer --- muxer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/muxer.go b/muxer.go index 14aaa15..a07d97e 100644 --- a/muxer.go +++ b/muxer.go @@ -26,6 +26,10 @@ func (self *Muxer) NewStream() av.Stream { return stream } +func (self *Muxer) WriteTrailer() (err error) { + return +} + func (self *Muxer) WriteHeader() (err error) { bufPAT := &bytes.Buffer{} bufPMT := &bytes.Buffer{} From bf4a926ce6aa3b2f2bc4c49af6b0f2f7956157bf Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 22:49:42 +0800 Subject: [PATCH 62/82] add DebugOutput --- demuxer.go | 7 +++++++ reader.go | 48 +++++++++++++++++++++++------------------------- ts.go | 5 +++++ writer.go | 12 +++++------- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/demuxer.go b/demuxer.go index ecf0e4e..232d727 100644 --- a/demuxer.go +++ b/demuxer.go @@ -19,6 +19,13 @@ type Demuxer struct { // ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) // WritePayload(pid, pts, dts, isKeyFrame, payloads, isVideoFrame) +func (self *Demuxer) Streams() (streams []av.Stream) { + for _, stream := range self.streams { + streams = append(streams, stream) + } + return +} + func (self *Demuxer) ReadHeader() (err error) { self.streams = []*Stream{} diff --git a/reader.go b/reader.go index a4d36c9..3cd8b5d 100644 --- a/reader.go +++ b/reader.go @@ -6,8 +6,6 @@ import ( "io/ioutil" ) -var DebugReader = false - func ReadUInt(r io.Reader, n int) (res uint, err error) { var b [4]byte if _, err = r.Read(b[0:n]); err != nil { @@ -57,7 +55,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } if DebugReader { - fmt.Printf("ts: flags %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "ts: flags %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -102,7 +100,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { } if DebugReader { - fmt.Printf("ts: ext_flags %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "ts: ext_flags %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -135,7 +133,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { // clock is 27MHz self.PCR = UIntToPCR(v) if DebugReader { - fmt.Printf("ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) + fmt.Fprintf(DebugOutput, "ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) } } @@ -172,7 +170,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { if lr.N > 0 { if DebugReader { // rubish - fmt.Println("ts: skip", lr.N) + fmt.Fprintln(DebugOutput, "ts: skip", lr.N) } if err = ReadDummy(lr, int(lr.N)); err != nil { @@ -191,7 +189,7 @@ func ReadTSPacket(r io.Reader, data []byte) (self TSHeader, n int, err error) { return } if DebugReader { - fmt.Println("ts: data len", lr.N) + fmt.Fprintln(DebugOutput, "ts: data len", lr.N) } if n, err = lr.Read(data[:lr.N]); err != nil { return @@ -208,7 +206,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } if DebugReader { - fmt.Printf("psi: pointer=%d\n", pointer) + fmt.Fprintf(DebugOutput, "psi: pointer=%d\n", pointer) } if pointer != 0 { @@ -232,7 +230,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err length = flags & 0x3FF if DebugReader { - fmt.Printf("psi: tableid=%d len=%d\n", self.TableId, length) + fmt.Fprintf(DebugOutput, "psi: tableid=%d len=%d\n", self.TableId, length) } lr = &io.LimitedReader{R: cr, N: int64(length)} @@ -250,7 +248,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } if DebugReader { - fmt.Printf("psi: %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "psi: %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -275,7 +273,7 @@ func ReadPSI(r io.Reader) (self PSI, lr *io.LimitedReader, cr *Crc32Reader, err } if DebugReader { - fmt.Printf("psi: table_id=%x table_extension=%x secnum=%x lastsecnum=%x\n", + fmt.Fprintf(DebugOutput, "psi: table_id=%x table_extension=%x secnum=%x lastsecnum=%x\n", self.TableId, self.TableIdExtension, self.SecNum, @@ -325,7 +323,7 @@ func ReadPMT(r io.Reader) (self PMT, err error) { self.PCRPID = flags & 0x1fff if DebugReader { - fmt.Printf("pmt: %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "pmt: %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -347,7 +345,7 @@ func ReadPMT(r io.Reader) (self PMT, err error) { length = flags & 0x3ff if DebugReader { - fmt.Printf("pmt: ProgramDescriptorsLen=%d\n", length) + fmt.Fprintf(DebugOutput, "pmt: ProgramDescriptorsLen=%d\n", length) } if length > 0 { @@ -371,7 +369,7 @@ func ReadPMT(r io.Reader) (self PMT, err error) { info.ElementaryPID = flags & 0x1fff if DebugReader { - fmt.Printf("pmt: info1 %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "pmt: info1 %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -392,7 +390,7 @@ func ReadPMT(r io.Reader) (self PMT, err error) { length = flags & 0x3ff if DebugReader { - fmt.Printf("pmt: info2 %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "pmt: info2 %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -415,13 +413,13 @@ func ReadPMT(r io.Reader) (self PMT, err error) { } if DebugReader { - fmt.Printf("pmt: ProgramDescriptors %v\n", self.ProgramDescriptors) - fmt.Printf("pmt: ElementaryStreamInfos %v\n", self.ElementaryStreamInfos) + fmt.Fprintf(DebugOutput, "pmt: ProgramDescriptors %v\n", self.ProgramDescriptors) + fmt.Fprintf(DebugOutput, "pmt: ElementaryStreamInfos %v\n", self.ElementaryStreamInfos) } if err = cr.ReadCrc32UIntAndCheck(); err != nil { if DebugReader { - fmt.Printf("pmt: %s\n", err) + fmt.Fprintf(DebugOutput, "pmt: %s\n", err) } return } @@ -459,13 +457,13 @@ func ReadPAT(r io.Reader) (self PAT, err error) { if err = cr.ReadCrc32UIntAndCheck(); err != nil { if DebugReader { - fmt.Printf("pat: %s\n", err) + fmt.Fprintf(DebugOutput, "pat: %s\n", err) } return } if DebugReader { - fmt.Printf("pat: %v\n", self) + fmt.Fprintf(DebugOutput, "pat: %v\n", self) } return @@ -494,7 +492,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { return } if DebugReader { - fmt.Printf("pes: StreamId=%x length=%d\n", self.StreamId, length) + fmt.Fprintf(DebugOutput, "pes: StreamId=%x length=%d\n", self.StreamId, length) } if length == 0 { @@ -514,7 +512,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } if DebugReader { - fmt.Printf("pes: %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "pes: %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -542,7 +540,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } if DebugReader { - fmt.Printf("pes: %s\n", FieldsDumper{ + fmt.Fprintf(DebugOutput, "pes: %s\n", FieldsDumper{ Fields: []struct { Length int Desc string @@ -574,7 +572,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: pts %d %f\n", self.PTS, float64(self.PTS)/float64(PTS_HZ)) + fmt.Fprintf(DebugOutput, "pes: pts %d %f\n", self.PTS, float64(self.PTS)/float64(PTS_HZ)) } } @@ -585,7 +583,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } self.DTS = PESUIntToTs(v) if DebugReader { - fmt.Printf("pes: dts %d %f\n", self.DTS, float64(self.DTS)/float64(PTS_HZ)) + fmt.Fprintf(DebugOutput, "pes: dts %d %f\n", self.DTS, float64(self.DTS)/float64(PTS_HZ)) } } diff --git a/ts.go b/ts.go index 1019f14..dccb6a0 100644 --- a/ts.go +++ b/ts.go @@ -2,6 +2,7 @@ package ts import ( "fmt" + "os" ) const ( @@ -95,6 +96,10 @@ func PCRToUInt(pcr uint64) uint64 { return base<<15 | 0x3f<<9 | ext } +var DebugOutput = os.Stdout +var DebugReader = false +var DebugWriter = false + type FieldsDumper struct { Fields []struct { Length int diff --git a/writer.go b/writer.go index 64f970a..ec537c4 100644 --- a/writer.go +++ b/writer.go @@ -6,8 +6,6 @@ import ( "io" ) -var DebugWriter = false - func WriteUInt64(w io.Writer, val uint64, n int) (err error) { var b [8]byte for i := n - 1; i >= 0; i-- { @@ -59,7 +57,7 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err flags |= self.ContinuityCounter & 0xf if DebugWriter { - fmt.Printf("tsw: pid=%x\n", self.PID) + fmt.Fprintf(DebugOutput, "tsw: pid=%x\n", self.PID) } const PCR = 0x10 @@ -118,7 +116,7 @@ func WriteTSHeader(w io.Writer, self TSHeader, dataLength int) (written int, err } if DebugWriter { - fmt.Printf("tsw: header padding=%d\n", paddingLength) + fmt.Fprintf(DebugOutput, "tsw: header padding=%d\n", paddingLength) } if err = WriteUInt(w, length, 1); err != nil { @@ -167,7 +165,7 @@ func (self *TSWriter) EnableVecWriter() { self.vecw = newVecWriter(self.W) if DebugWriter && self.vecw != nil { - fmt.Println("tsw: enabled vec writer") + fmt.Fprintln(DebugOutput, "tsw: enabled vec writer") } } } @@ -217,7 +215,7 @@ func (self *TSWriter) WriteIovecTo(w io.Writer, data *iovec) (err error) { } if DebugWriter { - fmt.Printf("tsw: payloadLength=%d dataLength=%d\n", payloadLength, data.Len) + fmt.Fprintf(DebugOutput, "tsw: payloadLength=%d dataLength=%d\n", payloadLength, data.Len) } if _, err = data.WriteTo(w, payloadLength); err != nil { @@ -273,7 +271,7 @@ func WritePSI(w io.Writer, self PSI, data []byte) (err error) { } if DebugWriter { - fmt.Printf("psiw: length=%d\n", length) + fmt.Fprintf(DebugOutput, "psiw: length=%d\n", length) } // Table ID extension(16) From b1239fc37c4382f1f9e4b76d0cbc2faa6759a276 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 21 Apr 2016 22:52:51 +0800 Subject: [PATCH 63/82] add Time() and fix pkt.cts missing bug --- demuxer.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/demuxer.go b/demuxer.go index 232d727..a330809 100644 --- a/demuxer.go +++ b/demuxer.go @@ -26,6 +26,15 @@ func (self *Demuxer) Streams() (streams []av.Stream) { return } +func (self *Demuxer) Time() float64 { + for _, stream := range self.streams { + if len(stream.pkts) > 0 { + return stream.pkts[len(stream.pkts)-1].time + } + } + return 0.0 +} + func (self *Demuxer) ReadHeader() (err error) { self.streams = []*Stream{} @@ -163,7 +172,7 @@ func (self *Stream) appendPayload() (err error) { } if pts != dts { - pkt.Duration = float64(pts-dts)/float64(PTS_HZ) + pkt.CompositionTime = float64(pts-dts)/float64(PTS_HZ) } if len(self.pkts) > 0 { From 12f2f28f55c4722a658482aaa79da9966d568cce Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 30 Apr 2016 09:44:07 +0800 Subject: [PATCH 64/82] change readPacket mechanism and add h264 probing --- demuxer.go | 185 +++++++++++++++++++++++++++++++++++++++-------------- stream.go | 2 +- 2 files changed, 138 insertions(+), 49 deletions(-) diff --git a/demuxer.go b/demuxer.go index a330809..1887e2d 100644 --- a/demuxer.go +++ b/demuxer.go @@ -3,8 +3,10 @@ package ts import ( "bytes" "fmt" + "encoding/hex" "github.com/nareix/av" "github.com/nareix/codec/aacparser" + "github.com/nareix/codec/h264parser" "io" ) @@ -14,6 +16,9 @@ type Demuxer struct { pat PAT pmt *PMT streams []*Stream + time float64 + + readErr error } // ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) @@ -27,10 +32,8 @@ func (self *Demuxer) Streams() (streams []av.Stream) { } func (self *Demuxer) Time() float64 { - for _, stream := range self.streams { - if len(stream.pkts) > 0 { - return stream.pkts[len(stream.pkts)-1].time - } + if len(self.streams) > 0 { + return self.streams[0].time } return 0.0 } @@ -50,7 +53,6 @@ func (self *Demuxer) ReadHeader() (err error) { break } } - if err = self.readPacket(); err != nil { return } @@ -66,17 +68,39 @@ func (self *Demuxer) ReadPacket() (streamIndex int, pkt av.Packet, err error) { } for { - for i, stream := range self.streams { - if len(stream.pkts) > 1 { - streamIndex = i - pkt = stream.pkts[0].Packet - stream.pkts = stream.pkts[1:] - return + if self.readErr != nil { + if false { + for _, stream := range self.streams { + fmt.Println("read(flush): stream", stream.Type(), "pkts", len(stream.pkts)) + } + } + for i, stream := range self.streams { + var ok bool + if pkt, ok = stream.readLastPacket(); ok { + streamIndex = i + return + } + } + err = self.readErr + return + + } else { + if false { + for _, stream := range self.streams { + fmt.Println("read(normal): stream", stream.Type(), "pkts", len(stream.pkts)) + } + } + for i, stream := range self.streams { + var ok bool + if pkt, ok = stream.readPacket(); ok { + streamIndex = i + return + } } } - if err = self.readPacket(); err != nil { - return + if self.readErr == nil { + self.readErr = self.readPacket() } } } @@ -123,7 +147,7 @@ func (self *Demuxer) readPacket() (err error) { } else { for _, stream := range self.streams { if header.PID == stream.pid { - if err = stream.appendPacket(header, payload); err != nil { + if err = stream.handleTSPacket(header, payload); err != nil { return } } @@ -135,13 +159,78 @@ func (self *Demuxer) readPacket() (err error) { return } -func (self *Stream) appendPayload() (err error) { - self.payload = self.buf.Bytes() +func (self *Stream) readLastPacket() (ret av.Packet, ok bool) { + if len(self.pkts) > 1 { + return self.readPacket() + } + if len(self.pkts) == 1 { + pkt := self.pkts[0] + self.pkts = self.pkts[1:] + if self.peshdr.DataLength == 0 { + pkt.Data = self.buf.Bytes() + } + self.time += pkt.Duration + return pkt.Packet, true + } + return +} - if self.Type() == av.AAC { - if len(self.CodecData()) == 0 { +func (self *Stream) readPacket() (ret av.Packet, ok bool) { + if len(self.pkts) > 1 { + pkt := self.pkts[0] + self.pkts = self.pkts[1:] + self.time += pkt.Duration + return pkt.Packet, true + } + return +} + +func (self *Stream) payloadStart() { + if false { + fmt.Println("payloadStart:", self) + } + + dts := self.peshdr.DTS + pts := self.peshdr.PTS + if dts == 0 { + dts = pts + } + + pkt := tsPacket{ + Packet: av.Packet{ + IsKeyFrame: self.tshdr.RandomAccessIndicator, + }, + time: float64(dts)/float64(PTS_HZ), + } + if pts != dts { + pkt.CompositionTime = float64(pts-dts)/float64(PTS_HZ) + } + + if len(self.pkts) > 0 { + lastpkt := &self.pkts[len(self.pkts)-1] + lastpkt.Duration = pkt.time - lastpkt.time + self.lastDuration = lastpkt.Duration + } else { + pkt.Duration = self.lastDuration + } + + self.pkts = append(self.pkts, pkt) +} + +func (self *Stream) payloadEnd() (err error) { + if false { + fmt.Println("payloadEnd:", self) + } + + payload := self.buf.Bytes() + + curpkt := &self.pkts[len(self.pkts)-1] + curpkt.Data = payload + + if len(self.CodecData()) == 0 { + if self.Type() == av.AAC { var config aacparser.MPEG4AudioConfig - if config, _, _, _, err = aacparser.ReadADTSFrame(self.payload); err != nil { + if config, _, _, _, err = aacparser.ReadADTSFrame(payload); err != nil { err = fmt.Errorf("ReadADTSFrame failed: %s", err) return } @@ -154,42 +243,41 @@ func (self *Stream) appendPayload() (err error) { err = fmt.Errorf("SetCodecData failed: %s", err) return } + } else if self.Type() == av.H264 { + if false { + fmt.Println(hex.Dump(payload)) + } + nalus, _ := h264parser.SplitNALUs(payload) + var sps, pps []byte + for _, nalu := range nalus { + if len(nalu) > 0 { + naltype := nalu[0]&0x1f + if naltype == 7 { + sps = nalu + } else if naltype == 8 { + pps = nalu + } + } + } + if len(sps) > 0 && len(pps) > 0 { + codecData, _ := h264parser.CreateCodecDataBySPSAndPPS(sps, pps) + if err = self.SetCodecData(codecData); err != nil { + err = fmt.Errorf("SetCodecData failed: %s", err) + return + } + } } } - dts := self.peshdr.DTS - pts := self.peshdr.PTS - if dts == 0 { - dts = pts - } - - pkt := tsPacket{ - Packet: av.Packet{ - IsKeyFrame: self.tshdr.RandomAccessIndicator, - Data: self.payload, - }, - time: float64(dts)/float64(PTS_HZ), - } - - if pts != dts { - pkt.CompositionTime = float64(pts-dts)/float64(PTS_HZ) - } - - if len(self.pkts) > 0 { - lastPkt := &self.pkts[len(self.pkts)-1] - lastPkt.Duration = pkt.time - lastPkt.time - } - self.pkts = append(self.pkts, pkt) - return } -func (self *Stream) appendPacket(header TSHeader, payload []byte) (err error) { - r := bytes.NewReader(payload) - lr := &io.LimitedReader{R: r, N: int64(len(payload))} +func (self *Stream) handleTSPacket(header TSHeader, tspacket []byte) (err error) { + r := bytes.NewReader(tspacket) + lr := &io.LimitedReader{R: r, N: int64(len(tspacket))} if header.PayloadUnitStart && self.peshdr != nil && self.peshdr.DataLength == 0 { - if err = self.appendPayload(); err != nil { + if err = self.payloadEnd(); err != nil { return } } @@ -200,6 +288,7 @@ func (self *Stream) appendPacket(header TSHeader, payload []byte) (err error) { return } self.tshdr = header + self.payloadStart() } if _, err = io.CopyN(&self.buf, lr, lr.N); err != nil { @@ -207,7 +296,7 @@ func (self *Stream) appendPacket(header TSHeader, payload []byte) (err error) { } if self.buf.Len() == int(self.peshdr.DataLength) { - if err = self.appendPayload(); err != nil { + if err = self.payloadEnd(); err != nil { return } } diff --git a/stream.go b/stream.go index e7c144c..49df939 100644 --- a/stream.go +++ b/stream.go @@ -14,10 +14,10 @@ type Stream struct { av.StreamCommon time float64 + lastDuration float64 pid uint buf bytes.Buffer - payload []byte peshdr *PESHeader tshdr TSHeader From 5b03e2383fd598212de97cb476daf66f12d2387f Mon Sep 17 00:00:00 2001 From: nareix Date: Tue, 3 May 2016 22:54:50 +0800 Subject: [PATCH 65/82] add writePaddingTSPackets --- demuxer.go | 10 +--------- muxer.go | 33 +++++++++++++++++++++++++++++---- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/demuxer.go b/demuxer.go index 1887e2d..0a6ab8d 100644 --- a/demuxer.go +++ b/demuxer.go @@ -45,7 +45,7 @@ func (self *Demuxer) ReadHeader() (err error) { if self.pmt != nil { n := 0 for _, stream := range self.streams { - if len(stream.pkts) > 0 { + if len(stream.CodecData()) > 0 { n++ } } @@ -186,10 +186,6 @@ func (self *Stream) readPacket() (ret av.Packet, ok bool) { } func (self *Stream) payloadStart() { - if false { - fmt.Println("payloadStart:", self) - } - dts := self.peshdr.DTS pts := self.peshdr.PTS if dts == 0 { @@ -218,10 +214,6 @@ func (self *Stream) payloadStart() { } func (self *Stream) payloadEnd() (err error) { - if false { - fmt.Println("payloadEnd:", self) - } - payload := self.buf.Bytes() curpkt := &self.pkts[len(self.pkts)-1] diff --git a/muxer.go b/muxer.go index a07d97e..4a36ba0 100644 --- a/muxer.go +++ b/muxer.go @@ -12,6 +12,10 @@ import ( type Muxer struct { W io.Writer streams []*Stream + PaddingToMakeCounterCont bool + + tswPAT *TSWriter + tswPMT *TSWriter } func (self *Muxer) NewStream() av.Stream { @@ -26,7 +30,28 @@ func (self *Muxer) NewStream() av.Stream { return stream } +func (self *Muxer) writePaddingTSPackets(tsw *TSWriter) (err error) { + for tsw.ContinuityCounter&0xf != 0x0 { + header := TSHeader{ + PID: tsw.PID, + ContinuityCounter: tsw.ContinuityCounter, + } + if _, err = WriteTSHeader(self.W, header, 0); err != nil { + return + } + tsw.ContinuityCounter++ + } + return +} + func (self *Muxer) WriteTrailer() (err error) { + if self.PaddingToMakeCounterCont { + for _, stream := range self.streams { + if err = self.writePaddingTSPackets(stream.tsw); err != nil { + return + } + } + } return } @@ -57,18 +82,18 @@ func (self *Muxer) WriteHeader() (err error) { } WritePMT(bufPMT, pmt) - tswPMT := &TSWriter{ + self.tswPMT = &TSWriter{ PID: 0x1000, DiscontinuityIndicator: true, } - tswPAT := &TSWriter{ + self.tswPAT = &TSWriter{ PID: 0, DiscontinuityIndicator: true, } - if err = tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil { + if err = self.tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil { return } - if err = tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil { + if err = self.tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil { return } From 9767e9333243d0ab1289309d80a079f1a24afdde Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 4 May 2016 11:32:13 +0800 Subject: [PATCH 66/82] remove example --- example/convts.sh | 4 - example/test.go | 396 ---------------------------------------------- 2 files changed, 400 deletions(-) delete mode 100644 example/convts.sh delete mode 100644 example/test.go diff --git a/example/convts.sh b/example/convts.sh deleted file mode 100644 index b88444a..0000000 --- a/example/convts.sh +++ /dev/null @@ -1,4 +0,0 @@ - -avconv -loglevel debug -y -i /tmp/tiny2.mov \ - -bsf h264_mp4toannexb -an -vcodec copy -strict experimental /tmp/out.ts 2>&1 - diff --git a/example/test.go b/example/test.go deleted file mode 100644 index 5156ac5..0000000 --- a/example/test.go +++ /dev/null @@ -1,396 +0,0 @@ -package main - -import ( - ts "../" - "bytes" - "encoding/gob" - "encoding/hex" - "flag" - "fmt" - "io" - "os" - "runtime/pprof" -) - -type GobAllSamples struct { - TimeScale int - SPS []byte - PPS []byte - Samples []GobSample -} - -type GobSample struct { - Duration int - Data []byte - Sync bool -} - -type Stream struct { - PID uint - PESHeader *ts.PESHeader - FirstTSHeader ts.TSHeader - Title string - Data bytes.Buffer - Type uint - PCR uint64 -} - -type Sample struct { - Type uint - PCR uint64 - PTS uint64 - DTS uint64 - Data []byte - RandomAccessIndicator bool -} - -var ( - debugData = true - debugStream = true -) - -func readSamples(filename string, ch chan Sample) { - defer func() { - close(ch) - }() - - var file *os.File - var err error - if file, err = os.Open(filename); err != nil { - return - } - - data := [188]byte{} - - var n int - var header ts.TSHeader - var pat ts.PAT - var pmt ts.PMT - var payload []byte - var info ts.ElementaryStreamInfo - streams := map[uint]*Stream{} - - findOrCreateStream := func(pid uint) (stream *Stream) { - stream, _ = streams[pid] - if stream == nil { - stream = &Stream{ - PID: pid, - Type: info.StreamType, - } - if stream.Type == ts.ElementaryStreamTypeH264 { - stream.Title = "h264" - } else if stream.Type == ts.ElementaryStreamTypeAdtsAAC { - stream.Title = "aac" - } - streams[pid] = stream - } - return - } - - onStreamPayloadUnitEnd := func(stream *Stream) { - if debugStream { - fmt.Printf("stream: %s end\n", stream.Title) - } - if debugData { - fmt.Println(stream.Type, stream.Title, stream.Data.Len(), "total") - fmt.Println(hex.Dump(stream.Data.Bytes())) - } - ch <- Sample{ - Type: stream.Type, - Data: stream.Data.Bytes(), - PTS: stream.PESHeader.PTS, - DTS: stream.PESHeader.DTS, - PCR: stream.FirstTSHeader.PCR, - RandomAccessIndicator: stream.FirstTSHeader.RandomAccessIndicator, - } - } - - onStreamPayload := func() (err error) { - stream := findOrCreateStream(header.PID) - r := bytes.NewReader(payload) - lr := &io.LimitedReader{R: r, N: int64(len(payload))} - - if header.PayloadUnitStart && stream.PESHeader != nil && stream.PESHeader.DataLength == 0 { - onStreamPayloadUnitEnd(stream) - } - - if header.PayloadUnitStart { - stream.Data = bytes.Buffer{} - if stream.PESHeader, err = ts.ReadPESHeader(lr); err != nil { - return - } - stream.FirstTSHeader = header - if debugStream { - fmt.Printf("stream: %s start\n", stream.Title) - } - } - - if _, err = io.CopyN(&stream.Data, lr, lr.N); err != nil { - return - } - if debugStream { - fmt.Printf("stream: %s %d/%d\n", stream.Title, stream.Data.Len(), stream.PESHeader.DataLength) - } - - if stream.Data.Len() == int(stream.PESHeader.DataLength) { - onStreamPayloadUnitEnd(stream) - } - - return - } - - for { - if header, n, err = ts.ReadTSPacket(file, data[:]); err != nil { - return - } - if debugData { - fmt.Println("header:", header, n) - } - - payload = data[:n] - pr := bytes.NewReader(payload) - - if header.PID == 0 { - if pat, err = ts.ReadPAT(pr); err != nil { - return - } - } - - for _, entry := range pat.Entries { - if entry.ProgramMapPID == header.PID { - //fmt.Println("matchs", entry) - if pmt, err = ts.ReadPMT(pr); err != nil { - return - } - //fmt.Println("pmt", pmt) - } - } - - for _, info = range pmt.ElementaryStreamInfos { - if info.ElementaryPID == header.PID { - onStreamPayload() - } - } - - } -} - -func writeM3U8Header(w io.Writer) { - fmt.Fprintln(w, `#EXTM3U -#EXT-X-ALLOW-CACHE:YES -#EXT-X-PLAYLIST-TYPE:VOD -#EXT-X-TARGETDURATION:9 -#EXT-X-VERSION:3 -#EXT-X-MEDIA-SEQUENCE:0`) -} - -func writeM3U8Item(w io.Writer, filename string, size int64, duration float64) { - fmt.Fprintf(w, `#EXT-X-BYTE-SIZE:%d -#EXTINF:%f, -%s -`, size, duration, filename) -} - -func writeM3U8Footer(w io.Writer) { - fmt.Fprintln(w, `#EXT-X-ENDLIST`) -} - -func testInputGob(pathGob string, pathOut string, testSeg bool, writeM3u8 bool) { - var m3u8file *os.File - lastFilename := pathOut - - gobfile, _ := os.Open(pathGob) - outfile, _ := os.Create(pathOut) - dec := gob.NewDecoder(gobfile) - var allSamples GobAllSamples - dec.Decode(&allSamples) - - if writeM3u8 { - m3u8file, _ = os.Create("index.m3u8") - writeM3U8Header(m3u8file) - } - - muxer := &ts.Muxer{ - W: outfile, - } - trackH264 := muxer.AddH264Track() - trackH264.SPS = allSamples.SPS - trackH264.PPS = allSamples.PPS - trackH264.TimeScale = int64(allSamples.TimeScale) - muxer.WriteHeader() - - lastPTS := int64(0) - syncCount := 0 - segCount := 0 - - for i, sample := range allSamples.Samples { - if debugStream { - fmt.Println("stream: write sample #", i) - } - if sample.Sync { - syncCount++ - if testSeg { - if syncCount%3 == 0 { - filename := fmt.Sprintf("%s.seg%d.ts", pathOut, segCount) - - if debugStream { - fmt.Println("stream:", "seg", segCount, "sync", syncCount, trackH264.PTS) - } - - if m3u8file != nil { - info, _ := outfile.Stat() - size := info.Size() - dur := float64(trackH264.PTS-lastPTS) / float64(allSamples.TimeScale) - writeM3U8Item(m3u8file, lastFilename, size, dur) - } - - lastFilename = filename - outfile.Close() - segCount++ - outfile, _ = os.Create(filename) - muxer.W = outfile - muxer.WriteHeader() - lastPTS = trackH264.PTS - } - } - } - - trackH264.WriteH264NALU(sample.Sync, sample.Duration, sample.Data) - } - - if m3u8file != nil { - writeM3U8Footer(m3u8file) - m3u8file.Close() - } - - outfile.Close() - if debugStream { - fmt.Println("stream: written to", pathOut) - } -} - -func main() { - input := flag.String("i", "", "input file") - output := flag.String("o", "", "output file") - inputGob := flag.String("g", "", "input gob file") - testSegment := flag.Bool("seg", false, "test segment") - writeM3u8 := flag.Bool("m3u8", false, "write m3u8 file") - cpuprofile := flag.String("cpuprofile", "", "write cpu profile to file") - - flag.BoolVar(&debugData, "vd", false, "debug data") - flag.BoolVar(&debugStream, "vs", false, "debug stream") - flag.BoolVar(&ts.DebugReader, "vr", false, "debug reader") - flag.BoolVar(&ts.DebugWriter, "vw", false, "debug writer") - flag.Parse() - - if *cpuprofile != "" { - f, err := os.Create(*cpuprofile) - if err != nil { - return - } - pprof.StartCPUProfile(f) - defer pprof.StopCPUProfile() - } - - if *inputGob != "" && *output != "" { - testInputGob(*inputGob, *output, *testSegment, *writeM3u8) - return - } - - var file *os.File - var err error - - ch := make(chan Sample, 0) - go readSamples(*input, ch) - - if *output != "" { - if file, err = os.Create(*output); err != nil { - return - } - } - - writePAT := func() (err error) { - pat := ts.PAT{ - Entries: []ts.PATEntry{ - {ProgramNumber: 1, ProgramMapPID: 0x1000}, - }, - } - if err = ts.WritePATPacket(file, pat); err != nil { - return - } - return - } - - writePMT := func() (err error) { - pmt := ts.PMT{ - PCRPID: 0x100, - ElementaryStreamInfos: []ts.ElementaryStreamInfo{ - {StreamType: ts.ElementaryStreamTypeH264, ElementaryPID: 0x100}, - {StreamType: ts.ElementaryStreamTypeAdtsAAC, ElementaryPID: 0x101}, - }, - } - if err = ts.WritePMTPacket(file, pmt, 0x1000); err != nil { - return - } - return - } - - var w *ts.TSWriter - var sample Sample - writeSample := func() (err error) { - var streamId uint - var pid uint - - switch sample.Type { - case ts.ElementaryStreamTypeH264: - streamId = ts.StreamIdH264 - pid = 0x100 - case ts.ElementaryStreamTypeAdtsAAC: - streamId = ts.StreamIdAAC - pid = 0x101 - } - - pes := ts.PESHeader{ - StreamId: streamId, - PTS: sample.PTS, - DTS: sample.DTS, - } - w.PID = pid - w.PCR = sample.PCR - w.RandomAccessIndicator = sample.RandomAccessIndicator - if err = ts.WritePESPacket(w, pes, sample.Data); err != nil { - return - } - return - } - - if file != nil { - writePAT() - writePMT() - w = &ts.TSWriter{ - W: file, - PID: 0x100, - } - w.EnableVecWriter() - } - - for { - var ok bool - if sample, ok = <-ch; !ok { - break - } - - if debugStream { - fmt.Println("sample: ", sample.Type, len(sample.Data), - "PCR", sample.PCR, "PTS", sample.PTS, - "DTS", sample.DTS, "sync", sample.RandomAccessIndicator, - ) - //fmt.Print(hex.Dump(sample.Data)) - } - - if file != nil { - writeSample() - } - } - -} From d3ba5273094a541cab4a3476adde079b87715cee Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 25 May 2016 07:44:59 +0800 Subject: [PATCH 67/82] adjust newapi --- demuxer.go | 168 ++++++++++++++++------------------------------------- muxer.go | 32 +++++++--- stream.go | 21 +++---- 3 files changed, 84 insertions(+), 137 deletions(-) diff --git a/demuxer.go b/demuxer.go index 0a6ab8d..af6c029 100644 --- a/demuxer.go +++ b/demuxer.go @@ -5,37 +5,43 @@ import ( "fmt" "encoding/hex" "github.com/nareix/av" + "github.com/nareix/av/pktqueue" "github.com/nareix/codec/aacparser" "github.com/nareix/codec/h264parser" "io" ) +func Open(R io.Reader) (demuxer *Demuxer, err error) { + _demuxer := &Demuxer{R: R} + if err = _demuxer.ReadHeader(); err != nil { + return + } + demuxer = _demuxer + return +} + type Demuxer struct { R io.Reader + gotpkt bool + pktque *pktqueue.Queue pat PAT pmt *PMT streams []*Stream - time float64 - - readErr error } -// ParsePacket() (pid uint, counter int, isStart bool, pts, dst int64, isKeyFrame bool) -// WritePayload(pid, pts, dts, isKeyFrame, payloads, isVideoFrame) - -func (self *Demuxer) Streams() (streams []av.Stream) { +func (self *Demuxer) Streams() (streams []av.CodecData) { for _, stream := range self.streams { streams = append(streams, stream) } return } -func (self *Demuxer) Time() float64 { - if len(self.streams) > 0 { - return self.streams[0].time +func (self *Demuxer) CurrentTime() (time float64) { + if self.pktque != nil { + time = self.pktque.CurrentTime() } - return 0.0 + return } func (self *Demuxer) ReadHeader() (err error) { @@ -45,7 +51,7 @@ func (self *Demuxer) ReadHeader() (err error) { if self.pmt != nil { n := 0 for _, stream := range self.streams { - if len(stream.CodecData()) > 0 { + if stream.CodecData != nil { n++ } } @@ -53,7 +59,7 @@ func (self *Demuxer) ReadHeader() (err error) { break } } - if err = self.readPacket(); err != nil { + if err = self.poll(); err != nil { return } } @@ -61,51 +67,21 @@ func (self *Demuxer) ReadHeader() (err error) { return } -func (self *Demuxer) ReadPacket() (streamIndex int, pkt av.Packet, err error) { - if len(self.streams) == 0 { - err = fmt.Errorf("no stream") - return - } - - for { - if self.readErr != nil { - if false { - for _, stream := range self.streams { - fmt.Println("read(flush): stream", stream.Type(), "pkts", len(stream.pkts)) - } - } - for i, stream := range self.streams { - var ok bool - if pkt, ok = stream.readLastPacket(); ok { - streamIndex = i - return - } - } - err = self.readErr - return - - } else { - if false { - for _, stream := range self.streams { - fmt.Println("read(normal): stream", stream.Type(), "pkts", len(stream.pkts)) - } - } - for i, stream := range self.streams { - var ok bool - if pkt, ok = stream.readPacket(); ok { - streamIndex = i - return - } - } - } - - if self.readErr == nil { - self.readErr = self.readPacket() - } - } +func (self *Demuxer) ReadPacket() (i int, pkt av.Packet, err error) { + return self.pktque.ReadPacket() } -func (self *Demuxer) readPacket() (err error) { +func (self *Demuxer) poll() (err error) { + for self.gotpkt { + if err = self.readTSPacket(); err != nil { + return + } + } + self.gotpkt = false + return +} + +func (self *Demuxer) readTSPacket() (err error) { var header TSHeader var n int var data [188]byte @@ -127,20 +103,21 @@ func (self *Demuxer) readPacket() (err error) { if *self.pmt, err = ReadPMT(bytes.NewReader(payload)); err != nil { return } - for _, info := range self.pmt.ElementaryStreamInfos { + for i, info := range self.pmt.ElementaryStreamInfos { stream := &Stream{} - + stream.idx = i stream.demuxer = self stream.pid = info.ElementaryPID switch info.StreamType { case ElementaryStreamTypeH264: - stream.SetType(av.H264) self.streams = append(self.streams, stream) case ElementaryStreamTypeAdtsAAC: - stream.SetType(av.AAC) self.streams = append(self.streams, stream) } } + self.pktque = &pktqueue.Queue{} + self.pktque.Alloc(len(self.streams)) + self.pktque.Poll = self.poll } } @@ -159,68 +136,28 @@ func (self *Demuxer) readPacket() (err error) { return } -func (self *Stream) readLastPacket() (ret av.Packet, ok bool) { - if len(self.pkts) > 1 { - return self.readPacket() - } - if len(self.pkts) == 1 { - pkt := self.pkts[0] - self.pkts = self.pkts[1:] - if self.peshdr.DataLength == 0 { - pkt.Data = self.buf.Bytes() - } - self.time += pkt.Duration - return pkt.Packet, true - } - return -} +func (self *Stream) payloadEnd() (err error) { + payload := self.buf.Bytes() -func (self *Stream) readPacket() (ret av.Packet, ok bool) { - if len(self.pkts) > 1 { - pkt := self.pkts[0] - self.pkts = self.pkts[1:] - self.time += pkt.Duration - return pkt.Packet, true - } - return -} - -func (self *Stream) payloadStart() { dts := self.peshdr.DTS pts := self.peshdr.PTS if dts == 0 { dts = pts } - pkt := tsPacket{ - Packet: av.Packet{ - IsKeyFrame: self.tshdr.RandomAccessIndicator, - }, - time: float64(dts)/float64(PTS_HZ), + pkt := av.Packet{ + IsKeyFrame: self.tshdr.RandomAccessIndicator, + Data: payload, } + time := float64(dts)/float64(PTS_HZ) if pts != dts { pkt.CompositionTime = float64(pts-dts)/float64(PTS_HZ) } + self.demuxer.pktque.WriteTimePacket(self.idx, time, pkt) + self.demuxer.gotpkt = true - if len(self.pkts) > 0 { - lastpkt := &self.pkts[len(self.pkts)-1] - lastpkt.Duration = pkt.time - lastpkt.time - self.lastDuration = lastpkt.Duration - } else { - pkt.Duration = self.lastDuration - } - - self.pkts = append(self.pkts, pkt) -} - -func (self *Stream) payloadEnd() (err error) { - payload := self.buf.Bytes() - - curpkt := &self.pkts[len(self.pkts)-1] - curpkt.Data = payload - - if len(self.CodecData()) == 0 { - if self.Type() == av.AAC { + if self.CodecData == nil { + if self.streamType == ElementaryStreamTypeAdtsAAC { var config aacparser.MPEG4AudioConfig if config, _, _, _, err = aacparser.ReadADTSFrame(payload); err != nil { err = fmt.Errorf("ReadADTSFrame failed: %s", err) @@ -231,11 +168,10 @@ func (self *Stream) payloadEnd() (err error) { err = fmt.Errorf("WriteMPEG4AudioConfig failed: %s", err) return } - if err = self.SetCodecData(bw.Bytes()); err != nil { - err = fmt.Errorf("SetCodecData failed: %s", err) + if self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(bw.Bytes()); err != nil { return } - } else if self.Type() == av.H264 { + } else if self.streamType == ElementaryStreamTypeH264 { if false { fmt.Println(hex.Dump(payload)) } @@ -252,9 +188,7 @@ func (self *Stream) payloadEnd() (err error) { } } if len(sps) > 0 && len(pps) > 0 { - codecData, _ := h264parser.CreateCodecDataBySPSAndPPS(sps, pps) - if err = self.SetCodecData(codecData); err != nil { - err = fmt.Errorf("SetCodecData failed: %s", err) + if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil { return } } @@ -280,7 +214,6 @@ func (self *Stream) handleTSPacket(header TSHeader, tspacket []byte) (err error) return } self.tshdr = header - self.payloadStart() } if _, err = io.CopyN(&self.buf, lr, lr.N); err != nil { @@ -295,3 +228,4 @@ func (self *Stream) handleTSPacket(header TSHeader, tspacket []byte) (err error) return } + diff --git a/muxer.go b/muxer.go index 4a36ba0..975a545 100644 --- a/muxer.go +++ b/muxer.go @@ -9,6 +9,15 @@ import ( "io" ) +func Create(W io.Writer, streams []av.CodecData) (muxer *Muxer, err error) { + _muxer := &Muxer{W: W} + if err = _muxer.WriteHeader(streams); err != nil { + return + } + muxer = _muxer + return +} + type Muxer struct { W io.Writer streams []*Stream @@ -18,16 +27,17 @@ type Muxer struct { tswPMT *TSWriter } -func (self *Muxer) NewStream() av.Stream { +func (self *Muxer) NewStream(codec av.CodecData) (err error) { stream := &Stream{ - mux: self, + muxer: self, + CodecData: codec, tsw: &TSWriter{ DiscontinuityIndicator: true, PID: uint(len(self.streams) + 0x100), }, } self.streams = append(self.streams, stream) - return stream + return } func (self *Muxer) writePaddingTSPackets(tsw *TSWriter) (err error) { @@ -55,7 +65,13 @@ func (self *Muxer) WriteTrailer() (err error) { return } -func (self *Muxer) WriteHeader() (err error) { +func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { + for _, stream := range streams { + if err = self.NewStream(stream); err != nil { + return + } + } + bufPAT := &bytes.Buffer{} bufPMT := &bytes.Buffer{} @@ -104,9 +120,10 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { stream := self.streams[streamIndex] if stream.Type() == av.AAC { + codec := stream.CodecData.(av.AACCodecData) data := pkt.Data if !aacparser.IsADTSFrame(data) { - data = append(aacparser.MakeADTSHeader(stream.AACCodecInfo.MPEG4AudioConfig, 1024, len(data)), data...) + data = append(codec.MakeADTSHeader(1024, len(data)), data...) } buf := &bytes.Buffer{} @@ -126,6 +143,7 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { stream.time += pkt.Duration } else if stream.Type() == av.H264 { + codec := stream.CodecData.(av.H264CodecData) buf := &bytes.Buffer{} pes := PESHeader{ StreamId: StreamIdH264, @@ -136,9 +154,7 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { nalus, _ := h264parser.SplitNALUs(pkt.Data) if pkt.IsKeyFrame { - sps := stream.H264CodecInfo.Record.SPS[0] - pps := stream.H264CodecInfo.Record.PPS[0] - nalus = append([][]byte{sps, pps}, nalus...) + nalus = append([][]byte{codec.SPS(), codec.PPS()}, nalus...) } h264parser.WalkNALUsAnnexb(nalus, func(b []byte) { buf.Write(b) diff --git a/stream.go b/stream.go index 49df939..a08f0c8 100644 --- a/stream.go +++ b/stream.go @@ -5,30 +5,27 @@ import ( "github.com/nareix/av" ) -type tsPacket struct { - av.Packet - time float64 -} - type Stream struct { - av.StreamCommon - - time float64 - lastDuration float64 + av.CodecData pid uint buf bytes.Buffer peshdr *PESHeader tshdr TSHeader - pkts []tsPacket - demuxer *Demuxer - mux *Muxer + muxer *Muxer + streamId uint + streamType uint + tsw *TSWriter dataBuf *iovec cacheSize int + + idx int + pkt av.Packet + time float64 } func timeToPesTs(time float64) uint64 { From e62032449111847a9058eebeebe4440b06bd4a51 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 26 May 2016 20:14:21 +0800 Subject: [PATCH 68/82] add IsCodecSupported --- muxer.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/muxer.go b/muxer.go index 975a545..ec6ee8b 100644 --- a/muxer.go +++ b/muxer.go @@ -27,7 +27,21 @@ type Muxer struct { tswPMT *TSWriter } +func (self *Muxer) IsCodecSupported(codec av.CodecData) bool { + switch codec.Type() { + case av.H264, av.AAC: + return true + default: + return false + } +} + func (self *Muxer) NewStream(codec av.CodecData) (err error) { + if !self.IsCodecSupported(codec) { + err = fmt.Errorf("codec type=%x is not supported", codec.Type()) + return + } + stream := &Stream{ muxer: self, CodecData: codec, From eaf15b625cf7a85e4546cc6917b536c8945900d6 Mon Sep 17 00:00:00 2001 From: nareix Date: Sun, 5 Jun 2016 20:01:54 +0800 Subject: [PATCH 69/82] update pktqueue --- demuxer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demuxer.go b/demuxer.go index af6c029..f6e8a29 100644 --- a/demuxer.go +++ b/demuxer.go @@ -116,7 +116,7 @@ func (self *Demuxer) readTSPacket() (err error) { } } self.pktque = &pktqueue.Queue{} - self.pktque.Alloc(len(self.streams)) + self.pktque.Alloc(self.Streams()) self.pktque.Poll = self.poll } } From 6c6ccb5d8a3bae3697d17fefcc8b57ad535a2836 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 8 Jun 2016 14:48:32 +0800 Subject: [PATCH 70/82] rename av.H264CodecData --- demuxer.go | 13 +++++++++++-- muxer.go | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/demuxer.go b/demuxer.go index f6e8a29..3d278d4 100644 --- a/demuxer.go +++ b/demuxer.go @@ -30,7 +30,11 @@ type Demuxer struct { streams []*Stream } -func (self *Demuxer) Streams() (streams []av.CodecData) { +func (self *Demuxer) Streams() (streams []av.CodecData, err error) { + if len(self.streams) == 0 { + err = fmt.Errorf("ts: no streams") + return + } for _, stream := range self.streams { streams = append(streams, stream) } @@ -116,7 +120,12 @@ func (self *Demuxer) readTSPacket() (err error) { } } self.pktque = &pktqueue.Queue{} - self.pktque.Alloc(self.Streams()) + + var streams []av.CodecData + if streams, err = self.Streams(); err != nil { + return + } + self.pktque.Alloc(streams) self.pktque.Poll = self.poll } } diff --git a/muxer.go b/muxer.go index ec6ee8b..7e95420 100644 --- a/muxer.go +++ b/muxer.go @@ -134,7 +134,7 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { stream := self.streams[streamIndex] if stream.Type() == av.AAC { - codec := stream.CodecData.(av.AACCodecData) + codec := stream.CodecData.(aacparser.CodecData) data := pkt.Data if !aacparser.IsADTSFrame(data) { data = append(codec.MakeADTSHeader(1024, len(data)), data...) @@ -157,7 +157,7 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { stream.time += pkt.Duration } else if stream.Type() == av.H264 { - codec := stream.CodecData.(av.H264CodecData) + codec := stream.CodecData.(h264parser.CodecData) buf := &bytes.Buffer{} pes := PESHeader{ StreamId: StreamIdH264, From 67ac6de4ed0e800b04f7a3579a0ceb6412aa276a Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 15 Jun 2016 07:46:04 +0800 Subject: [PATCH 71/82] bugfix WriteHeader streams duplicate bug; hide NewStream and IsCodecSupported --- muxer.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/muxer.go b/muxer.go index 7e95420..f113975 100644 --- a/muxer.go +++ b/muxer.go @@ -27,7 +27,7 @@ type Muxer struct { tswPMT *TSWriter } -func (self *Muxer) IsCodecSupported(codec av.CodecData) bool { +func (self *Muxer) isCodecSupported(codec av.CodecData) bool { switch codec.Type() { case av.H264, av.AAC: return true @@ -36,8 +36,8 @@ func (self *Muxer) IsCodecSupported(codec av.CodecData) bool { } } -func (self *Muxer) NewStream(codec av.CodecData) (err error) { - if !self.IsCodecSupported(codec) { +func (self *Muxer) newStream(codec av.CodecData) (err error) { + if !self.isCodecSupported(codec) { err = fmt.Errorf("codec type=%x is not supported", codec.Type()) return } @@ -80,8 +80,9 @@ func (self *Muxer) WriteTrailer() (err error) { } func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { + self.streams = []*Stream{} for _, stream := range streams { - if err = self.NewStream(stream); err != nil { + if err = self.newStream(stream); err != nil { return } } From bfcfd605e6bcfd7d7f66c7b566942a19decd9823 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 15 Jun 2016 07:49:24 +0800 Subject: [PATCH 72/82] add WritePATPMT() --- muxer.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/muxer.go b/muxer.go index f113975..877feeb 100644 --- a/muxer.go +++ b/muxer.go @@ -79,14 +79,7 @@ func (self *Muxer) WriteTrailer() (err error) { return } -func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { - self.streams = []*Stream{} - for _, stream := range streams { - if err = self.newStream(stream); err != nil { - return - } - } - +func (self *Muxer) WritePATPMT() (err error) { bufPAT := &bytes.Buffer{} bufPMT := &bytes.Buffer{} @@ -131,6 +124,21 @@ func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { return } +func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { + self.streams = []*Stream{} + for _, stream := range streams { + if err = self.newStream(stream); err != nil { + return + } + } + + if err = self.WritePATPMT(); err != nil { + return + } + + return +} + func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { stream := self.streams[streamIndex] From 97f2cceb2c9c750bc1cfd6858b73ad8e0a90ec15 Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 18 Jun 2016 08:12:43 +0800 Subject: [PATCH 73/82] change float64->float32 --- demuxer.go | 15 +++++++-------- muxer.go | 12 ++++++------ reader.go | 6 +++--- stream.go | 24 ++++++++++++------------ 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/demuxer.go b/demuxer.go index 3d278d4..3c4fec2 100644 --- a/demuxer.go +++ b/demuxer.go @@ -2,8 +2,8 @@ package ts import ( "bytes" - "fmt" "encoding/hex" + "fmt" "github.com/nareix/av" "github.com/nareix/av/pktqueue" "github.com/nareix/codec/aacparser" @@ -23,7 +23,7 @@ func Open(R io.Reader) (demuxer *Demuxer, err error) { type Demuxer struct { R io.Reader - gotpkt bool + gotpkt bool pktque *pktqueue.Queue pat PAT pmt *PMT @@ -41,7 +41,7 @@ func (self *Demuxer) Streams() (streams []av.CodecData, err error) { return } -func (self *Demuxer) CurrentTime() (time float64) { +func (self *Demuxer) CurrentTime() (time float32) { if self.pktque != nil { time = self.pktque.CurrentTime() } @@ -156,11 +156,11 @@ func (self *Stream) payloadEnd() (err error) { pkt := av.Packet{ IsKeyFrame: self.tshdr.RandomAccessIndicator, - Data: payload, + Data: payload, } - time := float64(dts)/float64(PTS_HZ) + time := float32(dts) / float32(PTS_HZ) if pts != dts { - pkt.CompositionTime = float64(pts-dts)/float64(PTS_HZ) + pkt.CompositionTime = float32(pts-dts) / float32(PTS_HZ) } self.demuxer.pktque.WriteTimePacket(self.idx, time, pkt) self.demuxer.gotpkt = true @@ -188,7 +188,7 @@ func (self *Stream) payloadEnd() (err error) { var sps, pps []byte for _, nalu := range nalus { if len(nalu) > 0 { - naltype := nalu[0]&0x1f + naltype := nalu[0] & 0x1f if naltype == 7 { sps = nalu } else if naltype == 8 { @@ -237,4 +237,3 @@ func (self *Stream) handleTSPacket(header TSHeader, tspacket []byte) (err error) return } - diff --git a/muxer.go b/muxer.go index 877feeb..4b0e472 100644 --- a/muxer.go +++ b/muxer.go @@ -19,12 +19,12 @@ func Create(W io.Writer, streams []av.CodecData) (muxer *Muxer, err error) { } type Muxer struct { - W io.Writer - streams []*Stream + W io.Writer + streams []*Stream PaddingToMakeCounterCont bool - tswPAT *TSWriter - tswPMT *TSWriter + tswPAT *TSWriter + tswPMT *TSWriter } func (self *Muxer) isCodecSupported(codec av.CodecData) bool { @@ -43,7 +43,7 @@ func (self *Muxer) newStream(codec av.CodecData) (err error) { } stream := &Stream{ - muxer: self, + muxer: self, CodecData: codec, tsw: &TSWriter{ DiscontinuityIndicator: true, @@ -57,7 +57,7 @@ func (self *Muxer) newStream(codec av.CodecData) (err error) { func (self *Muxer) writePaddingTSPackets(tsw *TSWriter) (err error) { for tsw.ContinuityCounter&0xf != 0x0 { header := TSHeader{ - PID: tsw.PID, + PID: tsw.PID, ContinuityCounter: tsw.ContinuityCounter, } if _, err = WriteTSHeader(self.W, header, 0); err != nil { diff --git a/reader.go b/reader.go index 3cd8b5d..5e786ff 100644 --- a/reader.go +++ b/reader.go @@ -133,7 +133,7 @@ func ReadTSHeader(r io.Reader) (self TSHeader, err error) { // clock is 27MHz self.PCR = UIntToPCR(v) if DebugReader { - fmt.Fprintf(DebugOutput, "ts: PCR %d %f\n", self.PCR, float64(self.PCR)/PCR_HZ) + fmt.Fprintf(DebugOutput, "ts: PCR %d %f\n", self.PCR, float32(self.PCR)/PCR_HZ) } } @@ -572,7 +572,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { self.PTS = PESUIntToTs(v) if DebugReader { - fmt.Fprintf(DebugOutput, "pes: pts %d %f\n", self.PTS, float64(self.PTS)/float64(PTS_HZ)) + fmt.Fprintf(DebugOutput, "pes: pts %d %f\n", self.PTS, float32(self.PTS)/float32(PTS_HZ)) } } @@ -583,7 +583,7 @@ func ReadPESHeader(r io.Reader) (res *PESHeader, err error) { } self.DTS = PESUIntToTs(v) if DebugReader { - fmt.Fprintf(DebugOutput, "pes: dts %d %f\n", self.DTS, float64(self.DTS)/float64(PTS_HZ)) + fmt.Fprintf(DebugOutput, "pes: dts %d %f\n", self.DTS, float32(self.DTS)/float32(PTS_HZ)) } } diff --git a/stream.go b/stream.go index a08f0c8..3c6b8ed 100644 --- a/stream.go +++ b/stream.go @@ -8,30 +8,30 @@ import ( type Stream struct { av.CodecData - pid uint - buf bytes.Buffer - peshdr *PESHeader - tshdr TSHeader + pid uint + buf bytes.Buffer + peshdr *PESHeader + tshdr TSHeader - demuxer *Demuxer - muxer *Muxer + demuxer *Demuxer + muxer *Muxer - streamId uint + streamId uint streamType uint tsw *TSWriter dataBuf *iovec cacheSize int - idx int - pkt av.Packet - time float64 + idx int + pkt av.Packet + time float32 } -func timeToPesTs(time float64) uint64 { +func timeToPesTs(time float32) uint64 { return uint64(time*PTS_HZ) + PTS_HZ } -func timeToPCR(time float64) uint64 { +func timeToPCR(time float32) uint64 { return uint64(time*PCR_HZ) + PCR_HZ } From 5815ff6efa66acfc879880732502301d20f2b11d Mon Sep 17 00:00:00 2001 From: nareix Date: Sat, 18 Jun 2016 10:13:15 +0800 Subject: [PATCH 74/82] switch to time.Duration --- demuxer.go | 11 ++++++----- stream.go | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/demuxer.go b/demuxer.go index 3c4fec2..dbcdefa 100644 --- a/demuxer.go +++ b/demuxer.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "time" "github.com/nareix/av" "github.com/nareix/av/pktqueue" "github.com/nareix/codec/aacparser" @@ -41,9 +42,9 @@ func (self *Demuxer) Streams() (streams []av.CodecData, err error) { return } -func (self *Demuxer) CurrentTime() (time float32) { +func (self *Demuxer) CurrentTime() (tm time.Duration) { if self.pktque != nil { - time = self.pktque.CurrentTime() + tm = self.pktque.CurrentTime() } return } @@ -158,11 +159,11 @@ func (self *Stream) payloadEnd() (err error) { IsKeyFrame: self.tshdr.RandomAccessIndicator, Data: payload, } - time := float32(dts) / float32(PTS_HZ) + tm := time.Duration(dts)*time.Second / time.Duration(PTS_HZ) if pts != dts { - pkt.CompositionTime = float32(pts-dts) / float32(PTS_HZ) + pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) } - self.demuxer.pktque.WriteTimePacket(self.idx, time, pkt) + self.demuxer.pktque.WriteTimePacket(self.idx, tm, pkt) self.demuxer.gotpkt = true if self.CodecData == nil { diff --git a/stream.go b/stream.go index 3c6b8ed..6410aea 100644 --- a/stream.go +++ b/stream.go @@ -1,6 +1,7 @@ package ts import ( + "time" "bytes" "github.com/nareix/av" ) @@ -25,13 +26,13 @@ type Stream struct { idx int pkt av.Packet - time float32 + time time.Duration } -func timeToPesTs(time float32) uint64 { - return uint64(time*PTS_HZ) + PTS_HZ +func timeToPesTs(tm time.Duration) uint64 { + return uint64(tm*PTS_HZ/time.Second) + PTS_HZ } -func timeToPCR(time float32) uint64 { - return uint64(time*PCR_HZ) + PCR_HZ +func timeToPCR(tm time.Duration) uint64 { + return uint64(tm*PCR_HZ/time.Second) + PCR_HZ } From 50b272ba764bb55b266ed88c0fb69b36cc89f065 Mon Sep 17 00:00:00 2001 From: nareix Date: Wed, 22 Jun 2016 17:59:12 +0800 Subject: [PATCH 75/82] change to new av.Packet --- demuxer.go | 48 +++++++++++++++++++++--------------------------- muxer.go | 38 ++++++++++++++++++++------------------ stream.go | 1 - 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/demuxer.go b/demuxer.go index dbcdefa..4eda814 100644 --- a/demuxer.go +++ b/demuxer.go @@ -6,7 +6,6 @@ import ( "fmt" "time" "github.com/nareix/av" - "github.com/nareix/av/pktqueue" "github.com/nareix/codec/aacparser" "github.com/nareix/codec/h264parser" "io" @@ -25,10 +24,12 @@ type Demuxer struct { R io.Reader gotpkt bool - pktque *pktqueue.Queue + pkt av.Packet + pat PAT pmt *PMT streams []*Stream + streamsintf []av.CodecData } func (self *Demuxer) Streams() (streams []av.CodecData, err error) { @@ -37,21 +38,12 @@ func (self *Demuxer) Streams() (streams []av.CodecData, err error) { return } for _, stream := range self.streams { - streams = append(streams, stream) - } - return -} - -func (self *Demuxer) CurrentTime() (tm time.Duration) { - if self.pktque != nil { - tm = self.pktque.CurrentTime() + streams = append(streams, stream.CodecData) } return } func (self *Demuxer) ReadHeader() (err error) { - self.streams = []*Stream{} - for { if self.pmt != nil { n := 0 @@ -68,12 +60,15 @@ func (self *Demuxer) ReadHeader() (err error) { return } } - return } -func (self *Demuxer) ReadPacket() (i int, pkt av.Packet, err error) { - return self.pktque.ReadPacket() +func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { + if err = self.poll(); err != nil { + return + } + pkt = self.pkt + return } func (self *Demuxer) poll() (err error) { @@ -102,6 +97,8 @@ func (self *Demuxer) readTSPacket() (err error) { } } else { if self.pmt == nil { + self.streams = []*Stream{} + for _, entry := range self.pat.Entries { if entry.ProgramMapPID == header.PID { self.pmt = new(PMT) @@ -120,17 +117,14 @@ func (self *Demuxer) readTSPacket() (err error) { self.streams = append(self.streams, stream) } } - self.pktque = &pktqueue.Queue{} - - var streams []av.CodecData - if streams, err = self.Streams(); err != nil { - return - } - self.pktque.Alloc(streams) - self.pktque.Poll = self.poll } } + self.streamsintf = make([]av.CodecData, len(self.streams)) + for i, stream := range self.streams { + self.streamsintf[i] = stream + } + } else { for _, stream := range self.streams { if header.PID == stream.pid { @@ -155,15 +149,15 @@ func (self *Stream) payloadEnd() (err error) { dts = pts } - pkt := av.Packet{ + self.pkt = av.Packet{ + Idx: int8(self.idx), IsKeyFrame: self.tshdr.RandomAccessIndicator, + Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ), Data: payload, } - tm := time.Duration(dts)*time.Second / time.Duration(PTS_HZ) if pts != dts { - pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) + self.pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) } - self.demuxer.pktque.WriteTimePacket(self.idx, tm, pkt) self.demuxer.gotpkt = true if self.CodecData == nil { diff --git a/muxer.go b/muxer.go index 4b0e472..7ab44e4 100644 --- a/muxer.go +++ b/muxer.go @@ -135,14 +135,24 @@ func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { if err = self.WritePATPMT(); err != nil { return } - return } -func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { - stream := self.streams[streamIndex] +func (self *Muxer) WritePacket(pkt av.Packet) (err error) { + if true { + fmt.Println("ts:", "in", pkt.Idx, pkt.Time, "len", len(pkt.Data)) + } + if err = self.writePacket(pkt); err != nil { + return + } + return +} - if stream.Type() == av.AAC { +func (self *Muxer) writePacket(pkt av.Packet) (err error) { + stream := self.streams[pkt.Idx] + + switch stream.Type() { + case av.AAC: codec := stream.CodecData.(aacparser.CodecData) data := pkt.Data if !aacparser.IsADTSFrame(data) { @@ -152,26 +162,24 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { buf := &bytes.Buffer{} pes := PESHeader{ StreamId: StreamIdAAC, - PTS: timeToPesTs(stream.time), + PTS: timeToPesTs(pkt.Time), } WritePESHeader(buf, pes, len(data)) buf.Write(data) stream.tsw.RandomAccessIndicator = true - stream.tsw.PCR = timeToPCR(stream.time) + stream.tsw.PCR = timeToPCR(pkt.Time) if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil { return } - stream.time += pkt.Duration - - } else if stream.Type() == av.H264 { + case av.H264: codec := stream.CodecData.(h264parser.CodecData) buf := &bytes.Buffer{} pes := PESHeader{ StreamId: StreamIdH264, - PTS: timeToPesTs(stream.time + pkt.CompositionTime), - DTS: timeToPesTs(stream.time), + PTS: timeToPesTs(pkt.Time + pkt.CompositionTime), + DTS: timeToPesTs(pkt.Time), } WritePESHeader(buf, pes, 0) @@ -184,16 +192,10 @@ func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) { }) stream.tsw.RandomAccessIndicator = pkt.IsKeyFrame - stream.tsw.PCR = timeToPCR(stream.time) + stream.tsw.PCR = timeToPCR(pkt.Time) if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil { return } - - stream.time += pkt.Duration - - } else { - err = fmt.Errorf("unknown stream type=%d", stream.Type()) - return } return diff --git a/stream.go b/stream.go index 6410aea..2c665fb 100644 --- a/stream.go +++ b/stream.go @@ -26,7 +26,6 @@ type Stream struct { idx int pkt av.Packet - time time.Duration } func timeToPesTs(tm time.Duration) uint64 { From e85b96ed10fba8bdfddd4d88bc74502f1789286e Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 30 Jun 2016 19:38:52 +0800 Subject: [PATCH 76/82] add handler --- demuxer.go | 9 --------- handler.go | 18 ++++++++++++++++++ muxer.go | 11 +---------- 3 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 handler.go diff --git a/demuxer.go b/demuxer.go index 4eda814..08b1d37 100644 --- a/demuxer.go +++ b/demuxer.go @@ -11,15 +11,6 @@ import ( "io" ) -func Open(R io.Reader) (demuxer *Demuxer, err error) { - _demuxer := &Demuxer{R: R} - if err = _demuxer.ReadHeader(); err != nil { - return - } - demuxer = _demuxer - return -} - type Demuxer struct { R io.Reader diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..d940a48 --- /dev/null +++ b/handler.go @@ -0,0 +1,18 @@ +package ts + +import ( + "io" + "github.com/nareix/av" + "github.com/nareix/av/avutil" +) + +func Handler(h *avutil.RegisterHandler) { + h.Ext = ".ts" + h.ReaderDemuxer = func(r io.Reader) av.Demuxer { + return &Demuxer{R: r} + } + h.WriterMuxer = func(w io.Writer) av.Muxer { + return &Muxer{W: w} + } +} + diff --git a/muxer.go b/muxer.go index 7ab44e4..3a087b7 100644 --- a/muxer.go +++ b/muxer.go @@ -9,15 +9,6 @@ import ( "io" ) -func Create(W io.Writer, streams []av.CodecData) (muxer *Muxer, err error) { - _muxer := &Muxer{W: W} - if err = _muxer.WriteHeader(streams); err != nil { - return - } - muxer = _muxer - return -} - type Muxer struct { W io.Writer streams []*Stream @@ -139,7 +130,7 @@ func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) { } func (self *Muxer) WritePacket(pkt av.Packet) (err error) { - if true { + if false { fmt.Println("ts:", "in", pkt.Idx, pkt.Time, "len", len(pkt.Data)) } if err = self.writePacket(pkt); err != nil { From 61326c9bfd74ca1e2a7f00db44270648e11f97b1 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 30 Jun 2016 22:55:11 +0800 Subject: [PATCH 77/82] ReadHeader() -> Streams() --- demuxer.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/demuxer.go b/demuxer.go index 08b1d37..c5dce08 100644 --- a/demuxer.go +++ b/demuxer.go @@ -20,12 +20,12 @@ type Demuxer struct { pat PAT pmt *PMT streams []*Stream - streamsintf []av.CodecData + + probed bool } func (self *Demuxer) Streams() (streams []av.CodecData, err error) { - if len(self.streams) == 0 { - err = fmt.Errorf("ts: no streams") + if err = self.probe(); err != nil { return } for _, stream := range self.streams { @@ -34,7 +34,10 @@ func (self *Demuxer) Streams() (streams []av.CodecData, err error) { return } -func (self *Demuxer) ReadHeader() (err error) { +func (self *Demuxer) probe() (err error) { + if self.probed { + return + } for { if self.pmt != nil { n := 0 @@ -51,10 +54,14 @@ func (self *Demuxer) ReadHeader() (err error) { return } } + self.probed = true return } func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { + if err = self.probe(); err != nil { + return + } if err = self.poll(); err != nil { return } @@ -111,11 +118,6 @@ func (self *Demuxer) readTSPacket() (err error) { } } - self.streamsintf = make([]av.CodecData, len(self.streams)) - for i, stream := range self.streams { - self.streamsintf[i] = stream - } - } else { for _, stream := range self.streams { if header.PID == stream.pid { From 9ffa28a3fd1f3a4c068c2deee3eb364d86a4afbf Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 30 Jun 2016 23:00:22 +0800 Subject: [PATCH 78/82] remove unused code --- writer.go | 144 ------------------------------------------------------ 1 file changed, 144 deletions(-) diff --git a/writer.go b/writer.go index ec537c4..85938ff 100644 --- a/writer.go +++ b/writer.go @@ -553,148 +553,4 @@ func WritePMTPacket(w io.Writer, pmt PMT, pid uint) (err error) { return } -type SimpleH264Writer struct { - W io.Writer - TimeScale int - SPS []byte - PPS []byte - - tswPAT *TSWriter - tswPMT *TSWriter - tswH264 *TSWriter - - PTS int64 - PCR int64 - - prepared bool - writeSPS bool - - pesBuf *bytes.Buffer - patBuf []byte - pmtBuf []byte -} - -func (self *SimpleH264Writer) prepare() (err error) { - pat := PAT{ - Entries: []PATEntry{ - {ProgramNumber: 1, ProgramMapPID: 0x1000}, - }, - } - bw := &bytes.Buffer{} - if err = WritePAT(bw, pat); err != nil { - return - } - self.patBuf = bw.Bytes() - - pmt := PMT{ - PCRPID: 0x100, - ElementaryStreamInfos: []ElementaryStreamInfo{ - {StreamType: ElementaryStreamTypeH264, ElementaryPID: 0x100}, - }, - } - bw = &bytes.Buffer{} - if err = WritePMT(bw, pmt); err != nil { - return - } - self.pmtBuf = bw.Bytes() - - self.tswPMT = &TSWriter{ - PID: 0x1000, - DiscontinuityIndicator: true, - } - self.tswPAT = &TSWriter{ - PID: 0, - DiscontinuityIndicator: true, - } - self.tswH264 = &TSWriter{ - PID: 0x100, - DiscontinuityIndicator: true, - } - - self.tswH264.EnableVecWriter() - - if self.PTS == 0 { - // 1s - self.PTS = int64(self.TimeScale) - } - if self.PCR == 0 { - // 1s - self.PCR = int64(self.TimeScale) - } - - self.pesBuf = &bytes.Buffer{} - - self.prepared = true - return -} - -func (self *SimpleH264Writer) WriteHeader() (err error) { - if !self.prepared { - if err = self.prepare(); err != nil { - return - } - } - self.tswPAT.W = self.W - if err = self.tswPAT.Write(self.patBuf); err != nil { - return - } - self.tswPMT.W = self.W - if err = self.tswPMT.Write(self.pmtBuf); err != nil { - return - } - self.writeSPS = true - return -} - -func (self *SimpleH264Writer) WriteNALU(sync bool, duration int, nalu []byte) (err error) { - nalus := [][]byte{} - - if !self.prepared { - if err = self.WriteHeader(); err != nil { - return - } - } - - if self.writeSPS { - self.writeSPS = false - nalus = append(nalus, self.SPS) - nalus = append(nalus, self.PPS) - } - nalus = append(nalus, nalu) - - pes := PESHeader{ - StreamId: StreamIdH264, - PTS: uint64(self.PTS) * PTS_HZ / uint64(self.TimeScale), - } - if err = WritePESHeader(self.pesBuf, pes, 0); err != nil { - return - } - - data := &iovec{} - data.Append(self.pesBuf.Bytes()) - for i, nalu := range nalus { - var startCode []byte - if i == 0 { - startCode = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD - } else { - startCode = []byte{0, 0, 1} - } - data.Append(startCode) - data.Append(nalu) - } - - self.tswH264.RandomAccessIndicator = sync - self.tswH264.PCR = uint64(self.PCR) * PCR_HZ / uint64(self.TimeScale) - - self.tswH264.W = self.W - if err = self.tswH264.WriteIovec(data); err != nil { - return - } - - self.PTS += int64(duration) - self.PCR += int64(duration) - self.pesBuf.Reset() - - return -} From 93f7f62fad45d0663ad0a5c59c842c28352fbd57 Mon Sep 17 00:00:00 2001 From: nareix Date: Thu, 30 Jun 2016 23:21:27 +0800 Subject: [PATCH 79/82] fix demuxer bug --- demuxer.go | 14 +++++++++----- stream.go | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/demuxer.go b/demuxer.go index c5dce08..f34b203 100644 --- a/demuxer.go +++ b/demuxer.go @@ -70,7 +70,7 @@ func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { } func (self *Demuxer) poll() (err error) { - for self.gotpkt { + for !self.gotpkt { if err = self.readTSPacket(); err != nil { return } @@ -108,6 +108,7 @@ func (self *Demuxer) readTSPacket() (err error) { stream.idx = i stream.demuxer = self stream.pid = info.ElementaryPID + stream.streamType = info.StreamType switch info.StreamType { case ElementaryStreamTypeH264: self.streams = append(self.streams, stream) @@ -142,19 +143,21 @@ func (self *Stream) payloadEnd() (err error) { dts = pts } - self.pkt = av.Packet{ + pkt := &self.demuxer.pkt + *pkt = av.Packet{ Idx: int8(self.idx), IsKeyFrame: self.tshdr.RandomAccessIndicator, Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ), Data: payload, } if pts != dts { - self.pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) + pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) } self.demuxer.gotpkt = true if self.CodecData == nil { - if self.streamType == ElementaryStreamTypeAdtsAAC { + switch self.streamType { + case ElementaryStreamTypeAdtsAAC: var config aacparser.MPEG4AudioConfig if config, _, _, _, err = aacparser.ReadADTSFrame(payload); err != nil { err = fmt.Errorf("ReadADTSFrame failed: %s", err) @@ -168,7 +171,8 @@ func (self *Stream) payloadEnd() (err error) { if self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(bw.Bytes()); err != nil { return } - } else if self.streamType == ElementaryStreamTypeH264 { + + case ElementaryStreamTypeH264: if false { fmt.Println(hex.Dump(payload)) } diff --git a/stream.go b/stream.go index 2c665fb..3bd2a2a 100644 --- a/stream.go +++ b/stream.go @@ -25,7 +25,6 @@ type Stream struct { cacheSize int idx int - pkt av.Packet } func timeToPesTs(tm time.Duration) uint64 { From 72fd658033903cd3b98af64f6a248ec0e69b0584 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 1 Jul 2016 06:10:18 +0800 Subject: [PATCH 80/82] demuxer add pkts buf --- demuxer.go | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/demuxer.go b/demuxer.go index f34b203..16b665c 100644 --- a/demuxer.go +++ b/demuxer.go @@ -14,8 +14,8 @@ import ( type Demuxer struct { R io.Reader - gotpkt bool - pkt av.Packet + pktidx int + pkts []av.Packet pat PAT pmt *PMT @@ -62,20 +62,31 @@ func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) { if err = self.probe(); err != nil { return } - if err = self.poll(); err != nil { - return + + for self.pktidx == len(self.pkts) { + if err = self.poll(); err != nil { + return + } } - pkt = self.pkt + if self.pktidx < len(self.pkts) { + pkt = self.pkts[self.pktidx] + self.pktidx++ + } + return } func (self *Demuxer) poll() (err error) { - for !self.gotpkt { + self.pktidx = 0 + self.pkts = self.pkts[0:0] + for { if err = self.readTSPacket(); err != nil { return } + if len(self.pkts) > 0 { + break + } } - self.gotpkt = false return } @@ -143,18 +154,6 @@ func (self *Stream) payloadEnd() (err error) { dts = pts } - pkt := &self.demuxer.pkt - *pkt = av.Packet{ - Idx: int8(self.idx), - IsKeyFrame: self.tshdr.RandomAccessIndicator, - Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ), - Data: payload, - } - if pts != dts { - pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) - } - self.demuxer.gotpkt = true - if self.CodecData == nil { switch self.streamType { case ElementaryStreamTypeAdtsAAC: @@ -196,6 +195,18 @@ func (self *Stream) payloadEnd() (err error) { } } + demuxer := self.demuxer + pkt := &av.Packet{ + Idx: int8(self.idx), + IsKeyFrame: self.tshdr.RandomAccessIndicator, + Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ), + Data: payload, + } + if pts != dts { + pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) + } + demuxer.pkts = append(demuxer.pkts, *pkt) + return } From 6b887066cffb1d321c25be9c613c373ed611f882 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 1 Jul 2016 18:25:47 +0800 Subject: [PATCH 81/82] demuxer: rewrite packet split logic --- demuxer.go | 99 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/demuxer.go b/demuxer.go index 16b665c..d0f6a78 100644 --- a/demuxer.go +++ b/demuxer.go @@ -145,23 +145,41 @@ func (self *Demuxer) readTSPacket() (err error) { return } -func (self *Stream) payloadEnd() (err error) { - payload := self.buf.Bytes() - +func (self *Stream) addPacket(payload []byte, timedelta time.Duration) { dts := self.peshdr.DTS pts := self.peshdr.PTS if dts == 0 { dts = pts } - if self.CodecData == nil { - switch self.streamType { - case ElementaryStreamTypeAdtsAAC: - var config aacparser.MPEG4AudioConfig - if config, _, _, _, err = aacparser.ReadADTSFrame(payload); err != nil { - err = fmt.Errorf("ReadADTSFrame failed: %s", err) - return - } + demuxer := self.demuxer + pkt := av.Packet{ + Idx: int8(self.idx), + IsKeyFrame: self.tshdr.RandomAccessIndicator, + Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ) + timedelta, + Data: payload, + } + if pts != dts { + pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) + } + demuxer.pkts = append(demuxer.pkts, pkt) +} + +func (self *Stream) payloadEnd() (err error) { + payload := self.buf.Bytes() + + switch self.streamType { + case ElementaryStreamTypeAdtsAAC: + var config aacparser.MPEG4AudioConfig + var packets [][]byte + var totsamples int + if config, packets, totsamples, err = aacparser.SplitADTSFrames(payload); err != nil { + err = fmt.Errorf("ts: demuxer: SplitADTSFrames failed: %s", err) + return + } + config = config.Complete() + + if self.CodecData == nil { bw := &bytes.Buffer{} if err = aacparser.WriteMPEG4AudioConfig(bw, config); err != nil { err = fmt.Errorf("WriteMPEG4AudioConfig failed: %s", err) @@ -170,42 +188,43 @@ func (self *Stream) payloadEnd() (err error) { if self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(bw.Bytes()); err != nil { return } + } - case ElementaryStreamTypeH264: - if false { - fmt.Println(hex.Dump(payload)) - } - nalus, _ := h264parser.SplitNALUs(payload) - var sps, pps []byte - for _, nalu := range nalus { - if len(nalu) > 0 { - naltype := nalu[0] & 0x1f - if naltype == 7 { - sps = nalu - } else if naltype == 8 { - pps = nalu + frametime := time.Duration(totsamples / len(packets)) * time.Second / time.Duration(config.SampleRate) + timedelta := time.Duration(0) + for _, packet := range packets { + self.addPacket(packet, timedelta) + timedelta += frametime + } + + case ElementaryStreamTypeH264: + nalus, _ := h264parser.SplitNALUs(payload) + var sps, pps []byte + for _, nalu := range nalus { + if len(nalu) > 0 { + naltype := nalu[0] & 0x1f + switch naltype { + case 7: + sps = nalu + case 8: + pps = nalu + case 6: + case 9: + default: + if false { + fmt.Println("h264", len(nalus), "\n", hex.Dump(nalu)) } - } - } - if len(sps) > 0 && len(pps) > 0 { - if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil { - return + self.addPacket(nalu, time.Duration(0)) } } } - } - demuxer := self.demuxer - pkt := &av.Packet{ - Idx: int8(self.idx), - IsKeyFrame: self.tshdr.RandomAccessIndicator, - Time: time.Duration(dts)*time.Second / time.Duration(PTS_HZ), - Data: payload, + if self.CodecData == nil && len(sps) > 0 && len(pps) > 0 { + if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil { + return + } + } } - if pts != dts { - pkt.CompositionTime = time.Duration(pts-dts)*time.Second / time.Duration(PTS_HZ) - } - demuxer.pkts = append(demuxer.pkts, *pkt) return } From 7530d353fdefcc59d83c3c8ebea0a147dcbab234 Mon Sep 17 00:00:00 2001 From: nareix Date: Fri, 1 Jul 2016 18:31:42 +0800 Subject: [PATCH 82/82] muxer: use h264parser.CheckNALUsType --- muxer.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/muxer.go b/muxer.go index 3a087b7..e43ffbc 100644 --- a/muxer.go +++ b/muxer.go @@ -146,9 +146,7 @@ func (self *Muxer) writePacket(pkt av.Packet) (err error) { case av.AAC: codec := stream.CodecData.(aacparser.CodecData) data := pkt.Data - if !aacparser.IsADTSFrame(data) { - data = append(codec.MakeADTSHeader(1024, len(data)), data...) - } + data = append(codec.MakeADTSHeader(1024, len(data)), data...) buf := &bytes.Buffer{} pes := PESHeader{ @@ -174,10 +172,15 @@ func (self *Muxer) writePacket(pkt av.Packet) (err error) { } WritePESHeader(buf, pes, 0) - nalus, _ := h264parser.SplitNALUs(pkt.Data) - if pkt.IsKeyFrame { - nalus = append([][]byte{codec.SPS(), codec.PPS()}, nalus...) + if typ := h264parser.CheckNALUsType(pkt.Data); typ != h264parser.NALU_RAW { + err = fmt.Errorf("ts: h264 nalu format=%d invalid", typ) + return } + nalus := [][]byte{} + if pkt.IsKeyFrame { + nalus = append([][]byte{codec.SPS(), codec.PPS()}) + } + nalus = append(nalus, pkt.Data) h264parser.WalkNALUsAnnexb(nalus, func(b []byte) { buf.Write(b) })