mirror of https://bitbucket.org/ausocean/av.git
container/mts: wrote function TrimToMtsRange and added related testing
This function will return a sub slice of MPEG-TS corresponding to an interval of metadata. Also wrote testing for this function.
This commit is contained in:
parent
513e9d06ff
commit
1a233d8576
|
@ -31,7 +31,6 @@ package mts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"bitbucket.org/ausocean/av/container/mts/meta"
|
"bitbucket.org/ausocean/av/container/mts/meta"
|
||||||
"bitbucket.org/ausocean/av/container/mts/psi"
|
"bitbucket.org/ausocean/av/container/mts/psi"
|
||||||
|
@ -175,11 +174,16 @@ func FindPat(d []byte) ([]byte, int, error) {
|
||||||
return FindPid(d, PatPid)
|
return FindPid(d, PatPid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
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
|
// 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.
|
// 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) {
|
func FindPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
|
||||||
if len(d) < PacketSize {
|
if len(d) < PacketSize {
|
||||||
return nil, -1, errors.New("MPEG-TS data not of valid length")
|
return nil, -1, errInvalidLen
|
||||||
}
|
}
|
||||||
for i = 0; i < len(d); i += PacketSize {
|
for i = 0; i < len(d); i += PacketSize {
|
||||||
p := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2])
|
p := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2])
|
||||||
|
@ -188,7 +192,7 @@ func FindPid(d []byte, pid uint16) (pkt []byte, i int, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, -1, fmt.Errorf("could not find packet with pid: %d", pid)
|
return nil, -1, errCouldNotFind
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillPayload takes a channel and fills the packets Payload field until the
|
// FillPayload takes a channel and fills the packets Payload field until the
|
||||||
|
@ -363,6 +367,8 @@ func GetPTSRange(clip []byte, pid uint16) (pts [2]uint64, err error) {
|
||||||
return [2]uint64{}, errors.New("could only find one access unit in mpegts clip")
|
return [2]uint64{}, errors.New("could only find one access unit in mpegts clip")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errNoMeta = errors.New("PMT does not contain meta")
|
||||||
|
|
||||||
// ExtractMeta returns a map of metadata from the first PMT's metaData
|
// ExtractMeta returns a map of metadata from the first PMT's metaData
|
||||||
// descriptor, that is found in the MPEG-TS clip d. d must contain a series of
|
// descriptor, that is found in the MPEG-TS clip d. d must contain a series of
|
||||||
// complete MPEG-TS packets.
|
// complete MPEG-TS packets.
|
||||||
|
@ -377,10 +383,62 @@ func ExtractMeta(d []byte) (map[string]string, error) {
|
||||||
_, metaDescriptor := pmt.HasDescriptor(psi.MetadataTag)
|
_, metaDescriptor := pmt.HasDescriptor(psi.MetadataTag)
|
||||||
|
|
||||||
if metaDescriptor == nil {
|
if metaDescriptor == nil {
|
||||||
return nil, errors.New("PMT does not have descriptor")
|
return nil, errNoMeta
|
||||||
}
|
}
|
||||||
// Skip the descriptor head.
|
// Skip the descriptor head.
|
||||||
m := metaDescriptor[2:]
|
m := metaDescriptor[2:]
|
||||||
|
|
||||||
return meta.GetAllAsMap(m)
|
return meta.GetAllAsMap(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TrimToMetaRange trims a slice of MPEG-TS to a segment between two points of
|
||||||
|
// meta data described by key, from and to.
|
||||||
|
func TrimToMetaRange(d []byte, key, from, to string) ([]byte, error) {
|
||||||
|
if len(d)%PacketSize != 0 {
|
||||||
|
return nil, errors.New("MTS clip is not of valid size")
|
||||||
|
}
|
||||||
|
|
||||||
|
if from == to {
|
||||||
|
return nil, errors.New("'from' and 'to' cannot be equivalent")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
start = -1 // Index of the start of the segment in d.
|
||||||
|
end = -1 // Index of the end of segment in d.
|
||||||
|
off int // Index of remaining slice of d to check after each PMT found.
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Find the next PMT.
|
||||||
|
pmt, idx, err := FindPid(d[off:], PmtPid)
|
||||||
|
if err != nil {
|
||||||
|
switch -1 {
|
||||||
|
case start:
|
||||||
|
return nil, errMetaLowerBound
|
||||||
|
case end:
|
||||||
|
return nil, errMetaUpperBound
|
||||||
|
default:
|
||||||
|
panic("should not have got error from FindPid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off += idx + PacketSize
|
||||||
|
|
||||||
|
meta, err := ExtractMeta(pmt)
|
||||||
|
switch err {
|
||||||
|
case nil: // do nothing
|
||||||
|
case errNoMeta:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if start == -1 {
|
||||||
|
if meta[key] == from {
|
||||||
|
start = off - PacketSize
|
||||||
|
}
|
||||||
|
} else if meta[key] == to {
|
||||||
|
end = off
|
||||||
|
return d[start:end], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,9 +30,11 @@ package mts
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"bitbucket.org/ausocean/av/container/mts/meta"
|
||||||
"bitbucket.org/ausocean/av/container/mts/pes"
|
"bitbucket.org/ausocean/av/container/mts/pes"
|
||||||
"bitbucket.org/ausocean/av/container/mts/psi"
|
"bitbucket.org/ausocean/av/container/mts/psi"
|
||||||
"github.com/Comcast/gots/packet"
|
"github.com/Comcast/gots/packet"
|
||||||
|
@ -264,3 +266,71 @@ func TestFindPid(t *testing.T) {
|
||||||
t.Errorf("index of found packet is not correct.\nGot: %v, want: %v\n", _got, targetPacketNum)
|
t.Errorf("index of found packet is not correct.\nGot: %v, want: %v\n", _got, targetPacketNum)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTrimToMetaRange checks that TrimToMetaRange can correctly return a segment
|
||||||
|
// of MPEG-TS corresponding to a meta interval in a slice of MPEG-TS.
|
||||||
|
func TestTrimToMetaRange(t *testing.T) {
|
||||||
|
Meta = meta.New()
|
||||||
|
|
||||||
|
const (
|
||||||
|
nPSI = 10
|
||||||
|
key = "n"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clip bytes.Buffer
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := 0; i < nPSI; i++ {
|
||||||
|
Meta.Add(key, strconv.Itoa((i*2)+1))
|
||||||
|
err := writePSIWithMeta(&clip)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("did not expect to get error writing PSI, error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
from string
|
||||||
|
to string
|
||||||
|
expect []byte
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
from: "3",
|
||||||
|
to: "9",
|
||||||
|
expect: clip.Bytes()[3*PacketSize : 10*PacketSize],
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "30",
|
||||||
|
to: "8",
|
||||||
|
expect: nil,
|
||||||
|
err: errMetaLowerBound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "3",
|
||||||
|
to: "30",
|
||||||
|
expect: nil,
|
||||||
|
err: errMetaUpperBound,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run tests.
|
||||||
|
for i, test := range tests {
|
||||||
|
got, err := TrimToMetaRange(clip.Bytes(), key, test.from, test.to)
|
||||||
|
|
||||||
|
// First check the error.
|
||||||
|
if err != nil && err != test.err {
|
||||||
|
t.Errorf("unexpected error: %v for test: %v", err, i)
|
||||||
|
continue
|
||||||
|
} else if err != test.err {
|
||||||
|
t.Errorf("expected to get error: %v for test: %v", test.err, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check data.
|
||||||
|
if test.err == nil && !bytes.Equal(test.expect, got) {
|
||||||
|
t.Errorf("did not get expected data for test: %v\n Got: %v\n, Want: %v\n", i, got, test.expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -318,7 +318,7 @@ func TestTrimToPTSRange(t *testing.T) {
|
||||||
|
|
||||||
// TestTrimToMetaRange checks that Clip.TrimToMetaRange correctly provides a
|
// TestTrimToMetaRange checks that Clip.TrimToMetaRange correctly provides a
|
||||||
// sub Clip for a given meta range.
|
// sub Clip for a given meta range.
|
||||||
func TestTrimToMetaRange(t *testing.T) {
|
func TestClipTrimToMetaRange(t *testing.T) {
|
||||||
const (
|
const (
|
||||||
numOfTestFrames = 10
|
numOfTestFrames = 10
|
||||||
ptsInterval = 4
|
ptsInterval = 4
|
||||||
|
|
Loading…
Reference in New Issue