2019-06-11 18:47:14 +03:00
|
|
|
package mts
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/Comcast/gots/packet"
|
|
|
|
"github.com/Comcast/gots/pes"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Extract extracts the media from an MPEG-TS clip given by p.
|
|
|
|
func Extract(p []byte) (Clip, error) {
|
|
|
|
l := len(p)
|
|
|
|
// Check that clip is divisible by 188, i.e. contains a series of full MPEG-TS clips.
|
|
|
|
if l%PacketSize != 0 {
|
|
|
|
return nil, errors.New("MTS clip is not of valid size")
|
|
|
|
}
|
|
|
|
|
2019-06-13 09:22:25 +03:00
|
|
|
// This will hold a copy of all the media in the MPEG-TS clip.
|
2019-06-11 18:47:14 +03:00
|
|
|
buf := make([]byte, 0, l/PacketSize)
|
|
|
|
|
2019-06-13 09:22:25 +03:00
|
|
|
var (
|
|
|
|
clip Clip // The data that will be returned.
|
|
|
|
meta map[string]string // Holds the most recently extracted meta.
|
|
|
|
lenOfFrame int // Len of current frame.
|
|
|
|
dataLen int // Len of data from MPEG-TS packet.
|
|
|
|
curPTS uint64 // Holds the current PTS.
|
|
|
|
curStreamID uint8 // Holds current StreamID (shouldn't change)
|
|
|
|
firstPUSI = true // Indicates that we have not yet received a PUSI.
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
// Go through the MPEGT-TS packets.
|
2019-06-11 18:47:14 +03:00
|
|
|
var pkt packet.Packet
|
|
|
|
for i := 0; i < l; i += PacketSize {
|
2019-06-13 09:22:25 +03:00
|
|
|
// We will use comcast/gots Packet type, so copy in.
|
2019-06-11 18:47:14 +03:00
|
|
|
copy(pkt[:], p[i:i+PacketSize])
|
|
|
|
|
|
|
|
switch pkt.PID() {
|
|
|
|
case PatPid: // Do nothing.
|
|
|
|
case PmtPid:
|
|
|
|
meta, err = ExtractMeta(pkt[:])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-13 09:22:25 +03:00
|
|
|
default: // Must be media.
|
|
|
|
// Get the MPEG-TS payload.
|
2019-06-11 18:47:14 +03:00
|
|
|
payload, err := pkt.Payload()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// If PUSI is true then we know it's the start of a new frame, and we have
|
|
|
|
// a PES header in the MTS payload.
|
2019-06-13 09:22:25 +03:00
|
|
|
|
2019-06-11 18:47:14 +03:00
|
|
|
if pkt.PayloadUnitStartIndicator() {
|
|
|
|
_pes, err := pes.NewPESHeader(payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-06-13 09:22:25 +03:00
|
|
|
// Extract the PTS and ID, then add a new frame to the clip.
|
|
|
|
curPTS = _pes.PTS()
|
|
|
|
curStreamID = _pes.StreamId()
|
|
|
|
clip = append(clip, Frame{
|
|
|
|
PTS: curPTS,
|
|
|
|
ID: curStreamID,
|
|
|
|
Meta: meta,
|
|
|
|
})
|
|
|
|
|
|
|
|
// Append the data to the underlying buffer and get appended lenghth.
|
|
|
|
buf = append(buf, _pes.Data()...)
|
|
|
|
dataLen = len(_pes.Data())
|
2019-06-11 18:47:14 +03:00
|
|
|
|
2019-06-13 09:22:25 +03:00
|
|
|
// If we haven't hit the first PUSI, then we know we have a full frame
|
|
|
|
// and can add this data to the frame pertaining to the finish frame.
|
|
|
|
if !firstPUSI {
|
|
|
|
clip[len(clip)-2].Media = buf[:lenOfFrame]
|
|
|
|
buf = buf[lenOfFrame:]
|
|
|
|
lenOfFrame = 0
|
2019-06-11 18:47:14 +03:00
|
|
|
}
|
2019-06-13 09:22:25 +03:00
|
|
|
firstPUSI = false
|
2019-06-11 18:47:14 +03:00
|
|
|
} else {
|
2019-06-13 09:22:25 +03:00
|
|
|
// We're not at the start of the frame, so we don't have a PES header.
|
|
|
|
// We can append the MPEG-TS data directly to the underlying buf.
|
|
|
|
dataLen = len(payload)
|
2019-06-11 18:47:14 +03:00
|
|
|
buf = append(buf, payload...)
|
|
|
|
}
|
2019-06-13 09:22:25 +03:00
|
|
|
lenOfFrame += dataLen
|
2019-06-11 18:47:14 +03:00
|
|
|
}
|
|
|
|
}
|
2019-06-13 09:22:25 +03:00
|
|
|
// We're finished up with media frames, so give the final Frame it's data.
|
|
|
|
clip[len(clip)-1].Media = buf[:lenOfFrame]
|
|
|
|
return clip, nil
|
2019-06-11 18:47:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Clip represents a clip of media, i.e. a sequence of media frames.
|
|
|
|
type Clip []Frame
|
|
|
|
|
|
|
|
// Frame describes a media frame that may be extracted from a PES packet.
|
|
|
|
type Frame struct {
|
|
|
|
Media []byte // Contains the media from the frame.
|
|
|
|
PTS uint64 // PTS from PES packet (this gives time relative from start of stream).
|
2019-06-13 09:22:25 +03:00
|
|
|
ID uint8 // StreamID from the PES packet, identifying media codec.
|
2019-06-11 18:47:14 +03:00
|
|
|
Meta map[string]string // Contains metadata from PMT relevant to this frame.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bytes returns the concatentated media bytes from each frame in the Clip c.
|
|
|
|
func (c *Clip) Bytes() []byte {
|
|
|
|
return nil
|
|
|
|
}
|