mkv in progress
This commit is contained in:
parent
e0fc50b09e
commit
13fad59f2c
147
format/mkv/demuxer.go
Normal file
147
format/mkv/demuxer.go
Normal file
@ -0,0 +1,147 @@
|
||||
package mkv
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/codec/h264parser"
|
||||
"github.com/deepch/vdk/format/mkv/mkvio"
|
||||
)
|
||||
|
||||
type Demuxer struct {
|
||||
r *mkvio.Document
|
||||
pkts []av.Packet
|
||||
sps []byte
|
||||
pps []byte
|
||||
streams []*Stream
|
||||
ps uint32
|
||||
stage int
|
||||
fc int
|
||||
ls time.Duration
|
||||
}
|
||||
|
||||
func NewDemuxer(r io.Reader) *Demuxer {
|
||||
|
||||
return &Demuxer{
|
||||
r: mkvio.InitDocument(r),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (self *Demuxer) Streams() (streams []av.CodecData, err error) {
|
||||
|
||||
if err = self.probe(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, stream := range self.streams {
|
||||
streams = append(streams, stream.CodecData)
|
||||
}
|
||||
|
||||
if len(streams) == 0 {
|
||||
return nil, errors.New("streams not found")
|
||||
}
|
||||
|
||||
return streams, err
|
||||
|
||||
}
|
||||
|
||||
func (self *Demuxer) probe() (err error) {
|
||||
if self.stage == 0 {
|
||||
|
||||
var el *mkvio.Element
|
||||
el, err = self.r.GetVideoCodec()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if el.ElementRegister.ID == mkvio.ElementCodecPrivate.ID {
|
||||
payload := el.Content[6:]
|
||||
var reader int
|
||||
for pos := 0; pos < len(payload); pos = reader {
|
||||
lens := int(binary.BigEndian.Uint16(payload[reader:]))
|
||||
reader += 2
|
||||
nal := payload[reader : reader+lens]
|
||||
naluType := nal[0] & 0x1f
|
||||
switch naluType {
|
||||
case h264parser.NALU_SPS:
|
||||
self.sps = nal
|
||||
case h264parser.NALU_PPS:
|
||||
self.pps = nal
|
||||
}
|
||||
reader += lens
|
||||
reader++
|
||||
}
|
||||
}
|
||||
|
||||
if len(self.sps) > 0 && len(self.pps) > 0 {
|
||||
var codec av.CodecData
|
||||
codec, err = h264parser.NewCodecDataFromSPSAndPPS(self.sps, self.pps)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stream := &Stream{}
|
||||
stream.idx = 0
|
||||
stream.demuxer = self
|
||||
stream.CodecData = codec
|
||||
self.streams = append(self.streams, stream)
|
||||
|
||||
}
|
||||
self.stage++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
|
||||
|
||||
var el mkvio.Element
|
||||
|
||||
for {
|
||||
el, err = self.r.ParseElement()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if el.Type == 6 && el.ElementRegister.ID == mkvio.ElementSimpleBlock.ID {
|
||||
self.fc++
|
||||
nals, _ := h264parser.SplitNALUs(el.Content[4:])
|
||||
for _, nal := range nals {
|
||||
|
||||
naluType := nal[0] & 0x1f
|
||||
|
||||
if naluType == 5 {
|
||||
l1 := int(binary.BigEndian.Uint16(el.Content[2:4]))
|
||||
dur := time.Duration(uint32(l1)) * time.Millisecond
|
||||
self.ls += time.Duration(uint32(l1)) * time.Millisecond
|
||||
self.ps = 0
|
||||
pkt = av.Packet{IsKeyFrame: true, Idx: 0, Duration: dur, Time: self.ls, Data: append(binSize(len(nal)), nal...)}
|
||||
return
|
||||
|
||||
} else if naluType == 1 {
|
||||
|
||||
l1 := int(binary.BigEndian.Uint16(el.Content[1:3]))
|
||||
dur := time.Duration(uint32(l1)-self.ps) * time.Millisecond
|
||||
self.ls += time.Duration(uint32(l1)-self.ps) * time.Millisecond
|
||||
self.ps = uint32(l1)
|
||||
pkt = av.Packet{Idx: 0, Duration: dur, Time: self.ls, Data: append(binSize(len(nal)), nal...)}
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func binSize(val int) []byte {
|
||||
buf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(buf, uint32(val))
|
||||
return buf
|
||||
}
|
29
format/mkv/handler.go
Normal file
29
format/mkv/handler.go
Normal file
@ -0,0 +1,29 @@
|
||||
package mkv
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
"github.com/deepch/vdk/av/avutil"
|
||||
)
|
||||
|
||||
var CodecTypes = []av.CodecType{av.H264, av.AAC}
|
||||
|
||||
func Handler(h *avutil.RegisterHandler) {
|
||||
h.Ext = ".mkv"
|
||||
|
||||
h.Probe = func(b []byte) bool {
|
||||
return b[0] == 0x47 && b[188] == 0x47
|
||||
}
|
||||
|
||||
h.ReaderDemuxer = func(r io.Reader) av.Demuxer {
|
||||
return NewDemuxer(r)
|
||||
}
|
||||
|
||||
h.WriterMuxer = func(w io.Writer) av.Muxer {
|
||||
//return NewMuxer(w)
|
||||
return nil
|
||||
}
|
||||
|
||||
h.CodecTypes = CodecTypes
|
||||
}
|
29
format/mkv/mkvio/ebml.go
Normal file
29
format/mkv/mkvio/ebml.go
Normal file
@ -0,0 +1,29 @@
|
||||
package mkvio
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Document represents a WebM file
|
||||
type Document struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
// ElementRegister contains the ID, type and name of the
|
||||
// standard WebM/Matroska elements
|
||||
type ElementRegister struct {
|
||||
ID uint32
|
||||
Type uint8
|
||||
Name string
|
||||
}
|
||||
|
||||
// Element is a Matroska/WebM/EBML element
|
||||
type Element struct {
|
||||
ElementRegister
|
||||
|
||||
Parent *Element
|
||||
Level int32
|
||||
Size uint64
|
||||
Content []byte // Data contained in the element, nil if it is a master element
|
||||
Bytes []byte // Whole binary representation of the element (nil if data is missing)
|
||||
}
|
359
format/mkv/mkvio/elements.go
Normal file
359
format/mkv/mkvio/elements.go
Normal file
@ -0,0 +1,359 @@
|
||||
package mkvio
|
||||
|
||||
const (
|
||||
ElementType uint8 = 0x0
|
||||
ElementTypeUnknown uint8 = 0x0
|
||||
ElementTypeMaster uint8 = 0x1
|
||||
ElementTypeUint uint8 = 0x2
|
||||
ElementTypeInt uint8 = 0x3
|
||||
ElementTypeString uint8 = 0x4
|
||||
ElementTypeUnicode uint8 = 0x5
|
||||
ElementTypeBinary uint8 = 0x6
|
||||
ElementTypeFloat uint8 = 0x7
|
||||
ElementTypeDate uint8 = 0x8
|
||||
)
|
||||
|
||||
var (
|
||||
ElementUnknown = ElementRegister{0x0, ElementTypeUnknown, "Unknown"}
|
||||
ElementEBML = ElementRegister{0x1a45dfa3, ElementTypeMaster, "EBML"}
|
||||
ElementEBMLVersion = ElementRegister{0x4286, ElementTypeUint, "EBMLVersion"}
|
||||
ElementEBMLReadVersion = ElementRegister{0x42f7, ElementTypeUint, "EBMLReadVersion"}
|
||||
ElementEBMLMaxIDLength = ElementRegister{0x42f2, ElementTypeUint, "EBMLMaxIDLength"}
|
||||
ElementEBMLMaxSizeLength = ElementRegister{0x42f3, ElementTypeUint, "EBMLMaxSizeLength"}
|
||||
ElementDocType = ElementRegister{0x4282, ElementTypeString, "DocType"}
|
||||
ElementDocTypeVersion = ElementRegister{0x4287, ElementTypeUint, "DocTypeVersion"}
|
||||
ElementDocTypeReadVersion = ElementRegister{0x4285, ElementTypeUint, "DocTypeReadVersion"}
|
||||
ElementVoid = ElementRegister{0xec, ElementTypeBinary, "Void"}
|
||||
ElementCRC32 = ElementRegister{0xbf, ElementTypeBinary, "CRC-32"}
|
||||
ElementSegment = ElementRegister{0x18538067, ElementTypeMaster, "Segment"}
|
||||
ElementSeekHead = ElementRegister{0x114d9b74, ElementTypeMaster, "SeekHead"}
|
||||
ElementSeek = ElementRegister{0x4dbb, ElementTypeMaster, "Seek"}
|
||||
ElementSeekID = ElementRegister{0x53ab, ElementTypeBinary, "SeekID"}
|
||||
ElementSeekPosition = ElementRegister{0x53ac, ElementTypeUint, "SeekPosition"}
|
||||
ElementInfo = ElementRegister{0x1549a966, ElementTypeMaster, "Info"}
|
||||
ElementSegmentUID = ElementRegister{0x73a4, ElementTypeBinary, "SegmentUID"}
|
||||
ElementSegmentFilename = ElementRegister{0x7384, ElementTypeUnicode, "SegmentFilename"}
|
||||
ElementPrevUID = ElementRegister{0x3cb923, ElementTypeBinary, "PrevUID"}
|
||||
ElementPrevFilename = ElementRegister{0x3c83ab, ElementTypeUnicode, "PrevFilename"}
|
||||
ElementNextUID = ElementRegister{0x3eb923, ElementTypeBinary, "NextUID"}
|
||||
ElementNextFilename = ElementRegister{0x3e83bb, ElementTypeUnicode, "NextFilename"}
|
||||
ElementSegmentFamily = ElementRegister{0x4444, ElementTypeBinary, "SegmentFamily"}
|
||||
ElementChapterTranslate = ElementRegister{0x6924, ElementTypeMaster, "ChapterTranslate"}
|
||||
ElementChapterTranslateEditionUID = ElementRegister{0x69fc, ElementTypeUint, "ChapterTranslateEditionUID"}
|
||||
ElementChapterTranslateCodec = ElementRegister{0x69bf, ElementTypeUint, "ChapterTranslateCodec"}
|
||||
ElementChapterTranslateID = ElementRegister{0x69a5, ElementTypeBinary, "ChapterTranslateID"}
|
||||
ElementTimecodeScale = ElementRegister{0x2ad7b1, ElementTypeUint, "TimecodeScale"}
|
||||
ElementDuration = ElementRegister{0x4489, ElementTypeFloat, "Duration"}
|
||||
ElementDateUTC = ElementRegister{0x4461, ElementTypeDate, "DateUTC"}
|
||||
ElementTitle = ElementRegister{0x7ba9, ElementTypeUnicode, "Title"}
|
||||
ElementMuxingApp = ElementRegister{0x4d80, ElementTypeUnicode, "MuxingApp"}
|
||||
ElementWritingApp = ElementRegister{0x5741, ElementTypeUnicode, "WritingApp"}
|
||||
ElementCluster = ElementRegister{0x1f43b675, ElementTypeMaster, "Cluster"}
|
||||
ElementTimecode = ElementRegister{0xe7, ElementTypeUint, "Timecode"}
|
||||
ElementSlientTracks = ElementRegister{0x5854, ElementTypeMaster, "SlientTracks"}
|
||||
ElementSlientTrackNumber = ElementRegister{0x58d7, ElementTypeUint, "SlientTrackNumber"}
|
||||
ElementPosition = ElementRegister{0xa7, ElementTypeUint, "Position"}
|
||||
ElementPrevSize = ElementRegister{0xab, ElementTypeUint, "PrevSize"}
|
||||
ElementSimpleBlock = ElementRegister{0xa3, ElementTypeBinary, "SimpleBlock"}
|
||||
ElementBlockGroup = ElementRegister{0xa0, ElementTypeMaster, "BlockGroup"}
|
||||
ElementBlock = ElementRegister{0xa1, ElementTypeBinary, "Block"}
|
||||
ElementBlockAdditions = ElementRegister{0x75a1, ElementTypeMaster, "BlockAdditions"}
|
||||
ElementBlockMore = ElementRegister{0xa6, ElementTypeMaster, "BlockMore"}
|
||||
ElementBlockAddID = ElementRegister{0xee, ElementTypeUint, "BlockAddID"}
|
||||
ElementBlockAdditional = ElementRegister{0xa5, ElementTypeBinary, "BlockAdditional"}
|
||||
ElementBlockDuration = ElementRegister{0x9b, ElementTypeUint, "BlockDuration"}
|
||||
ElementReferencePriority = ElementRegister{0xfa, ElementTypeUint, "ReferencePriority"}
|
||||
ElementReferenceBlock = ElementRegister{0xfb, ElementTypeInt, "ReferenceBlock"}
|
||||
ElementCodecState = ElementRegister{0xa4, ElementTypeBinary, "CodecState"}
|
||||
ElementDiscardPadding = ElementRegister{0x75a2, ElementTypeInt, "DiscardPadding"}
|
||||
ElementSlices = ElementRegister{0x8e, ElementTypeMaster, "Slices"}
|
||||
ElementTimeSlice = ElementRegister{0xe8, ElementTypeMaster, "TimeSlice"}
|
||||
ElementLaceNumber = ElementRegister{0xcc, ElementTypeUint, "LaceNumber"}
|
||||
ElementTracks = ElementRegister{0x1654ae6b, ElementTypeMaster, "Tracks"}
|
||||
ElementTrackEntry = ElementRegister{0xae, ElementTypeMaster, "TrackEntry"}
|
||||
ElementTrackNumber = ElementRegister{0xd7, ElementTypeUint, "TrackNumber"}
|
||||
ElementTrackUID = ElementRegister{0x73c5, ElementTypeUint, "TrackUID"}
|
||||
ElementTrackType = ElementRegister{0x83, ElementTypeUint, "TrackType"}
|
||||
ElementFlagEnabled = ElementRegister{0xb9, ElementTypeUint, "FlagEnabled"}
|
||||
ElementFlagDefault = ElementRegister{0x88, ElementTypeUint, "FlagDefault"}
|
||||
ElementFlagForced = ElementRegister{0x55aa, ElementTypeUint, "FlagForced"}
|
||||
ElementFlagLacing = ElementRegister{0x9c, ElementTypeUint, "FlagLacing"}
|
||||
ElementMinCache = ElementRegister{0x6de7, ElementTypeUint, "MinCache"}
|
||||
ElementMaxCache = ElementRegister{0x6df8, ElementTypeUint, "MaxCache"}
|
||||
ElementDefaultDuration = ElementRegister{0x23e383, ElementTypeUint, "DefaultDuration"}
|
||||
ElementDefaultDecodedFieldDuration = ElementRegister{0x234e7a, ElementTypeUint, "DefaultDecodedFieldDuration"}
|
||||
ElementMaxBlockAdditionID = ElementRegister{0x55ee, ElementTypeUint, "MaxBlockAdditionID"}
|
||||
ElementName = ElementRegister{0x536e, ElementTypeUnicode, "Name"}
|
||||
ElementLanguage = ElementRegister{0x22b59c, ElementTypeString, "Language"}
|
||||
ElementCodecID = ElementRegister{0x86, ElementTypeString, "CodecID"}
|
||||
ElementCodecPrivate = ElementRegister{0x63a2, ElementTypeBinary, "CodecPrivate"}
|
||||
ElementCodecName = ElementRegister{0x258688, ElementTypeUnicode, "CodecName"}
|
||||
ElementAttachmentLink = ElementRegister{0x7446, ElementTypeUint, "AttachmentLink"}
|
||||
ElementCodecDecodeAll = ElementRegister{0xaa, ElementTypeUint, "CodecDecodeAll"}
|
||||
ElementTrackOverlay = ElementRegister{0x6fab, ElementTypeUint, "TrackOverlay"}
|
||||
ElementCodecDelay = ElementRegister{0x56aa, ElementTypeUint, "CodecDelay"}
|
||||
ElementSeekPreRoll = ElementRegister{0x56bb, ElementTypeUint, "SeekPreRoll"}
|
||||
ElementTrackTranslate = ElementRegister{0x6624, ElementTypeMaster, "TrackTranslate"}
|
||||
ElementTrackTranslateEditionUID = ElementRegister{0x66fc, ElementTypeUint, "TrackTranslateEditionUID"}
|
||||
ElementTrackTranslateCodec = ElementRegister{0x66bf, ElementTypeUint, "TrackTranslateCodec"}
|
||||
ElementTrackTranslateTrackID = ElementRegister{0x66a5, ElementTypeBinary, "TrackTranslateTrackID"}
|
||||
ElementVideo = ElementRegister{0xe0, ElementTypeMaster, "Video"}
|
||||
ElementFlagInterlaced = ElementRegister{0x9a, ElementTypeUint, "FlagInterlaced"}
|
||||
ElementStereoMode = ElementRegister{0x53b8, ElementTypeUint, "StereoMode"}
|
||||
ElementAlphaMode = ElementRegister{0x53c0, ElementTypeUint, "AlphaMode"}
|
||||
ElementPixelWidth = ElementRegister{0xb0, ElementTypeUint, "PixelWidth"}
|
||||
ElementPixelHeight = ElementRegister{0xba, ElementTypeUint, "PixelHeight"}
|
||||
ElementPixelCropBottom = ElementRegister{0x54aa, ElementTypeUint, "PixelCropBottom"}
|
||||
ElementPixelCropTop = ElementRegister{0x54bb, ElementTypeUint, "PixelCropTop"}
|
||||
ElementPixelCropLeft = ElementRegister{0x54cc, ElementTypeUint, "PixelCropLeft"}
|
||||
ElementPixelCropRight = ElementRegister{0x54dd, ElementTypeUint, "PixelCropRight"}
|
||||
ElementDisplayWidth = ElementRegister{0x54b0, ElementTypeUint, "DisplayWidth"}
|
||||
ElementDisplayHeight = ElementRegister{0x54ba, ElementTypeUint, "DisplayHeight"}
|
||||
ElementDisplayUint = ElementRegister{0x54b2, ElementTypeUint, "DisplayUint"}
|
||||
ElementAspectRatioType = ElementRegister{0x54b3, ElementTypeUint, "AspectRatioType"}
|
||||
ElementColourSpace = ElementRegister{0x2eb524, ElementTypeBinary, "ColourSpace"}
|
||||
ElementAudio = ElementRegister{0xe1, ElementTypeMaster, "Audio"}
|
||||
ElementSamplingFrequency = ElementRegister{0xb5, ElementTypeFloat, "SamplingFrequency"}
|
||||
ElementOutputSamplingFrequency = ElementRegister{0x78b5, ElementTypeFloat, "OutputSamplingFrequency"}
|
||||
ElementChannels = ElementRegister{0x9f, ElementTypeUint, "Channels"}
|
||||
ElementBitDepth = ElementRegister{0x6264, ElementTypeUint, "BitDepth"}
|
||||
ElementTrackOperation = ElementRegister{0xe2, ElementTypeMaster, "TrackOperation"}
|
||||
ElementTrackCombinePlanes = ElementRegister{0xe3, ElementTypeMaster, "TrackCombinePlanes"}
|
||||
ElementTrackPlane = ElementRegister{0xe4, ElementTypeMaster, "TrackPlane"}
|
||||
ElementTrackPlaneUID = ElementRegister{0xe5, ElementTypeUint, "TrackPlaneUID"}
|
||||
ElementTrackPlaneType = ElementRegister{0xe6, ElementTypeUint, "TrackPlaneType"}
|
||||
ElementTrackJoinBlocks = ElementRegister{0xe9, ElementTypeMaster, "TrackJoinBlocks"}
|
||||
ElementTrackJoinUID = ElementRegister{0xed, ElementTypeUint, "TrackJoinUID"}
|
||||
ElementContentEncodings = ElementRegister{0x6d80, ElementTypeMaster, "ContentEncodings"}
|
||||
ElementContentEncoding = ElementRegister{0x6240, ElementTypeMaster, "ContentEncoding"}
|
||||
ElementContentEncodingOrder = ElementRegister{0x5031, ElementTypeUint, "ContentEncodingOrder"}
|
||||
ElementContentEncodingScope = ElementRegister{0x5032, ElementTypeUint, "ContentEncodingScope"}
|
||||
ElementContentEncodingType = ElementRegister{0x5033, ElementTypeUint, "ContentEncodingType"}
|
||||
ElementContentCompression = ElementRegister{0x5034, ElementTypeMaster, "ContentCompression"}
|
||||
ElementContentCompAlgo = ElementRegister{0x4254, ElementTypeUint, "ContentCompAlgo"}
|
||||
ElementContentCompSettings = ElementRegister{0x4255, ElementTypeBinary, "ContentCompSettings"}
|
||||
ElementContentEncryption = ElementRegister{0x5035, ElementTypeMaster, "ContentEncryption"}
|
||||
ElementContentEncAlgo = ElementRegister{0x47e1, ElementTypeUint, "ContentEncAlgo"}
|
||||
ElementContentEncKeyID = ElementRegister{0x47e2, ElementTypeUint, "ContentEncKeyID"}
|
||||
ElementContentSignature = ElementRegister{0x47e3, ElementTypeBinary, "ContentSignature"}
|
||||
ElementContentSigKeyID = ElementRegister{0x47e4, ElementTypeBinary, "ContentSigKeyID"}
|
||||
ElementContentSigAlgo = ElementRegister{0x47e5, ElementTypeUint, "ContentSigAlgo"}
|
||||
ElementContentSigHashAlgo = ElementRegister{0x47e6, ElementTypeUint, "ContentSigHashAlgo"}
|
||||
ElementCues = ElementRegister{0x1c53bb6b, ElementTypeMaster, "Cues"}
|
||||
ElementCuePoint = ElementRegister{0xbb, ElementTypeMaster, "CuePoint"}
|
||||
ElementCueTime = ElementRegister{0xb3, ElementTypeUint, "CueTime"}
|
||||
ElementCueTrackPositions = ElementRegister{0xb7, ElementTypeMaster, "CueTrackPositions"}
|
||||
ElementCueTrack = ElementRegister{0xf7, ElementTypeUint, "CueTrack"}
|
||||
ElementCueClusterPosition = ElementRegister{0xf1, ElementTypeUint, "CueClusterPosition"}
|
||||
ElementCueRelativePosition = ElementRegister{0xf0, ElementTypeUint, "CueRelativePosition"}
|
||||
ElementCueDuration = ElementRegister{0xb2, ElementTypeUint, "CueDuration"}
|
||||
ElementCueBlockNumber = ElementRegister{0x5378, ElementTypeUint, "CueBlockNumber"}
|
||||
ElementCueCodecState = ElementRegister{0xea, ElementTypeUint, "CueCodecState"}
|
||||
ElementCueReference = ElementRegister{0xdb, ElementTypeMaster, "CueReference"}
|
||||
ElementCueRefTime = ElementRegister{0x96, ElementTypeUint, "CueRefTime"}
|
||||
ElementAttachments = ElementRegister{0x1941a469, ElementTypeMaster, "Attachments"}
|
||||
ElementAttachedFile = ElementRegister{0x61a7, ElementTypeMaster, "AttachedFile"}
|
||||
ElementFileDescription = ElementRegister{0x467e, ElementTypeUnicode, "FileDescription"}
|
||||
ElementFileName = ElementRegister{0x466e, ElementTypeUnicode, "FileName"}
|
||||
ElementFileMimeType = ElementRegister{0x6460, ElementTypeString, "FileMimeType"}
|
||||
ElementFileData = ElementRegister{0x465c, ElementTypeBinary, "FileData"}
|
||||
ElementFileUID = ElementRegister{0x46ae, ElementTypeUint, "FileUID"}
|
||||
ElementChapters = ElementRegister{0x1043a770, ElementTypeMaster, "Chapters"}
|
||||
ElementEditionEntry = ElementRegister{0x45b9, ElementTypeMaster, "EditionEntry"}
|
||||
ElementEditionUID = ElementRegister{0x45bc, ElementTypeUint, "EditionUID"}
|
||||
ElementEditionFlagHidden = ElementRegister{0x45bd, ElementTypeUint, "EditionFlagHidden"}
|
||||
ElementEditionFlagDefault = ElementRegister{0x45db, ElementTypeUint, "EditionFlagDefault"}
|
||||
ElementEditionFlagOrdered = ElementRegister{0x45dd, ElementTypeUint, "EditionFlagOrdered"}
|
||||
ElementChapterAtom = ElementRegister{0xb6, ElementTypeMaster, "ChapterAtom"}
|
||||
ElementChapterUID = ElementRegister{0x73c4, ElementTypeUint, "ChapterUID"}
|
||||
ElementChapterStringUID = ElementRegister{0x5654, ElementTypeUnicode, "ChapterStringUID"}
|
||||
ElementChapterTimeStart = ElementRegister{0x91, ElementTypeUint, "ChapterTimeStart"}
|
||||
ElementChapterTimeEnd = ElementRegister{0x92, ElementTypeUint, "ChapterTimeEnd"}
|
||||
ElementChapterFlagHidden = ElementRegister{0x98, ElementTypeUint, "ChapterFlagHidden"}
|
||||
ElementChapterFlagEnabled = ElementRegister{0x4598, ElementTypeUint, "ChapterFlagEnabled"}
|
||||
ElementChapterSegmentUID = ElementRegister{0x6e67, ElementTypeBinary, "ChapterSegmentUID"}
|
||||
ElementChapterSegmentEditionUID = ElementRegister{0x6ebc, ElementTypeUint, "ChapterSegmentEditionUID"}
|
||||
ElementChapterPhysicalEquiv = ElementRegister{0x63c3, ElementTypeUint, "ChapterPhysicalEquiv"}
|
||||
ElementChapterTrack = ElementRegister{0x8f, ElementTypeMaster, "ChapterTrack"}
|
||||
ElementChapterTrackNumber = ElementRegister{0x89, ElementTypeUint, "ChapterTrackNumber"}
|
||||
ElementChapterDisplay = ElementRegister{0x80, ElementTypeMaster, "ChapterDisplay"}
|
||||
ElementChapString = ElementRegister{0x85, ElementTypeUnicode, "ChapString"}
|
||||
ElementChapLanguage = ElementRegister{0x437c, ElementTypeString, "ChapLanguage"}
|
||||
ElementChapCountry = ElementRegister{0x437e, ElementTypeString, "ChapCountry"}
|
||||
ElementChapProcess = ElementRegister{0x6944, ElementTypeMaster, "ChapProcess"}
|
||||
ElementChapProcessCodecID = ElementRegister{0x6955, ElementTypeUint, "ChapProcessCodecID"}
|
||||
ElementChapProcessPrivate = ElementRegister{0x450d, ElementTypeBinary, "ChapProcessPrivate"}
|
||||
ElementChapProcessCommand = ElementRegister{0x6911, ElementTypeMaster, "ChapProcessCommand"}
|
||||
ElementChapProcessTime = ElementRegister{0x6922, ElementTypeUint, "ChapProcessTime"}
|
||||
ElementChapProcessData = ElementRegister{0x6933, ElementTypeBinary, "ChapProcessData"}
|
||||
|
||||
// TODO: Add tags
|
||||
)
|
||||
|
||||
// GetElementRegister returns the infos concerning the provided element ID
|
||||
func GetElementRegister(id uint32) ElementRegister {
|
||||
switch id {
|
||||
case ElementEBML.ID:
|
||||
return ElementEBML
|
||||
case ElementEBMLVersion.ID:
|
||||
return ElementEBMLVersion
|
||||
case ElementEBMLReadVersion.ID:
|
||||
return ElementEBMLReadVersion
|
||||
case ElementEBMLMaxIDLength.ID:
|
||||
return ElementEBMLMaxIDLength
|
||||
case ElementEBMLMaxSizeLength.ID:
|
||||
return ElementEBMLMaxSizeLength
|
||||
case ElementDocType.ID:
|
||||
return ElementDocType
|
||||
case ElementDocTypeVersion.ID:
|
||||
return ElementDocTypeVersion
|
||||
case ElementDocTypeReadVersion.ID:
|
||||
return ElementDocTypeReadVersion
|
||||
case ElementVoid.ID:
|
||||
return ElementVoid
|
||||
case ElementCRC32.ID:
|
||||
return ElementCRC32
|
||||
case ElementSegment.ID:
|
||||
return ElementSegment
|
||||
case ElementSeekHead.ID:
|
||||
return ElementSeekHead
|
||||
case ElementSeek.ID:
|
||||
return ElementSeek
|
||||
case ElementSeekID.ID:
|
||||
return ElementSeekID
|
||||
case ElementSeekPosition.ID:
|
||||
return ElementSeekPosition
|
||||
case ElementInfo.ID:
|
||||
return ElementInfo
|
||||
case ElementTimecodeScale.ID:
|
||||
return ElementTimecodeScale
|
||||
case ElementDuration.ID:
|
||||
return ElementDuration
|
||||
case ElementDateUTC.ID:
|
||||
return ElementDateUTC
|
||||
case ElementTitle.ID:
|
||||
return ElementTitle
|
||||
case ElementMuxingApp.ID:
|
||||
return ElementMuxingApp
|
||||
case ElementWritingApp.ID:
|
||||
return ElementWritingApp
|
||||
case ElementCluster.ID:
|
||||
return ElementCluster
|
||||
case ElementTimecode.ID:
|
||||
return ElementTimecode
|
||||
case ElementPrevSize.ID:
|
||||
return ElementPrevSize
|
||||
case ElementSimpleBlock.ID:
|
||||
return ElementSimpleBlock
|
||||
case ElementBlockGroup.ID:
|
||||
return ElementBlockGroup
|
||||
case ElementBlock.ID:
|
||||
return ElementBlock
|
||||
case ElementBlockAdditions.ID:
|
||||
return ElementBlockAdditions
|
||||
case ElementBlockMore.ID:
|
||||
return ElementBlockMore
|
||||
case ElementBlockAddID.ID:
|
||||
return ElementBlockAddID
|
||||
case ElementBlockAdditional.ID:
|
||||
return ElementBlockAdditional
|
||||
case ElementBlockDuration.ID:
|
||||
return ElementBlockDuration
|
||||
case ElementReferenceBlock.ID:
|
||||
return ElementReferenceBlock
|
||||
case ElementDiscardPadding.ID:
|
||||
return ElementDiscardPadding
|
||||
case ElementTracks.ID:
|
||||
return ElementTracks
|
||||
case ElementTrackEntry.ID:
|
||||
return ElementTrackEntry
|
||||
case ElementTrackNumber.ID:
|
||||
return ElementTrackNumber
|
||||
case ElementTrackUID.ID:
|
||||
return ElementTrackUID
|
||||
case ElementTrackType.ID:
|
||||
return ElementTrackType
|
||||
case ElementFlagEnabled.ID:
|
||||
return ElementFlagEnabled
|
||||
case ElementFlagDefault.ID:
|
||||
return ElementFlagDefault
|
||||
case ElementFlagForced.ID:
|
||||
return ElementFlagForced
|
||||
case ElementFlagLacing.ID:
|
||||
return ElementFlagLacing
|
||||
case ElementDefaultDuration.ID:
|
||||
return ElementDefaultDuration
|
||||
case ElementName.ID:
|
||||
return ElementName
|
||||
case ElementLanguage.ID:
|
||||
return ElementLanguage
|
||||
case ElementCodecID.ID:
|
||||
return ElementCodecID
|
||||
case ElementCodecPrivate.ID:
|
||||
return ElementCodecPrivate
|
||||
case ElementCodecName.ID:
|
||||
return ElementCodecName
|
||||
case ElementCodecDelay.ID:
|
||||
return ElementCodecDelay
|
||||
case ElementSeekPreRoll.ID:
|
||||
return ElementSeekPreRoll
|
||||
case ElementVideo.ID:
|
||||
return ElementVideo
|
||||
case ElementFlagInterlaced.ID:
|
||||
return ElementFlagInterlaced
|
||||
case ElementStereoMode.ID:
|
||||
return ElementStereoMode
|
||||
case ElementAlphaMode.ID:
|
||||
return ElementAlphaMode
|
||||
case ElementPixelWidth.ID:
|
||||
return ElementPixelWidth
|
||||
case ElementPixelHeight.ID:
|
||||
return ElementPixelHeight
|
||||
case ElementPixelCropBottom.ID:
|
||||
return ElementPixelCropBottom
|
||||
case ElementPixelCropTop.ID:
|
||||
return ElementPixelCropTop
|
||||
case ElementPixelCropLeft.ID:
|
||||
return ElementPixelCropLeft
|
||||
case ElementPixelCropRight.ID:
|
||||
return ElementPixelCropRight
|
||||
case ElementDisplayWidth.ID:
|
||||
return ElementDisplayWidth
|
||||
case ElementDisplayHeight.ID:
|
||||
return ElementDisplayHeight
|
||||
case ElementDisplayUint.ID:
|
||||
return ElementDisplayUint
|
||||
case ElementAspectRatioType.ID:
|
||||
return ElementAspectRatioType
|
||||
case ElementAudio.ID:
|
||||
return ElementAudio
|
||||
case ElementSamplingFrequency.ID:
|
||||
return ElementSamplingFrequency
|
||||
case ElementOutputSamplingFrequency.ID:
|
||||
return ElementOutputSamplingFrequency
|
||||
case ElementChannels.ID:
|
||||
return ElementChannels
|
||||
case ElementBitDepth.ID:
|
||||
return ElementBitDepth
|
||||
case ElementContentEncodings.ID:
|
||||
return ElementContentEncodings
|
||||
case ElementContentEncoding.ID:
|
||||
return ElementContentEncoding
|
||||
case ElementContentEncodingOrder.ID:
|
||||
return ElementContentEncodingOrder
|
||||
case ElementContentEncodingScope.ID:
|
||||
return ElementContentEncodingScope
|
||||
case ElementContentEncodingType.ID:
|
||||
return ElementContentEncodingType
|
||||
case ElementContentEncryption.ID:
|
||||
return ElementContentEncryption
|
||||
case ElementContentEncAlgo.ID:
|
||||
return ElementContentEncAlgo
|
||||
case ElementContentEncKeyID.ID:
|
||||
return ElementContentEncKeyID
|
||||
case ElementUnknown.ID:
|
||||
return ElementUnknown
|
||||
default:
|
||||
return ElementUnknown
|
||||
}
|
||||
}
|
214
format/mkv/mkvio/parser.go
Normal file
214
format/mkv/mkvio/parser.go
Normal file
@ -0,0 +1,214 @@
|
||||
package mkvio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrParse = errors.New("Parse error")
|
||||
ErrUnexpectedEOF = errors.New("Unexpected EOF")
|
||||
)
|
||||
|
||||
// InitDocument creates a MKV/WebM document containing the file data
|
||||
// It does not do any parsing
|
||||
func InitDocument(r io.Reader) *Document {
|
||||
doc := new(Document)
|
||||
doc.r = r
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// ParseAll parses the entire MKV/WebM document
|
||||
// When an EBML/WebM element is encountered, it calls the provided function
|
||||
// and passes the newly parsed element
|
||||
func (doc *Document) ParseAll(c func(Element)) error {
|
||||
for {
|
||||
el, err := doc.ParseElement()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c(el)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (doc *Document) GetVideoCodec() (*Element, error) {
|
||||
for {
|
||||
|
||||
el, err := doc.ParseElement()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if el.ElementRegister.ID == ElementCodecPrivate.ID {
|
||||
return &el, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil, errors.New("not found")
|
||||
}
|
||||
|
||||
// ParseElement parses an EBML element starting at the document's current cursor position.
|
||||
// Because of its nature, it does not set the elements's parent or level.
|
||||
func (doc *Document) ParseElement() (Element, error) {
|
||||
var el Element
|
||||
|
||||
id, err := doc.GetElementID(&el)
|
||||
if err != nil {
|
||||
return el, err
|
||||
}
|
||||
|
||||
size, err := doc.GetElementSize(&el)
|
||||
if err != nil {
|
||||
return el, err
|
||||
}
|
||||
|
||||
reg := GetElementRegister(id)
|
||||
el.ID = reg.ID
|
||||
el.Type = reg.Type
|
||||
el.Name = reg.Name
|
||||
el.Size = size
|
||||
|
||||
if el.Type != ElementTypeMaster {
|
||||
d, err := doc.GetElementContent(&el)
|
||||
if err != nil {
|
||||
return el, err
|
||||
}
|
||||
|
||||
el.Content = d
|
||||
}
|
||||
|
||||
return el, nil
|
||||
}
|
||||
|
||||
// GetElementID tries to parse the next element's id,
|
||||
// starting from the document's current cursor position.
|
||||
func (doc *Document) GetElementID(el *Element) (uint32, error) {
|
||||
b := make([]byte, 1)
|
||||
|
||||
_, err := io.ReadFull(doc.r, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if ((b[0] & 0x80) >> 7) == 1 { // Class A ID (on 1 byte)
|
||||
el.Bytes = append(el.Bytes, b[0])
|
||||
return uint32(b[0]), nil
|
||||
}
|
||||
if ((b[0] & 0x40) >> 6) == 1 { // Class B ID (on 2 byte)
|
||||
bb := make([]byte, 2)
|
||||
copy(bb, b)
|
||||
|
||||
_, err = io.ReadFull(doc.r, bb[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
el.Bytes = append(el.Bytes, bb...)
|
||||
return uint32(pack(2, bb)), nil
|
||||
}
|
||||
if ((b[0] & 0x20) >> 5) == 1 { // Class C ID (on 3 bytes)
|
||||
bb := make([]byte, 3)
|
||||
copy(bb, b)
|
||||
|
||||
_, err = io.ReadFull(doc.r, bb[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
el.Bytes = append(el.Bytes, bb...)
|
||||
return uint32(pack(3, bb)), nil
|
||||
}
|
||||
if ((b[0] & 0x10) >> 4) == 1 { // Class D ID (on 4 bytes)
|
||||
bb := make([]byte, 4)
|
||||
copy(bb, b)
|
||||
|
||||
_, err = io.ReadFull(doc.r, bb[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
el.Bytes = append(el.Bytes, bb...)
|
||||
return uint32(pack(4, bb)), nil
|
||||
}
|
||||
|
||||
return 0, ErrParse
|
||||
}
|
||||
|
||||
// GetElementSize tries to parse the next element's size,
|
||||
// starting from the document's current cursor position.
|
||||
func (doc *Document) GetElementSize(el *Element) (uint64, error) {
|
||||
b := make([]byte, 1)
|
||||
|
||||
_, err := io.ReadFull(doc.r, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var mask byte
|
||||
var length uint64
|
||||
|
||||
if b[0] >= 0x80 {
|
||||
length = 1
|
||||
mask = 0x7f
|
||||
} else if b[0] >= 0x40 {
|
||||
length = 2
|
||||
mask = 0x3f
|
||||
} else if b[0] >= 0x20 {
|
||||
length = 3
|
||||
mask = 0x1f
|
||||
} else if b[0] >= 0x10 {
|
||||
length = 4
|
||||
mask = 0x0f
|
||||
} else if b[0] >= 0x08 {
|
||||
length = 5
|
||||
mask = 0x07
|
||||
} else if b[0] >= 0x04 {
|
||||
length = 6
|
||||
mask = 0x03
|
||||
} else if b[0] >= 0x02 {
|
||||
length = 7
|
||||
mask = 0x01
|
||||
} else if b[0] >= 0x01 {
|
||||
length = 8
|
||||
mask = 0x00
|
||||
} else {
|
||||
return 0, ErrParse
|
||||
}
|
||||
|
||||
bb := make([]byte, length)
|
||||
bb[0] = b[0]
|
||||
|
||||
if length > 1 {
|
||||
_, err = io.ReadFull(doc.r, bb[1:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
el.Bytes = append(el.Bytes, bb...)
|
||||
|
||||
bb[0] &= mask
|
||||
v := pack(int(length), bb)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// GetElementContent returns the element's data (if any)
|
||||
// Data is present if the element's type is not Master
|
||||
func (doc *Document) GetElementContent(el *Element) ([]byte, error) {
|
||||
buf := make([]byte, el.Size)
|
||||
|
||||
_, err := io.ReadFull(doc.r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
el.Bytes = append(el.Bytes, buf...)
|
||||
return buf, nil
|
||||
}
|
23
format/mkv/mkvio/utils.go
Normal file
23
format/mkv/mkvio/utils.go
Normal file
@ -0,0 +1,23 @@
|
||||
package mkvio
|
||||
|
||||
func pack(n int, b []byte) uint64 {
|
||||
var v uint64
|
||||
var k uint64 = (uint64(n) - 1) * 8
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
v |= uint64(b[i]) << k
|
||||
k -= 8
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func unpack(n int, v uint64) []byte {
|
||||
var b []byte
|
||||
|
||||
for i := uint(n); i > 0; i-- {
|
||||
b = append(b, byte(v>>(8*i))&0xff)
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
24
format/mkv/stream.go
Normal file
24
format/mkv/stream.go
Normal file
@ -0,0 +1,24 @@
|
||||
package mkv
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
)
|
||||
|
||||
type Stream struct {
|
||||
av.CodecData
|
||||
|
||||
demuxer *Demuxer
|
||||
|
||||
pid uint16
|
||||
streamId uint8
|
||||
streamType uint8
|
||||
|
||||
idx int
|
||||
|
||||
iskeyframe bool
|
||||
pts, dts time.Duration
|
||||
data []byte
|
||||
datalen int
|
||||
}
|
1
go.mod
1
go.mod
@ -6,4 +6,5 @@ require (
|
||||
github.com/pion/interceptor v0.1.6
|
||||
github.com/pion/webrtc/v2 v2.2.26
|
||||
github.com/pion/webrtc/v3 v3.1.17
|
||||
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c // indirect
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -154,6 +154,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c h1:Nu1I2o5Lk5nvn2RChPOJ1MzTXKZoHiy665a5aANHssQ=
|
||||
github.com/quadrifoglio/go-mkv v0.0.0-20180620161916-e7a1fc70199c/go.mod h1:Mtv+LuzkLR5r7fwBlvqI/N/5d4nOaH/7fpgQFXwxnFk=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
|
Loading…
Reference in New Issue
Block a user