263 lines
5.1 KiB
Go
263 lines
5.1 KiB
Go
package isom
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/nareix/bits"
|
|
"io"
|
|
"io/ioutil"
|
|
)
|
|
|
|
// copied from libavformat/isom.h
|
|
|
|
const (
|
|
MP4ESDescrTag = 3
|
|
MP4DecConfigDescrTag = 4
|
|
MP4DecSpecificDescrTag = 5
|
|
)
|
|
|
|
var debugReader = false
|
|
var debugWriter = false
|
|
|
|
func readDesc(r io.Reader) (tag uint, data []byte, err error) {
|
|
if tag, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
var length uint
|
|
for i := 0; i < 4; i++ {
|
|
var c uint
|
|
if c, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
length = (length << 7) | (c & 0x7f)
|
|
if c&0x80 == 0 {
|
|
break
|
|
}
|
|
}
|
|
data = make([]byte, length)
|
|
if _, err = r.Read(data); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func writeDesc(w io.Writer, tag uint, data []byte) (err error) {
|
|
if err = bits.WriteUIntBE(w, tag, 8); err != nil {
|
|
return
|
|
}
|
|
length := uint(len(data))
|
|
for i := 3; i > 0; i-- {
|
|
if err = bits.WriteUIntBE(w, (length>>uint(7*i))&0x7f|0x80, 8); err != nil {
|
|
return
|
|
}
|
|
}
|
|
if err = bits.WriteUIntBE(w, length&0x7f, 8); err != nil {
|
|
return
|
|
}
|
|
if _, err = w.Write(data); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func readESDesc(r io.Reader) (err error) {
|
|
var ES_ID uint
|
|
// ES_ID
|
|
if ES_ID, err = bits.ReadUIntBE(r, 16); err != nil {
|
|
return
|
|
}
|
|
var flags uint
|
|
if flags, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
//streamDependenceFlag
|
|
if flags&0x80 != 0 {
|
|
if _, err = bits.ReadUIntBE(r, 16); err != nil {
|
|
return
|
|
}
|
|
}
|
|
//URL_Flag
|
|
if flags&0x40 != 0 {
|
|
var length uint
|
|
if length, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
if _, err = io.CopyN(ioutil.Discard, r, int64(length)); err != nil {
|
|
return
|
|
}
|
|
}
|
|
//OCRstreamFlag
|
|
if flags&0x20 != 0 {
|
|
if _, err = bits.ReadUIntBE(r, 16); err != nil {
|
|
return
|
|
}
|
|
}
|
|
if debugReader {
|
|
println("readESDesc:", ES_ID, flags)
|
|
}
|
|
return
|
|
}
|
|
|
|
func writeESDesc(w io.Writer, ES_ID uint) (err error) {
|
|
// ES_ID
|
|
if err = bits.WriteUIntBE(w, ES_ID, 16); err != nil {
|
|
return
|
|
}
|
|
// flags
|
|
if err = bits.WriteUIntBE(w, 0, 8); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func readDescByTag(r io.Reader, targetTag uint) (data []byte, err error) {
|
|
var found bool
|
|
for {
|
|
if tag, _data, err := readDesc(r); err != nil {
|
|
break
|
|
} else {
|
|
if tag == targetTag {
|
|
data = _data
|
|
found = true
|
|
}
|
|
if debugReader {
|
|
println("readDescByTag:", tag, len(_data))
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
err = fmt.Errorf("tag not found")
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// copied from libavformat/isom.c ff_mp4_read_dec_config_descr()
|
|
func readDecConfDesc(r io.Reader) (decConfig []byte, err error) {
|
|
var objectId uint
|
|
var streamType uint
|
|
var bufSize uint
|
|
var maxBitrate uint
|
|
var avgBitrate uint
|
|
|
|
// objectId
|
|
if objectId, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
// streamType
|
|
if streamType, err = bits.ReadUIntBE(r, 8); err != nil {
|
|
return
|
|
}
|
|
// buffer size db
|
|
if bufSize, err = bits.ReadUIntBE(r, 24); err != nil {
|
|
return
|
|
}
|
|
// max bitrate
|
|
if maxBitrate, err = bits.ReadUIntBE(r, 32); err != nil {
|
|
return
|
|
}
|
|
// avg bitrate
|
|
if avgBitrate, err = bits.ReadUIntBE(r, 32); err != nil {
|
|
return
|
|
}
|
|
|
|
if debugReader {
|
|
println("readDecConfDesc:", objectId, streamType, bufSize, maxBitrate, avgBitrate)
|
|
}
|
|
|
|
if decConfig, err = readDescByTag(r, MP4DecSpecificDescrTag); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// copied from libavformat/movenc.c mov_write_esds_tag()
|
|
func writeDecConfDesc(w io.Writer, objectId uint, streamType uint, decConfig []byte) (err error) {
|
|
// objectId
|
|
if err = bits.WriteUIntBE(w, objectId, 8); err != nil {
|
|
return
|
|
}
|
|
// streamType
|
|
if err = bits.WriteUIntBE(w, streamType, 8); err != nil {
|
|
return
|
|
}
|
|
// buffer size db
|
|
if err = bits.WriteUIntBE(w, 0, 24); err != nil {
|
|
return
|
|
}
|
|
// max bitrate
|
|
if err = bits.WriteUIntBE(w, 200000, 32); err != nil {
|
|
return
|
|
}
|
|
// avg bitrate
|
|
if err = bits.WriteUIntBE(w, 0, 32); err != nil {
|
|
return
|
|
}
|
|
if err = writeDesc(w, MP4DecSpecificDescrTag, decConfig); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// copied from libavformat/mov.c ff_mov_read_esds()
|
|
func ReadElemStreamDesc(r io.Reader) (decConfig []byte, err error) {
|
|
if debugReader {
|
|
println("ReadElemStreamDesc: start")
|
|
}
|
|
|
|
var data []byte
|
|
if data, err = readDescByTag(r, MP4ESDescrTag); err != nil {
|
|
return
|
|
}
|
|
r = bytes.NewReader(data)
|
|
|
|
if err = readESDesc(r); err != nil {
|
|
return
|
|
}
|
|
|
|
if data, err = readDescByTag(r, MP4DecConfigDescrTag); err != nil {
|
|
return
|
|
}
|
|
r = bytes.NewReader(data)
|
|
|
|
if decConfig, err = readDecConfDesc(r); err != nil {
|
|
return
|
|
}
|
|
|
|
if debugReader {
|
|
println("ReadElemStreamDesc: end")
|
|
}
|
|
return
|
|
}
|
|
|
|
func WriteElemStreamDesc(w io.Writer, decConfig []byte, trackId uint) (err error) {
|
|
// MP4ESDescrTag(ESDesc MP4DecConfigDescrTag(objectId streamType bufSize avgBitrate MP4DecSpecificDescrTag(decConfig)))
|
|
|
|
data := decConfig
|
|
|
|
buf := &bytes.Buffer{}
|
|
// 0x40 = ObjectType AAC
|
|
// 0x15 = Audiostream
|
|
writeDecConfDesc(buf, 0x40, 0x15, data)
|
|
data = buf.Bytes()
|
|
|
|
buf = &bytes.Buffer{}
|
|
writeDesc(buf, MP4DecConfigDescrTag, data) // 4
|
|
data = buf.Bytes()
|
|
|
|
buf = &bytes.Buffer{}
|
|
writeESDesc(buf, trackId)
|
|
buf.Write(data)
|
|
writeDesc(buf, 0x06, []byte{0x02})
|
|
data = buf.Bytes()
|
|
|
|
buf = &bytes.Buffer{}
|
|
writeDesc(buf, MP4ESDescrTag, data) // 3
|
|
data = buf.Bytes()
|
|
|
|
if _, err = w.Write(data); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|