work HEVC in progress next v3
This commit is contained in:
parent
d9a713ebc1
commit
ae4ae47c04
@ -441,7 +441,7 @@ func NewCodecDataFromAVCDecoderConfRecord(record []byte) (self CodecData, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCodecDataFromSPSAndPPS(sps, pps, vps []byte) (self CodecData, err error) {
|
func NewCodecDataFromVPSAndSPSAndPPS(vps, sps, pps []byte) (self CodecData, err error) {
|
||||||
recordinfo := AVCDecoderConfRecord{}
|
recordinfo := AVCDecoderConfRecord{}
|
||||||
recordinfo.AVCProfileIndication = sps[1]
|
recordinfo.AVCProfileIndication = sps[1]
|
||||||
recordinfo.ProfileCompatibility = sps[2]
|
recordinfo.ProfileCompatibility = sps[2]
|
||||||
@ -468,9 +468,9 @@ type AVCDecoderConfRecord struct {
|
|||||||
ProfileCompatibility uint8
|
ProfileCompatibility uint8
|
||||||
AVCLevelIndication uint8
|
AVCLevelIndication uint8
|
||||||
LengthSizeMinusOne uint8
|
LengthSizeMinusOne uint8
|
||||||
|
VPS [][]byte
|
||||||
SPS [][]byte
|
SPS [][]byte
|
||||||
PPS [][]byte
|
PPS [][]byte
|
||||||
VPS [][]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrDecconfInvalid = fmt.Errorf("h265parser: AVCDecoderConfRecord invalid")
|
var ErrDecconfInvalid = fmt.Errorf("h265parser: AVCDecoderConfRecord invalid")
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/deepch/vdk/av"
|
"github.com/deepch/vdk/av"
|
||||||
"github.com/deepch/vdk/codec/aacparser"
|
"github.com/deepch/vdk/codec/aacparser"
|
||||||
"github.com/deepch/vdk/codec/h264parser"
|
"github.com/deepch/vdk/codec/h264parser"
|
||||||
|
"github.com/deepch/vdk/codec/h265parser"
|
||||||
"github.com/deepch/vdk/format/mp4/mp4io"
|
"github.com/deepch/vdk/format/mp4/mp4io"
|
||||||
"github.com/deepch/vdk/format/mp4f/mp4fio"
|
"github.com/deepch/vdk/format/mp4f/mp4fio"
|
||||||
"github.com/deepch/vdk/utils/bits/pio"
|
"github.com/deepch/vdk/utils/bits/pio"
|
||||||
@ -178,7 +179,7 @@ func (self *Stream) fillTrackAtom() (err error) {
|
|||||||
}
|
}
|
||||||
self.codecString = fmt.Sprintf("avc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
|
self.codecString = fmt.Sprintf("avc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
|
||||||
} else if self.Type() == av.H265 {
|
} else if self.Type() == av.H265 {
|
||||||
codec := self.CodecData.(h264parser.CodecData)
|
codec := self.CodecData.(h265parser.CodecData)
|
||||||
width, height := codec.Width(), codec.Height()
|
width, height := codec.Width(), codec.Height()
|
||||||
self.sample.SampleDesc.HV1Desc = &mp4io.HV1Desc{
|
self.sample.SampleDesc.HV1Desc = &mp4io.HV1Desc{
|
||||||
DataRefIdx: 1,
|
DataRefIdx: 1,
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -23,6 +24,9 @@ type Media struct {
|
|||||||
ChannelCount int
|
ChannelCount int
|
||||||
Config []byte
|
Config []byte
|
||||||
SpropParameterSets [][]byte
|
SpropParameterSets [][]byte
|
||||||
|
SpropVPS []byte
|
||||||
|
SpropSPS []byte
|
||||||
|
SpropPPS []byte
|
||||||
PayloadType int
|
PayloadType int
|
||||||
SizeLength int
|
SizeLength int
|
||||||
IndexLength int
|
IndexLength int
|
||||||
@ -93,6 +97,10 @@ func Parse(content string) (sess Session, medias []Media) {
|
|||||||
}
|
}
|
||||||
case "H264":
|
case "H264":
|
||||||
media.Type = av.H264
|
media.Type = av.H264
|
||||||
|
case "H265":
|
||||||
|
media.Type = av.H265
|
||||||
|
case "HEVC":
|
||||||
|
media.Type = av.H265
|
||||||
}
|
}
|
||||||
if i, err := strconv.Atoi(keyval[1]); err == nil {
|
if i, err := strconv.Atoi(keyval[1]); err == nil {
|
||||||
media.TimeScale = i
|
media.TimeScale = i
|
||||||
@ -115,6 +123,27 @@ func Parse(content string) (sess Session, medias []Media) {
|
|||||||
media.SizeLength, _ = strconv.Atoi(val)
|
media.SizeLength, _ = strconv.Atoi(val)
|
||||||
case "indexlength":
|
case "indexlength":
|
||||||
media.IndexLength, _ = strconv.Atoi(val)
|
media.IndexLength, _ = strconv.Atoi(val)
|
||||||
|
case "sprop-vps":
|
||||||
|
val, err := base64.StdEncoding.DecodeString(val)
|
||||||
|
if err == nil {
|
||||||
|
media.SpropVPS = val
|
||||||
|
} else {
|
||||||
|
log.Println("SDP: decode vps error", err)
|
||||||
|
}
|
||||||
|
case "sprop-sps":
|
||||||
|
val, err := base64.StdEncoding.DecodeString(val)
|
||||||
|
if err == nil {
|
||||||
|
media.SpropSPS = val
|
||||||
|
} else {
|
||||||
|
log.Println("SDP: decode sps error", err)
|
||||||
|
}
|
||||||
|
case "sprop-pps":
|
||||||
|
val, err := base64.StdEncoding.DecodeString(val)
|
||||||
|
if err == nil {
|
||||||
|
media.SpropPPS = val
|
||||||
|
} else {
|
||||||
|
log.Println("SDP: decode pps error", err)
|
||||||
|
}
|
||||||
case "sprop-parameter-sets":
|
case "sprop-parameter-sets":
|
||||||
fields := strings.Split(val, ",")
|
fields := strings.Split(val, ",")
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/deepch/vdk/codec"
|
"github.com/deepch/vdk/codec"
|
||||||
"github.com/deepch/vdk/codec/aacparser"
|
"github.com/deepch/vdk/codec/aacparser"
|
||||||
"github.com/deepch/vdk/codec/h264parser"
|
"github.com/deepch/vdk/codec/h264parser"
|
||||||
|
"github.com/deepch/vdk/codec/h265parser"
|
||||||
"github.com/deepch/vdk/format/rtsp/sdp"
|
"github.com/deepch/vdk/format/rtsp/sdp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -72,12 +73,14 @@ type RTSPClient struct {
|
|||||||
fuStarted bool
|
fuStarted bool
|
||||||
options RTSPClientOptions
|
options RTSPClientOptions
|
||||||
BufferRtpPacket *bytes.Buffer
|
BufferRtpPacket *bytes.Buffer
|
||||||
|
vps []byte
|
||||||
sps []byte
|
sps []byte
|
||||||
pps []byte
|
pps []byte
|
||||||
CodecData []av.CodecData
|
CodecData []av.CodecData
|
||||||
AudioTimeLine time.Duration
|
AudioTimeLine time.Duration
|
||||||
AudioTimeScale int64
|
AudioTimeScale int64
|
||||||
audioCodec av.CodecType
|
audioCodec av.CodecType
|
||||||
|
videoCodec av.CodecType
|
||||||
PreAudioTS int64
|
PreAudioTS int64
|
||||||
PreVideoTS int64
|
PreVideoTS int64
|
||||||
PreSequenceNumber int
|
PreSequenceNumber int
|
||||||
@ -145,6 +148,16 @@ func Dial(options RTSPClientOptions) (*RTSPClient, error) {
|
|||||||
client.pps = i2.SpropParameterSets[1]
|
client.pps = i2.SpropParameterSets[1]
|
||||||
client.CodecData = append(client.CodecData, codecData)
|
client.CodecData = append(client.CodecData, codecData)
|
||||||
client.videoIDX = int8(len(client.CodecData) - 1)
|
client.videoIDX = int8(len(client.CodecData) - 1)
|
||||||
|
client.videoCodec = av.H264
|
||||||
|
}
|
||||||
|
} else if i2.Type == av.H265 && len(i2.SpropVPS) > 1 && len(i2.SpropSPS) > 1 && len(i2.SpropPPS) > 1 {
|
||||||
|
if codecData, err := h265parser.NewCodecDataFromVPSAndSPSAndPPS(i2.SpropVPS, i2.SpropSPS, i2.SpropPPS); err == nil {
|
||||||
|
client.vps = i2.SpropVPS
|
||||||
|
client.sps = i2.SpropSPS
|
||||||
|
client.pps = i2.SpropPPS
|
||||||
|
client.CodecData = append(client.CodecData, codecData)
|
||||||
|
client.videoIDX = int8(len(client.CodecData) - 1)
|
||||||
|
client.videoCodec = av.H265
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
client.Println("SDP Video Codec Type Not Supported", i2.Type)
|
client.Println("SDP Video Codec Type Not Supported", i2.Type)
|
||||||
@ -531,6 +544,55 @@ func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
|
|||||||
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
|
nalRaw, _ := h264parser.SplitNALUs(content[offset:end])
|
||||||
var retmap []*av.Packet
|
var retmap []*av.Packet
|
||||||
for _, nal := range nalRaw {
|
for _, nal := range nalRaw {
|
||||||
|
if client.videoCodec == av.H265 {
|
||||||
|
naluType := (nal[0] >> 1) & 0x3f
|
||||||
|
switch naluType {
|
||||||
|
case h265parser.NAL_UNIT_CODED_SLICE_TRAIL_R:
|
||||||
|
retmap = append(retmap, &av.Packet{
|
||||||
|
Data: append(binSize(len(nal)), nal...),
|
||||||
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
||||||
|
Idx: client.videoIDX,
|
||||||
|
IsKeyFrame: naluType == h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
|
||||||
|
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
|
||||||
|
Time: time.Duration(timestamp/90) * time.Millisecond,
|
||||||
|
})
|
||||||
|
case h265parser.NAL_UNIT_VPS:
|
||||||
|
client.CodecUpdateVPS(nal)
|
||||||
|
case h265parser.NAL_UNIT_SPS:
|
||||||
|
client.CodecUpdateSPS(nal)
|
||||||
|
case h265parser.NAL_UNIT_PPS:
|
||||||
|
client.CodecUpdatePPS(nal)
|
||||||
|
case h265parser.NAL_UNIT_UNSPECIFIED_49:
|
||||||
|
se := nal[2] >> 6
|
||||||
|
naluType = nal[2] & 0x3f
|
||||||
|
if se == 2 {
|
||||||
|
client.BufferRtpPacket.Truncate(0)
|
||||||
|
client.BufferRtpPacket.Reset()
|
||||||
|
client.BufferRtpPacket.Write([]byte{0, 0, 0, 0, (nal[0] & 0x81) | (naluType << 1), nal[1]})
|
||||||
|
r := make([]byte, 2)
|
||||||
|
r[1] = nal[1]
|
||||||
|
r[0] = (nal[0] & 0x81) | (naluType << 1)
|
||||||
|
client.BufferRtpPacket.Write(nal[3:])
|
||||||
|
} else if se == 1 {
|
||||||
|
client.BufferRtpPacket.Write(nal[3:])
|
||||||
|
binary.BigEndian.PutUint32(client.BufferRtpPacket.Bytes()[:4], uint32(client.BufferRtpPacket.Len())-4)
|
||||||
|
buf := make([]byte, client.BufferRtpPacket.Len())
|
||||||
|
copy(buf, client.BufferRtpPacket.Bytes())
|
||||||
|
retmap = append(retmap, &av.Packet{
|
||||||
|
Data: append(binSize(len(nal)), nal...),
|
||||||
|
CompositionTime: time.Duration(1) * time.Millisecond,
|
||||||
|
Idx: client.videoIDX,
|
||||||
|
IsKeyFrame: naluType == h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
|
||||||
|
Duration: time.Duration(float32(timestamp-client.PreVideoTS)/90) * time.Millisecond,
|
||||||
|
Time: time.Duration(timestamp/90) * time.Millisecond,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
client.BufferRtpPacket.Write(nal[3:])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
//log.Println("???", naluType)
|
||||||
|
}
|
||||||
|
} else if client.videoCodec == av.H264 {
|
||||||
naluType := nal[0] & 0x1f
|
naluType := nal[0] & 0x1f
|
||||||
switch {
|
switch {
|
||||||
case naluType >= 1 && naluType <= 5:
|
case naluType >= 1 && naluType <= 5:
|
||||||
@ -594,7 +656,7 @@ func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
|
|||||||
client.Println("Unsupported NAL Type", naluType)
|
client.Println("Unsupported NAL Type", naluType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if len(retmap) > 0 {
|
if len(retmap) > 0 {
|
||||||
client.PreVideoTS = timestamp
|
client.PreVideoTS = timestamp
|
||||||
return retmap, true
|
return retmap, true
|
||||||
@ -680,13 +742,32 @@ func (client *RTSPClient) RTPDemuxer(payloadRAW *[]byte) ([]*av.Packet, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *RTSPClient) CodecUpdateSPS(val []byte) {
|
func (client *RTSPClient) CodecUpdateSPS(val []byte) {
|
||||||
if bytes.Compare(val, client.sps) != 0 {
|
if client.videoCodec != av.H264 && client.videoCodec != av.H265 {
|
||||||
if len(client.sps) > 0 && len(client.pps) > 0 {
|
return
|
||||||
codecData, err := h264parser.NewCodecDataFromSPSAndPPS(val, client.pps)
|
}
|
||||||
|
if bytes.Compare(val, client.sps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.sps = val
|
||||||
|
if (client.videoCodec == av.H264 && len(client.pps) == 0) || (client.videoCodec == av.H265 && (len(client.vps) == 0 || len(client.pps) == 0)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var codecData av.VideoCodecData
|
||||||
|
var err error
|
||||||
|
switch client.videoCodec {
|
||||||
|
case av.H264:
|
||||||
|
codecData, err = h264parser.NewCodecDataFromSPSAndPPS(val, client.pps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.Println("Parse Codec Data Error", err)
|
client.Println("Parse Codec Data Error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case av.H265:
|
||||||
|
codecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(client.vps, val, client.pps)
|
||||||
|
if err != nil {
|
||||||
|
client.Println("Parse Codec Data Error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(client.CodecData) > 0 {
|
if len(client.CodecData) > 0 {
|
||||||
for i, i2 := range client.CodecData {
|
for i, i2 := range client.CodecData {
|
||||||
if i2.Type().IsVideo() {
|
if i2.Type().IsVideo() {
|
||||||
@ -696,16 +777,60 @@ func (client *RTSPClient) CodecUpdateSPS(val []byte) {
|
|||||||
} else {
|
} else {
|
||||||
client.CodecData = append(client.CodecData, codecData)
|
client.CodecData = append(client.CodecData, codecData)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
client.Signals <- SignalCodecUpdate
|
client.Signals <- SignalCodecUpdate
|
||||||
client.sps = val
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *RTSPClient) CodecUpdatePPS(val []byte) {
|
func (client *RTSPClient) CodecUpdatePPS(val []byte) {
|
||||||
if bytes.Compare(val, client.pps) != 0 {
|
if client.videoCodec != av.H264 && client.videoCodec != av.H265 {
|
||||||
if len(client.sps) > 0 && len(client.pps) > 0 {
|
return
|
||||||
codecData, err := h264parser.NewCodecDataFromSPSAndPPS(client.sps, val)
|
}
|
||||||
|
if bytes.Compare(val, client.pps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.pps = val
|
||||||
|
if (client.videoCodec == av.H264 && len(client.sps) == 0) || (client.videoCodec == av.H265 && (len(client.vps) == 0 || len(client.sps) == 0)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var codecData av.VideoCodecData
|
||||||
|
var err error
|
||||||
|
switch client.videoCodec {
|
||||||
|
case av.H264:
|
||||||
|
codecData, err = h264parser.NewCodecDataFromSPSAndPPS(client.sps, val)
|
||||||
|
if err != nil {
|
||||||
|
client.Println("Parse Codec Data Error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case av.H265:
|
||||||
|
codecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(client.vps, client.sps, val)
|
||||||
|
if err != nil {
|
||||||
|
client.Println("Parse Codec Data Error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(client.CodecData) > 0 {
|
||||||
|
for i, i2 := range client.CodecData {
|
||||||
|
if i2.Type().IsVideo() {
|
||||||
|
client.CodecData[i] = codecData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
client.CodecData = append(client.CodecData, codecData)
|
||||||
|
}
|
||||||
|
client.Signals <- SignalCodecUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *RTSPClient) CodecUpdateVPS(val []byte) {
|
||||||
|
if client.videoCodec != av.H265 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if bytes.Compare(val, client.vps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client.vps = val
|
||||||
|
if len(client.sps) == 0 || len(client.pps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
codecData, err := h265parser.NewCodecDataFromVPSAndSPSAndPPS(val, client.sps, client.pps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.Println("Parse Codec Data Error", err)
|
client.Println("Parse Codec Data Error", err)
|
||||||
return
|
return
|
||||||
@ -719,10 +844,7 @@ func (client *RTSPClient) CodecUpdatePPS(val []byte) {
|
|||||||
} else {
|
} else {
|
||||||
client.CodecData = append(client.CodecData, codecData)
|
client.CodecData = append(client.CodecData, codecData)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
client.Signals <- SignalCodecUpdate
|
client.Signals <- SignalCodecUpdate
|
||||||
client.pps = val
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Println mini logging functions
|
//Println mini logging functions
|
||||||
|
Loading…
Reference in New Issue
Block a user