mirror of https://github.com/gwuhaolin/livego.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
6.9 KiB
364 lines
6.9 KiB
package ts |
|
|
|
import ( |
|
"io" |
|
|
|
"github.com/gwuhaolin/livego/av" |
|
) |
|
|
|
const ( |
|
tsDefaultDataLen = 184 |
|
tsPacketLen = 188 |
|
h264DefaultHZ = 90 |
|
|
|
videoPID = 0x100 |
|
audioPID = 0x101 |
|
videoSID = 0xe0 |
|
audioSID = 0xc0 |
|
) |
|
|
|
type Muxer struct { |
|
videoCc byte |
|
audioCc byte |
|
patCc byte |
|
pmtCc byte |
|
pat [tsPacketLen]byte |
|
pmt [tsPacketLen]byte |
|
tsPacket [tsPacketLen]byte |
|
} |
|
|
|
func NewMuxer() *Muxer { |
|
return &Muxer{} |
|
} |
|
|
|
func (muxer *Muxer) Mux(p *av.Packet, w io.Writer) error { |
|
first := true |
|
wBytes := 0 |
|
pesIndex := 0 |
|
tmpLen := byte(0) |
|
dataLen := byte(0) |
|
|
|
var pes pesHeader |
|
dts := int64(p.TimeStamp) * int64(h264DefaultHZ) |
|
pts := dts |
|
pid := audioPID |
|
var videoH av.VideoPacketHeader |
|
if p.IsVideo { |
|
pid = videoPID |
|
videoH, _ = p.Header.(av.VideoPacketHeader) |
|
pts = dts + int64(videoH.CompositionTime())*int64(h264DefaultHZ) |
|
} |
|
err := pes.packet(p, pts, dts) |
|
if err != nil { |
|
return err |
|
} |
|
pesHeaderLen := pes.len |
|
packetBytesLen := len(p.Data) + int(pesHeaderLen) |
|
|
|
for { |
|
if packetBytesLen <= 0 { |
|
break |
|
} |
|
if p.IsVideo { |
|
muxer.videoCc++ |
|
if muxer.videoCc > 0xf { |
|
muxer.videoCc = 0 |
|
} |
|
} else { |
|
muxer.audioCc++ |
|
if muxer.audioCc > 0xf { |
|
muxer.audioCc = 0 |
|
} |
|
} |
|
|
|
i := byte(0) |
|
|
|
//sync byte |
|
muxer.tsPacket[i] = 0x47 |
|
i++ |
|
|
|
//error indicator, unit start indicator,ts priority,pid |
|
muxer.tsPacket[i] = byte(pid >> 8) //pid high 5 bits |
|
if first { |
|
muxer.tsPacket[i] = muxer.tsPacket[i] | 0x40 //unit start indicator |
|
} |
|
i++ |
|
|
|
//pid low 8 bits |
|
muxer.tsPacket[i] = byte(pid) |
|
i++ |
|
|
|
//scram control, adaptation control, counter |
|
if p.IsVideo { |
|
muxer.tsPacket[i] = 0x10 | byte(muxer.videoCc&0x0f) |
|
} else { |
|
muxer.tsPacket[i] = 0x10 | byte(muxer.audioCc&0x0f) |
|
} |
|
i++ |
|
|
|
//关键帧需要加pcr |
|
if first && p.IsVideo && videoH.IsKeyFrame() { |
|
muxer.tsPacket[3] |= 0x20 |
|
muxer.tsPacket[i] = 7 |
|
i++ |
|
muxer.tsPacket[i] = 0x50 |
|
i++ |
|
muxer.writePcr(muxer.tsPacket[0:], i, dts) |
|
i += 6 |
|
} |
|
|
|
//frame data |
|
if packetBytesLen >= tsDefaultDataLen { |
|
dataLen = tsDefaultDataLen |
|
if first { |
|
dataLen -= (i - 4) |
|
} |
|
} else { |
|
muxer.tsPacket[3] |= 0x20 //have adaptation |
|
remainBytes := byte(0) |
|
dataLen = byte(packetBytesLen) |
|
if first { |
|
remainBytes = tsDefaultDataLen - dataLen - (i - 4) |
|
} else { |
|
remainBytes = tsDefaultDataLen - dataLen |
|
} |
|
muxer.adaptationBufInit(muxer.tsPacket[i:], byte(remainBytes)) |
|
i += remainBytes |
|
} |
|
if first && i < tsPacketLen && pesHeaderLen > 0 { |
|
tmpLen = tsPacketLen - i |
|
if pesHeaderLen <= tmpLen { |
|
tmpLen = pesHeaderLen |
|
} |
|
copy(muxer.tsPacket[i:], pes.data[pesIndex:pesIndex+int(tmpLen)]) |
|
i += tmpLen |
|
packetBytesLen -= int(tmpLen) |
|
dataLen -= tmpLen |
|
pesHeaderLen -= tmpLen |
|
pesIndex += int(tmpLen) |
|
} |
|
|
|
if i < tsPacketLen { |
|
tmpLen = tsPacketLen - i |
|
if tmpLen <= dataLen { |
|
dataLen = tmpLen |
|
} |
|
copy(muxer.tsPacket[i:], p.Data[wBytes:wBytes+int(dataLen)]) |
|
wBytes += int(dataLen) |
|
packetBytesLen -= int(dataLen) |
|
} |
|
if w != nil { |
|
if _, err := w.Write(muxer.tsPacket[0:]); err != nil { |
|
return err |
|
} |
|
} |
|
first = false |
|
} |
|
|
|
return nil |
|
} |
|
|
|
//PAT return pat data |
|
func (muxer *Muxer) PAT() []byte { |
|
i := 0 |
|
remainByte := 0 |
|
tsHeader := []byte{0x47, 0x40, 0x00, 0x10, 0x00} |
|
patHeader := []byte{0x00, 0xb0, 0x0d, 0x00, 0x01, 0xc1, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x01} |
|
|
|
if muxer.patCc > 0xf { |
|
muxer.patCc = 0 |
|
} |
|
tsHeader[3] |= muxer.patCc & 0x0f |
|
muxer.patCc++ |
|
|
|
copy(muxer.pat[i:], tsHeader) |
|
i += len(tsHeader) |
|
|
|
copy(muxer.pat[i:], patHeader) |
|
i += len(patHeader) |
|
|
|
crc32Value := GenCrc32(patHeader) |
|
muxer.pat[i] = byte(crc32Value >> 24) |
|
i++ |
|
muxer.pat[i] = byte(crc32Value >> 16) |
|
i++ |
|
muxer.pat[i] = byte(crc32Value >> 8) |
|
i++ |
|
muxer.pat[i] = byte(crc32Value) |
|
i++ |
|
|
|
remainByte = int(tsPacketLen - i) |
|
for j := 0; j < remainByte; j++ { |
|
muxer.pat[i+j] = 0xff |
|
} |
|
|
|
return muxer.pat[0:] |
|
} |
|
|
|
// PMT return pmt data |
|
func (muxer *Muxer) PMT(soundFormat byte, hasVideo bool) []byte { |
|
i := int(0) |
|
j := int(0) |
|
var progInfo []byte |
|
remainBytes := int(0) |
|
tsHeader := []byte{0x47, 0x50, 0x01, 0x10, 0x00} |
|
pmtHeader := []byte{0x02, 0xb0, 0xff, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xe1, 0x00, 0xf0, 0x00} |
|
if !hasVideo { |
|
pmtHeader[9] = 0x01 |
|
progInfo = []byte{0x0f, 0xe1, 0x01, 0xf0, 0x00} |
|
} else { |
|
progInfo = []byte{0x1b, 0xe1, 0x00, 0xf0, 0x00, //h264 or h265* |
|
0x0f, 0xe1, 0x01, 0xf0, 0x00, //mp3 or aac |
|
} |
|
} |
|
pmtHeader[2] = byte(len(progInfo) + 9 + 4) |
|
|
|
if muxer.pmtCc > 0xf { |
|
muxer.pmtCc = 0 |
|
} |
|
tsHeader[3] |= muxer.pmtCc & 0x0f |
|
muxer.pmtCc++ |
|
|
|
if soundFormat == 2 || |
|
soundFormat == 14 { |
|
if hasVideo { |
|
progInfo[5] = 0x4 |
|
} else { |
|
progInfo[0] = 0x4 |
|
} |
|
} |
|
|
|
copy(muxer.pmt[i:], tsHeader) |
|
i += len(tsHeader) |
|
|
|
copy(muxer.pmt[i:], pmtHeader) |
|
i += len(pmtHeader) |
|
|
|
copy(muxer.pmt[i:], progInfo[0:]) |
|
i += len(progInfo) |
|
|
|
crc32Value := GenCrc32(muxer.pmt[5 : 5+len(pmtHeader)+len(progInfo)]) |
|
muxer.pmt[i] = byte(crc32Value >> 24) |
|
i++ |
|
muxer.pmt[i] = byte(crc32Value >> 16) |
|
i++ |
|
muxer.pmt[i] = byte(crc32Value >> 8) |
|
i++ |
|
muxer.pmt[i] = byte(crc32Value) |
|
i++ |
|
|
|
remainBytes = int(tsPacketLen - i) |
|
for j = 0; j < remainBytes; j++ { |
|
muxer.pmt[i+j] = 0xff |
|
} |
|
|
|
return muxer.pmt[0:] |
|
} |
|
|
|
func (muxer *Muxer) adaptationBufInit(src []byte, remainBytes byte) { |
|
src[0] = byte(remainBytes - 1) |
|
if remainBytes == 1 { |
|
} else { |
|
src[1] = 0x00 |
|
for i := 2; i < len(src); i++ { |
|
src[i] = 0xff |
|
} |
|
} |
|
return |
|
} |
|
|
|
func (muxer *Muxer) writePcr(b []byte, i byte, pcr int64) error { |
|
b[i] = byte(pcr >> 25) |
|
i++ |
|
b[i] = byte((pcr >> 17) & 0xff) |
|
i++ |
|
b[i] = byte((pcr >> 9) & 0xff) |
|
i++ |
|
b[i] = byte((pcr >> 1) & 0xff) |
|
i++ |
|
b[i] = byte(((pcr & 0x1) << 7) | 0x7e) |
|
i++ |
|
b[i] = 0x00 |
|
|
|
return nil |
|
} |
|
|
|
type pesHeader struct { |
|
len byte |
|
data [tsPacketLen]byte |
|
} |
|
|
|
//pesPacket return pes packet |
|
func (header *pesHeader) packet(p *av.Packet, pts, dts int64) error { |
|
//PES header |
|
i := 0 |
|
header.data[i] = 0x00 |
|
i++ |
|
header.data[i] = 0x00 |
|
i++ |
|
header.data[i] = 0x01 |
|
i++ |
|
|
|
sid := audioSID |
|
if p.IsVideo { |
|
sid = videoSID |
|
} |
|
header.data[i] = byte(sid) |
|
i++ |
|
|
|
flag := 0x80 |
|
ptslen := 5 |
|
dtslen := ptslen |
|
headerSize := ptslen |
|
if p.IsVideo && pts != dts { |
|
flag |= 0x40 |
|
headerSize += 5 //add dts |
|
} |
|
size := len(p.Data) + headerSize + 3 |
|
if size > 0xffff { |
|
size = 0 |
|
} |
|
header.data[i] = byte(size >> 8) |
|
i++ |
|
header.data[i] = byte(size) |
|
i++ |
|
|
|
header.data[i] = 0x80 |
|
i++ |
|
header.data[i] = byte(flag) |
|
i++ |
|
header.data[i] = byte(headerSize) |
|
i++ |
|
|
|
header.writeTs(header.data[0:], i, flag>>6, pts) |
|
i += ptslen |
|
if p.IsVideo && pts != dts { |
|
header.writeTs(header.data[0:], i, 1, dts) |
|
i += dtslen |
|
} |
|
|
|
header.len = byte(i) |
|
|
|
return nil |
|
} |
|
|
|
func (header *pesHeader) writeTs(src []byte, i int, fb int, ts int64) { |
|
val := uint32(0) |
|
if ts > 0x1ffffffff { |
|
ts -= 0x1ffffffff |
|
} |
|
val = uint32(fb<<4) | ((uint32(ts>>30) & 0x07) << 1) | 1 |
|
src[i] = byte(val) |
|
i++ |
|
|
|
val = ((uint32(ts>>15) & 0x7fff) << 1) | 1 |
|
src[i] = byte(val >> 8) |
|
i++ |
|
src[i] = byte(val) |
|
i++ |
|
|
|
val = (uint32(ts&0x7fff) << 1) | 1 |
|
src[i] = byte(val >> 8) |
|
i++ |
|
src[i] = byte(val) |
|
}
|
|
|