container/mts/mpegts.go: renamed IndexPid to FindPSIs and change such that only looked for PSI

We have only ever used this function to find PSI so it makes sense to make it exclusive for this. It now also returns the streams found in the PMT. So that
we can actually retrieve the PIDs of the media and their types.
This commit is contained in:
Saxon 2019-07-24 12:39:14 +09:30
parent 4807b4fcb6
commit 0825799cfa
2 changed files with 80 additions and 64 deletions

View File

@ -178,16 +178,15 @@ func FindPat(d []byte) ([]byte, int, error) {
// Errors used by FindPid.
var (
errInvalidLen = errors.New("MPEG-TS data not of valid length")
errCouldNotFind = errors.New("could not find packet with given PID")
errNotConsecutive = errors.New("could not find consecutive PIDs")
ErrInvalidLen = errors.New("MPEG-TS data not of valid length")
errCouldNotFind = errors.New("could not find packet with given PID")
)
// FindPid will take a clip of MPEG-TS and try to find a packet with given PID - if one
// is found, then it is returned along with its index, otherwise nil, -1 and an error is returned.
func FindPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
if len(d) < PacketSize {
return nil, -1, errInvalidLen
return nil, -1, ErrInvalidLen
}
for i = 0; i < len(d); i += PacketSize {
p := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2])
@ -205,7 +204,7 @@ func FindPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
// nil, -1 and an error is returned.
func LastPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
if len(d) < PacketSize {
return nil, -1, errInvalidLen
return nil, -1, ErrInvalidLen
}
for i = len(d) - PacketSize; i >= 0; i -= PacketSize {
@ -218,30 +217,68 @@ func LastPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
return nil, -1, errCouldNotFind
}
// IndexPid returns the position of one or more consecutive pids,
// along with optional metadata if present. Commonly used to find a
// PAT immediately followed by a PMT.
func IndexPid(d []byte, pids ...uint16) (idx int, m map[string]string, err error) {
idx = -1
for _, pid := range pids {
if len(d) < PacketSize {
return idx, m, errInvalidLen
}
pkt, i, err := FindPid(d, pid)
if err != nil {
return idx, m, errCouldNotFind
}
if pid == PmtPid {
m, _ = metaFromPMT(pkt)
}
if idx == -1 {
idx = i
} else if i != 0 {
return idx, m, errNotConsecutive
}
d = d[i+PacketSize:]
// Errors used by FindPSI.
var (
ErrMultiplePrograms = errors.New("more than one program not supported")
ErrNoPrograms = errors.New("no programs in PAT")
ErrNotConsecutive = errors.New("could not find consecutive PIDs")
)
// FindPSI finds the index of a PAT in an a slice of MPEG-TS and returns, along
// with a map of meta from the PMT and the PmtElementaryStreams, which contain
// the media PIDs and their types.
func FindPSI(d []byte) (int, []gotspsi.PmtElementaryStream, map[string]string, error) {
if len(d) < PacketSize {
return -1, nil, nil, ErrInvalidLen
}
return
// Find the PAT if it exists.
pkt, i, err := FindPid(d, PatPid)
if err != nil {
return -1, nil, nil, errors.Wrap(err, "error finding PAT")
}
// Let's take this opportunity to check what programs are in this MPEG-TS
// stream, and therefore the PID of the PMT, from which we can get metadata.
// NB: currently we only support one program.
progs, err := Programs(pkt)
if err != nil {
return i, nil, nil, errors.Wrap(err, "cannot get programs from PAT")
}
if len(progs) == 0 {
return i, nil, nil, ErrNoPrograms
}
if len(progs) > 1 {
return i, nil, nil, ErrMultiplePrograms
}
pmtPID := pmtPIDs(progs)[0]
// Now we can look for the PMT. We want to adjust d so that we're not looking
// at the same data twice.
d = d[i+PacketSize:]
pkt, pmtIdx, err := FindPid(d, pmtPID)
if err != nil {
return i, nil, nil, errors.Wrap(err, "error finding PMT")
}
// Check that the PMT comes straight after the PAT.
if pmtIdx != 0 {
return i, nil, nil, ErrNotConsecutive
}
// Now we can try to get meta from the PMT.
meta, _ := metaFromPMT(pkt)
// Now to get the elementary streams defined for this program.
streams, err := Streams(pkt)
if err != nil {
return i, nil, meta, errors.Wrap(err, "could not get streams from PMT")
}
return i, streams, meta, nil
}
// FillPayload takes a channel and fills the packets Payload field until the
@ -610,19 +647,6 @@ func Programs(p []byte) (map[uint16]uint16, error) {
}
// Streams returns elementary streams defined in a given MPEG-TS PMT packet.
// A gotspsi.PmtElementaryStream will give stream type from
// gotspsi.PmtElementaryStream.StreamType() and PID from
// gotspsi.PmtElementaryStream.ElementaryPid().
//
// PmtStreamTypes from gots/psi are defined as follows:
// PmtStreamTypeMpeg2VideoH262 uint8 = 2 // H262
// PmtStreamTypeMpeg4Video uint8 = 27 // H264
// PmtStreamTypeMpeg4VideoH264 uint8 = 27 // H264
// PmtStreamTypeMpeg4VideoH265 uint8 = 36 // H265
// PmtStreamTypeAac uint8 = 15 // AAC
// PmtStreamTypeAc3 uint8 = 129 // DD
// PmtStreamTypeEc3 uint8 = 135 // DD+
// PmtStreamTypeScte35 uint8 = 134 // SCTE-35
func Streams(p []byte) ([]gotspsi.PmtElementaryStream, error) {
pmt, err := gotspsi.NewPMT(p)
if err != nil {
@ -636,20 +660,6 @@ func Streams(p []byte) ([]gotspsi.PmtElementaryStream, error) {
// after. We also assume that this MPEG-TS stream contains just one program,
// but this program may contain different streams, i.e. a video stream + audio
// stream.
//
// A gotspsi.PmtElementaryStream will give stream type from
// gotspsi.PmtElementaryStream.StreamType() and PID from
// gotspsi.PmtElementaryStream.ElementaryPid().
//
// PmtStreamTypes from gots/psi are defined as follows:
// PmtStreamTypeMpeg2VideoH262 uint8 = 2 // H262
// PmtStreamTypeMpeg4Video uint8 = 27 // H264
// PmtStreamTypeMpeg4VideoH264 uint8 = 27 // H264
// PmtStreamTypeMpeg4VideoH265 uint8 = 36 // H265
// PmtStreamTypeAac uint8 = 15 // AAC
// PmtStreamTypeAc3 uint8 = 129 // DD
// PmtStreamTypeEc3 uint8 = 135 // DD+
// PmtStreamTypeScte35 uint8 = 134 // SCTE-35
func MediaStreams(p []byte) ([]gotspsi.PmtElementaryStream, error) {
pat := p[:PacketSize]
pmt := p[PacketSize : 2*PacketSize]
@ -664,18 +674,14 @@ func MediaStreams(p []byte) ([]gotspsi.PmtElementaryStream, error) {
}
if len(m) == 0 {
return nil, errors.New("no programs contained in PAT")
return nil, ErrNoPrograms
}
if len(m) > 1 {
return nil, errors.New("more than one program not yet supported")
return nil, ErrMultiplePrograms
}
var v uint16
for _, v = range m {
}
if pid(pmt) != v {
if pid(pmt) != pmtPIDs(m)[0] {
return nil, errors.New("second packet is not desired PMT")
}
@ -685,3 +691,13 @@ func MediaStreams(p []byte) ([]gotspsi.PmtElementaryStream, error) {
}
return s, nil
}
// pmtPIDs returns PMT PIDS from a map containing program number as keys and
// corresponding PMT PIDs as values.
func pmtPIDs(m map[uint16]uint16) []uint16 {
r := make([]uint16, 0, len(m))
for _, v := range m {
r = append(r, v)
}
return r
}

View File

@ -493,7 +493,7 @@ func TestSegmentForMeta(t *testing.T) {
}
// Now test IndexPid.
i, m, err := IndexPid(clip.Bytes(), PatPid, PmtPid)
i, _, m, err := FindPSI(clip.Bytes())
if err != nil {
t.Fatalf("IndexPid failed with error: %v", err)
}
@ -507,7 +507,7 @@ func TestSegmentForMeta(t *testing.T) {
// Finally, test IndexPid error handling.
for _, d := range [][]byte{[]byte{}, make([]byte, PacketSize/2), make([]byte, PacketSize)} {
_, _, err := IndexPid(d, PatPid, PmtPid)
_, _, _, err := FindPSI(d)
if err == nil {
t.Fatalf("IndexPid expected error")
}