Merged in nal-extension-types (pull request #216)

codec/h264/h264dec: NAL unit extension types and clean up
This commit is contained in:
Saxon Milton 2019-07-29 04:41:00 +00:00
commit 939fd4853e
4 changed files with 282 additions and 150 deletions

View File

@ -19,6 +19,8 @@ const (
naluTypePrefixNALU
naluTypeSubsetSPS
naluTypeDepthParamSet
naluTypeSliceLayerExtRBSP = 20
naluTypeSliceLayerExtRBSP2 = 21
)
var (

View File

@ -1,161 +1,291 @@
/*
DESCRIPTION
nalunit.go provides structures for a NAL unit as well as it's extensions.
AUTHORS
Saxon Nelson-Milton <saxon@ausocean.org>, The Australian Ocean Laboratory (AusOcean)
mrmod <mcmoranbjr@gmail.com>
*/
package h264dec
import (
"bitbucket.org/ausocean/av/codec/h264/h264dec/bits"
"fmt"
"io"
"github.com/pkg/errors"
"bitbucket.org/ausocean/av/codec/h264/h264dec/bits"
)
type NalUnit struct {
NumBytes int
ForbiddenZeroBit int
RefIdc int
Type int
SvcExtensionFlag int
Avc3dExtensionFlag int
IdrFlag int
PriorityId int
NoInterLayerPredFlag int
DependencyId int
QualityId int
TemporalId int
UseRefBasePicFlag int
DiscardableFlag int
OutputFlag int
ReservedThree2Bits int
HeaderBytes int
NonIdrFlag int
ViewId int
AnchorPicFlag int
InterViewFlag int
ReservedOneBit int
ViewIdx int
DepthFlag int
// MVCExtension describes a NAL unit header multiview video coding extension, as
// defined in section H.7.3.1.1 of the specifications.
// Semantics of fields are specified in section H.7.4.1.1.
type MVCExtension struct {
// non_idr_flag, if true indicates that access unit is not IDR.
NonIdrFlag bool
// priority_id, indicates priority of NAL unit. A lower value => higher priority.
PriorityID uint8
// view_id, specifies a view identifier for the unit. Units with identical
// view_id are in the same view.
ViewID uint32
// temporal_id, temporal identifier for the unit.
TemporalID uint8
// anchor_pic_flag, if true access unit is an anchor access unit.
AnchorPicFlag bool
// inter_view_flag, if false current view component not used for inter-view
// prediction elsewhere in access unit.
InterViewFlag bool
// reserved_one_bit, always 1 (ignored by decoders)
ReservedOneBit uint8
}
// NewMVCExtension parses a NAL unit header multiview video coding extension
// from br following the syntax structure specified in section H.7.3.1.1, and
// returns as a new MVCExtension.
func NewMVCExtension(br *bits.BitReader) (*MVCExtension, error) {
e := &MVCExtension{}
r := newFieldReader(br)
e.NonIdrFlag = r.readBits(1) == 1
e.PriorityID = uint8(r.readBits(6))
e.ViewID = uint32(r.readBits(10))
e.TemporalID = uint8(r.readBits(3))
e.AnchorPicFlag = r.readBits(1) == 1
e.InterViewFlag = r.readBits(1) == 1
e.ReservedOneBit = uint8(r.readBits(1))
if r.err() != nil {
return nil, fmt.Errorf("error from fieldReader: %v", r.err())
}
return e, nil
}
// ThreeDAVCExtension describes a NAL unit header 3D advanced video coding
// extension, as defined in section J.7.3.1.1 of the specifications.
// For field semantics see section J.7.4.1.1.
type ThreeDAVCExtension struct {
// view_idx, specifies the order index for the NAL i.e. view_id = view_id[view_idx].
ViewIdx uint8
// dpeth_flag, if true indicates NAL part of a depth view component, otherwise
// a texture view component.
DepthFlag bool
// non_idr_flag, if true indicates that access unit is not IDR.
NonIdrFlag bool
// temporal_id, temporal identifier for the unit.
TemporalID uint8
// anchor_pic_flag, if true access unit is an anchor access unit.
AnchorPicFlag bool
// inter_view_flag, if false current view component not used for inter-view
// prediction elsewhere in access unit.
InterViewFlag bool
}
// NewThreeDAVCExtension parses a NAL unit header 3D advanced video coding
// extension from br following the syntax structure specified in section
// J.7.3.1.1, and returns as a new ThreeDAVCExtension.
func NewThreeDAVCExtension(br *bits.BitReader) (*ThreeDAVCExtension, error) {
e := &ThreeDAVCExtension{}
r := newFieldReader(br)
e.ViewIdx = uint8(r.readBits(8))
e.DepthFlag = r.readBits(1) == 1
e.NonIdrFlag = r.readBits(1) == 1
e.TemporalID = uint8(r.readBits(3))
e.AnchorPicFlag = r.readBits(1) == 1
e.InterViewFlag = r.readBits(1) == 1
if r.err() != nil {
return nil, fmt.Errorf("error from fieldReader: %v", r.err())
}
return e, nil
}
// SVCExtension describes a NAL unit header scalable video coding extension, as
// defined in section G.7.3.1.1 of the specifications.
// For field semantics see section G.7.4.1.1.
type SVCExtension struct {
// idr_flag, if true the current coded picture is an IDR picture when
// dependency_id == max(dependency_id) in the coded picture.
IdrFlag bool
// priority_id, specifies priority identifier for unit.
PriorityID uint8
// no_inter_layer_pred_flag, if true inter-layer prediction can't be used for
// decoding slice.
NoInterLayerPredFlag bool
// dependency_id, specifies a dependency identifier for the NAL.
DependencyID uint8
// quality_id, specifies a quality identifier for the NAL.
QualityID uint8
// temporal_id, specifiesa temporal identifier for the NAL.
TemporalID uint8
// use_ref_base_pic_flag, if true indicates reference base pictures and
// decoded pictures are used as references for inter prediction.
UseRefBasePicFlag bool
// discardable_flag, if true, indicates current NAL is not used for decoding
// dependency representations that are part of the current coded picture or
// any subsequent coded picture in decoding order and have a greater
// dependency_id value than current NAL.
DiscardableFlag bool
// output_flag, affects the decoded picture output and removal processes as
// specified in Annex C.
OutputFlag bool
// reserved_three_2bits, equal to 3. Decoders ignore.
ReservedThree2Bits uint8
}
// NewSVCExtension parses a NAL unit header scalable video coding extension from
// br following the syntax structure specified in section G.7.3.1.1, and returns
// as a new SVCExtension.
func NewSVCExtension(br *bits.BitReader) (*SVCExtension, error) {
e := &SVCExtension{}
r := newFieldReader(br)
e.IdrFlag = r.readBits(1) == 1
e.PriorityID = uint8(r.readBits(6))
e.NoInterLayerPredFlag = r.readBits(1) == 1
e.DependencyID = uint8(r.readBits(3))
e.QualityID = uint8(r.readBits(4))
e.TemporalID = uint8(r.readBits(3))
e.UseRefBasePicFlag = r.readBits(1) == 1
e.DiscardableFlag = r.readBits(1) == 1
e.OutputFlag = r.readBits(1) == 1
e.ReservedThree2Bits = uint8(r.readBits(2))
if r.err() != nil {
return nil, fmt.Errorf("error from fieldReader: %v", r.err())
}
return e, nil
}
// NALUnit describes a network abstraction layer unit, as defined in section
// 7.3.1 of the specifications.
// Field semantics are defined in section 7.4.1.
type NALUnit struct {
// forbidden_zero_bit, always 0.
ForbiddenZeroBit uint8
// nal_ref_idc, if not 0 indicates content of NAL contains a sequence parameter
// set, a sequence parameter set extension, a subset sequence parameter set,
// a picture parameter set, a slice of a reference picture, a slice data
// partition of a reference picture, or a prefix NAL preceding a slice of
// a reference picture.
RefIdc uint8
// nal_unit_type, specifies the type of RBSP data contained in the NAL as
// defined in Table 7-1.
Type uint8
// svc_extension_flag, indicates whether a nal_unit_header_svc_extension()
// (G.7.3.1.1) or nal_unit_header_mvc_extension() (H.7.3.1.1) will follow next
// in the syntax structure.
SVCExtensionFlag bool
// avc_3d_extension_flag, for nal_unit_type = 21, indicates that a
// nal_unit_header_mvc_extension() (H.7.3.1.1) or
// nal_unit_header_3davc_extension() (J.7.3.1.1) will follow next in syntax
// structure.
AVC3DExtensionFlag bool
// nal_unit_header_svc_extension() as defined in section G.7.3.1.1.
SVCExtension *SVCExtension
// nal_unit_header_3davc_extension() as defined in section J.7.3.1.1
ThreeDAVCExtension *ThreeDAVCExtension
// nal_unit_header_mvc_extension() as defined in section H.7.3.1.1).
MVCExtension *MVCExtension
// emulation_prevention_three_byte, equal to 0x03 and is discarded by decoder.
EmulationPreventionThreeByte byte
rbsp []byte
// rbsp_byte, the raw byte sequence payload data for the NAL.
RBSP []byte
}
func NalUnitHeaderSvcExtension(nalUnit *NalUnit, br *bits.BitReader) error {
return readFields(br, []field{
{&nalUnit.IdrFlag, "IdrFlag", 1},
{&nalUnit.PriorityId, "PriorityId", 6},
{&nalUnit.NoInterLayerPredFlag, "NoInterLayerPredFlag", 1},
{&nalUnit.DependencyId, "DependencyId", 3},
{&nalUnit.QualityId, "QualityId", 4},
{&nalUnit.TemporalId, "TemporalId", 3},
{&nalUnit.UseRefBasePicFlag, "UseRefBasePicFlag", 1},
{&nalUnit.DiscardableFlag, "DiscardableFlag", 1},
{&nalUnit.OutputFlag, "OutputFlag", 1},
{&nalUnit.ReservedThree2Bits, "ReservedThree2Bits", 2},
})
}
// NewNALUnit parses a network abstraction layer unit from br following the
// syntax structure specified in section 7.3.1, and returns as a new NALUnit.
func NewNALUnit(br *bits.BitReader) (*NALUnit, error) {
n := &NALUnit{}
r := newFieldReader(br)
func NalUnitHeader3davcExtension(nalUnit *NalUnit, br *bits.BitReader) error {
return readFields(br, []field{
{&nalUnit.ViewIdx, "ViewIdx", 8},
{&nalUnit.DepthFlag, "DepthFlag", 1},
{&nalUnit.NonIdrFlag, "NonIdrFlag", 1},
{&nalUnit.TemporalId, "TemporalId", 3},
{&nalUnit.AnchorPicFlag, "AnchorPicFlag", 1},
{&nalUnit.InterViewFlag, "InterViewFlag", 1},
})
}
n.ForbiddenZeroBit = uint8(r.readBits(1))
n.RefIdc = uint8(r.readBits(2))
n.Type = uint8(r.readBits(5))
func NalUnitHeaderMvcExtension(nalUnit *NalUnit, br *bits.BitReader) error {
return readFields(br, []field{
{&nalUnit.NonIdrFlag, "NonIdrFlag", 1},
{&nalUnit.PriorityId, "PriorityId", 6},
{&nalUnit.ViewId, "ViewId", 10},
{&nalUnit.TemporalId, "TemporalId", 3},
{&nalUnit.AnchorPicFlag, "AnchorPicFlag", 1},
{&nalUnit.InterViewFlag, "InterViewFlag", 1},
{&nalUnit.ReservedOneBit, "ReservedOneBit", 1},
})
}
func (n *NalUnit) RBSP() []byte {
return n.rbsp
}
func NewNalUnit(frame []byte, numBytesInNal int) (*NalUnit, error) {
logger.Printf("debug: reading %d byte NAL\n", numBytesInNal)
nalUnit := NalUnit{
NumBytes: numBytesInNal,
HeaderBytes: 1,
}
// TODO: pass in actual io.Reader to NewBitReader
br := bits.NewBitReader(nil)
err := readFields(br, []field{
{&nalUnit.ForbiddenZeroBit, "ForbiddenZeroBit", 1},
{&nalUnit.RefIdc, "NalRefIdc", 2},
{&nalUnit.Type, "NalUnitType", 5},
})
if err != nil {
return nil, err
}
if nalUnit.Type == 14 || nalUnit.Type == 20 || nalUnit.Type == 21 {
if nalUnit.Type != 21 {
b, err := br.ReadBits(1)
if err != nil {
return nil, errors.Wrap(err, "could not read SvcExtensionFlag")
}
nalUnit.SvcExtensionFlag = int(b)
var err error
if n.Type == naluTypePrefixNALU || n.Type == naluTypeSliceLayerExtRBSP || n.Type == naluTypeSliceLayerExtRBSP2 {
if n.Type != naluTypeSliceLayerExtRBSP2 {
n.SVCExtensionFlag = r.readBits(1) == 1
} else {
b, err := br.ReadBits(1)
if err != nil {
return nil, errors.Wrap(err, "could not read Avc3dExtensionFlag")
}
nalUnit.Avc3dExtensionFlag = int(b)
n.AVC3DExtensionFlag = r.readBits(1) == 1
}
if nalUnit.SvcExtensionFlag == 1 {
NalUnitHeaderSvcExtension(&nalUnit, br)
nalUnit.HeaderBytes += 3
} else if nalUnit.Avc3dExtensionFlag == 1 {
NalUnitHeader3davcExtension(&nalUnit, br)
nalUnit.HeaderBytes += 2
if n.SVCExtensionFlag {
n.SVCExtension, err = NewSVCExtension(br)
if err != nil {
return nil, errors.Wrap(err, "could not parse SVCExtension")
}
} else if n.AVC3DExtensionFlag {
n.ThreeDAVCExtension, err = NewThreeDAVCExtension(br)
if err != nil {
return nil, errors.Wrap(err, "could not parse ThreeDAVCExtension")
}
} else {
NalUnitHeaderMvcExtension(&nalUnit, br)
nalUnit.HeaderBytes += 3
n.MVCExtension, err = NewMVCExtension(br)
if err != nil {
return nil, errors.Wrap(err, "could not parse MVCExtension")
}
}
}
logger.Printf("debug: found %d byte header. Reading body\n", nalUnit.HeaderBytes)
for i := nalUnit.HeaderBytes; i < nalUnit.NumBytes; i++ {
for moreRBSPData(br) {
next3Bytes, err := br.PeekBits(24)
if err != nil {
logger.Printf("error: while reading next 3 NAL bytes: %v\n", err)
break
// If PeekBits cannot get 3 bytes, but there still might be 2 bytes left in
// the source, we will get an io.EOF; we wish to ignore this and continue.
// The call to moreRBSPData will determine when we have reached the end of
// the NAL unit.
if err != nil && errors.Cause(err) != io.EOF {
return nil, errors.Wrap(err, "could not Peek next 3 bytes")
}
// Little odd, the err above and the i+2 check might be synonyms
if i+2 < nalUnit.NumBytes && next3Bytes == 0x000003 {
if next3Bytes == 0x000003 {
for j := 0; j < 2; j++ {
rbspByte, err := br.ReadBits(8)
if err != nil {
return nil, errors.Wrap(err, "could not read rbspByte")
}
nalUnit.rbsp = append(nalUnit.rbsp, byte(rbspByte))
rbspByte := byte(r.readBits(8))
n.RBSP = append(n.RBSP, byte(rbspByte))
}
i += 2
// Read Emulation prevention three byte.
eptByte, err := br.ReadBits(8)
if err != nil {
return nil, errors.Wrap(err, "could not read eptByte")
}
nalUnit.EmulationPreventionThreeByte = byte(eptByte)
n.EmulationPreventionThreeByte = byte(r.readBits(8))
} else {
if b, err := br.ReadBits(8); err == nil {
nalUnit.rbsp = append(nalUnit.rbsp, byte(b))
} else {
logger.Printf("error: while reading byte %d of %d nal bytes: %v\n", i, nalUnit.NumBytes, err)
break
}
n.RBSP = append(n.RBSP, byte(r.readBits(8)))
}
}
// nalUnit.rbsp = frame[nalUnit.HeaderBytes:]
logger.Printf("info: decoded %s NAL with %d RBSP bytes\n", NALUnitType[nalUnit.Type], len(nalUnit.rbsp))
return &nalUnit, nil
if r.err() != nil {
return nil, fmt.Errorf("fieldReader error: %v", r.err())
}
return n, nil
}

View File

@ -63,7 +63,7 @@ func (h *H264Reader) Start() {
switch nalUnit.Type {
case naluTypeSPS:
// TODO: handle this error
sps, _ := NewSPS(nalUnit.rbsp, false)
sps, _ := NewSPS(nalUnit.RBSP, false)
h.VideoStreams = append(
h.VideoStreams,
&VideoStream{SPS: sps},
@ -71,20 +71,20 @@ func (h *H264Reader) Start() {
case naluTypePPS:
videoStream := h.VideoStreams[len(h.VideoStreams)-1]
// TODO: handle this error
videoStream.PPS, _ = NewPPS(videoStream.SPS, nalUnit.RBSP(), false)
videoStream.PPS, _ = NewPPS(videoStream.SPS, nalUnit.RBSP, false)
case naluTypeSliceIDRPicture:
fallthrough
case naluTypeSliceNonIDRPicture:
videoStream := h.VideoStreams[len(h.VideoStreams)-1]
logger.Printf("info: frame number %d\n", len(videoStream.Slices))
// TODO: handle this error
sliceContext, _ := NewSliceContext(videoStream, nalUnit, nalUnit.RBSP(), true)
sliceContext, _ := NewSliceContext(videoStream, nalUnit, nalUnit.RBSP, true)
videoStream.Slices = append(videoStream.Slices, sliceContext)
}
}
}
func (r *H264Reader) readNalUnit() (*NalUnit, *bits.BitReader, error) {
func (r *H264Reader) readNalUnit() (*NALUnit, *bits.BitReader, error) {
// Read to start of NAL
logger.Printf("debug: Seeking NAL %d start\n", len(r.NalUnits))
@ -131,7 +131,7 @@ func (r *H264Reader) readNalUnit() (*NalUnit, *bits.BitReader, error) {
r.NalUnits = append(r.NalUnits, nalUnitReader)
// TODO: this should really take an io.Reader rather than []byte. Need to fix nil
// once this is fixed.
nalUnit, err := NewNalUnit(nil, 0)
nalUnit, err := NewNALUnit(nil)
if err != nil {
return nil, nil, errors.Wrap(err, "cannot create new nal unit")
}

View File

@ -23,7 +23,7 @@ type VideoStream struct {
Slices []*SliceContext
}
type SliceContext struct {
*NalUnit
*NALUnit
*SPS
*PPS
*Slice
@ -240,12 +240,12 @@ func CodedBlockPatternChroma(data *SliceData) int {
// dependencyId see Annex G.8.8.1
// Also G7.3.1.1 nal_unit_header_svc_extension
func DQId(nalUnit *NalUnit) int {
return (nalUnit.DependencyId << 4) + nalUnit.QualityId
func DQId(nalUnit *NALUnit) int {
return int((nalUnit.SVCExtension.DependencyID << 4)) + int(nalUnit.SVCExtension.QualityID)
}
// Annex G p527
func NumMbPart(nalUnit *NalUnit, sps *SPS, header *SliceHeader, data *SliceData) int {
func NumMbPart(nalUnit *NALUnit, sps *SPS, header *SliceHeader, data *SliceData) int {
sliceType := sliceTypeMap[header.SliceType]
numMbPart := 0
if MbTypeName(sliceType, CurrMbAddr(sps, header)) == "B_SKIP" || MbTypeName(sliceType, CurrMbAddr(sps, header)) == "B_Direct_16x16" {
@ -381,7 +381,7 @@ func MbPred(sliceContext *SliceContext, br *bits.BitReader, rbsp []byte) error {
}
} else if mbPartPredMode != direct {
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NalUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NALUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
sliceContext.Update(sliceContext.Slice.Header, sliceContext.Slice.Data)
m, err := MbPartPredMode(sliceContext.Slice.Data, sliceType, sliceContext.Slice.Data.MbType, mbPartIdx)
if err != nil {
@ -417,7 +417,7 @@ func MbPred(sliceContext *SliceContext, br *bits.BitReader, rbsp []byte) error {
}
}
}
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NalUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NALUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
m, err := MbPartPredMode(sliceContext.Slice.Data, sliceType, sliceContext.Slice.Data.MbType, mbPartIdx)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("could not get mbPartPredMode for loop 2 mbPartIdx: %d", mbPartIdx))
@ -456,7 +456,7 @@ func MbPred(sliceContext *SliceContext, br *bits.BitReader, rbsp []byte) error {
}
}
}
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NalUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
for mbPartIdx := 0; mbPartIdx < NumMbPart(sliceContext.NALUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data); mbPartIdx++ {
sliceContext.Update(sliceContext.Slice.Header, sliceContext.Slice.Data)
m, err := MbPartPredMode(sliceContext.Slice.Data, sliceType, sliceContext.Slice.Data.MbType, mbPartIdx)
if err != nil {
@ -815,7 +815,7 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e
if err != nil {
return nil, errors.Wrap(err, "could not get mbPartPredMode")
}
if sliceContext.Slice.Data.MbTypeName == "I_NxN" && m != intra16x16 && NumMbPart(sliceContext.NalUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data) == 4 {
if sliceContext.Slice.Data.MbTypeName == "I_NxN" && m != intra16x16 && NumMbPart(sliceContext.NALUnit, sliceContext.SPS, sliceContext.Slice.Header, sliceContext.Slice.Data) == 4 {
logger.Printf("\tTODO: subMbPred\n")
/*
subMbType := SubMbPred(sliceContext.Slice.Data.MbType)
@ -945,11 +945,11 @@ func NewSliceData(sliceContext *SliceContext, br *bits.BitReader) (*SliceData, e
func (c *SliceContext) Update(header *SliceHeader, data *SliceData) {
c.Slice = &Slice{Header: header, Data: data}
}
func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, showPacket bool) (*SliceContext, error) {
func NewSliceContext(videoStream *VideoStream, nalUnit *NALUnit, rbsp []byte, showPacket bool) (*SliceContext, error) {
var err error
sps := videoStream.SPS
pps := videoStream.PPS
logger.Printf("debug: %s RBSP %d bytes %d bits == \n", NALUnitType[nalUnit.Type], len(rbsp), len(rbsp)*8)
logger.Printf("debug: %s RBSP %d bytes %d bits == \n", NALUnitType[int(nalUnit.Type)], len(rbsp), len(rbsp)*8)
logger.Printf("debug: \t%#v\n", rbsp[0:8])
var idrPic bool
if nalUnit.Type == 5 {
@ -974,7 +974,7 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh
}
sliceType := sliceTypeMap[header.SliceType]
logger.Printf("debug: %s (%s) slice of %d bytes\n", NALUnitType[nalUnit.Type], sliceType, len(rbsp))
logger.Printf("debug: %s (%s) slice of %d bytes\n", NALUnitType[int(nalUnit.Type)], sliceType, len(rbsp))
header.PPSID, err = readUe(br)
if err != nil {
return nil, errors.Wrap(err, "could not parse PPSID")
@ -1350,7 +1350,7 @@ func NewSliceContext(videoStream *VideoStream, nalUnit *NalUnit, rbsp []byte, sh
}
sliceContext := &SliceContext{
NalUnit: nalUnit,
NALUnit: nalUnit,
SPS: sps,
PPS: pps,
Slice: &Slice{