many modifications to change to new api
This commit is contained in:
parent
c6b37ad0a5
commit
2ffa926532
@ -259,7 +259,7 @@ var atoms = {
|
|||||||
avc1Conf: {
|
avc1Conf: {
|
||||||
cc4: 'avcC',
|
cc4: 'avcC',
|
||||||
fields: [
|
fields: [
|
||||||
['record', 'AVCDecoderConfRecord'],
|
['data', '[]byte'],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,357 +1,12 @@
|
|||||||
package atom
|
package atom
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
_ "bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/nareix/bits"
|
"github.com/nareix/bits"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
type H264SPSInfo struct {
|
|
||||||
ProfileIdc uint
|
|
||||||
LevelIdc uint
|
|
||||||
|
|
||||||
MbWidth uint
|
|
||||||
MbHeight uint
|
|
||||||
|
|
||||||
CropLeft uint
|
|
||||||
CropRight uint
|
|
||||||
CropTop uint
|
|
||||||
CropBottom uint
|
|
||||||
|
|
||||||
Width uint
|
|
||||||
Height uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseH264SPS(data []byte) (res *H264SPSInfo, err error) {
|
|
||||||
r := &bits.GolombBitReader{
|
|
||||||
R: bytes.NewReader(data),
|
|
||||||
}
|
|
||||||
|
|
||||||
self := &H264SPSInfo{}
|
|
||||||
|
|
||||||
if self.ProfileIdc, err = r.ReadBits(8); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// constraint_set0_flag-constraint_set6_flag,reserved_zero_2bits
|
|
||||||
if _, err = r.ReadBits(8); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// level_idc
|
|
||||||
if self.LevelIdc, err = r.ReadBits(8); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// seq_parameter_set_id
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.ProfileIdc == 100 || self.ProfileIdc == 110 ||
|
|
||||||
self.ProfileIdc == 122 || self.ProfileIdc == 244 ||
|
|
||||||
self.ProfileIdc == 44 || self.ProfileIdc == 83 ||
|
|
||||||
self.ProfileIdc == 86 || self.ProfileIdc == 118 {
|
|
||||||
|
|
||||||
var chroma_format_idc uint
|
|
||||||
if chroma_format_idc, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if chroma_format_idc == 3 {
|
|
||||||
// residual_colour_transform_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bit_depth_luma_minus8
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// bit_depth_chroma_minus8
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// qpprime_y_zero_transform_bypass_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq_scaling_matrix_present_flag uint
|
|
||||||
if seq_scaling_matrix_present_flag, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if seq_scaling_matrix_present_flag != 0 {
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
var seq_scaling_list_present_flag uint
|
|
||||||
if seq_scaling_list_present_flag, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if seq_scaling_list_present_flag != 0 {
|
|
||||||
var sizeOfScalingList uint
|
|
||||||
if i < 6 {
|
|
||||||
sizeOfScalingList = 16
|
|
||||||
} else {
|
|
||||||
sizeOfScalingList = 64
|
|
||||||
}
|
|
||||||
lastScale := uint(8)
|
|
||||||
nextScale := uint(8)
|
|
||||||
for j := uint(0); j < sizeOfScalingList; j++ {
|
|
||||||
if nextScale != 0 {
|
|
||||||
var delta_scale uint
|
|
||||||
if delta_scale, err = r.ReadSE(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nextScale = (lastScale + delta_scale + 256) % 256
|
|
||||||
}
|
|
||||||
if nextScale != 0 {
|
|
||||||
lastScale = nextScale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// log2_max_frame_num_minus4
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var pic_order_cnt_type uint
|
|
||||||
if pic_order_cnt_type, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if pic_order_cnt_type == 0 {
|
|
||||||
// log2_max_pic_order_cnt_lsb_minus4
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if pic_order_cnt_type == 1 {
|
|
||||||
// delta_pic_order_always_zero_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// offset_for_non_ref_pic
|
|
||||||
if _, err = r.ReadSE(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// offset_for_top_to_bottom_field
|
|
||||||
if _, err = r.ReadSE(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var num_ref_frames_in_pic_order_cnt_cycle uint
|
|
||||||
if num_ref_frames_in_pic_order_cnt_cycle, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := uint(0); i < num_ref_frames_in_pic_order_cnt_cycle; i++ {
|
|
||||||
if _, err = r.ReadSE(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// max_num_ref_frames
|
|
||||||
if _, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// gaps_in_frame_num_value_allowed_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.MbWidth, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.MbWidth++
|
|
||||||
|
|
||||||
if self.MbHeight, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.MbHeight++
|
|
||||||
|
|
||||||
var frame_mbs_only_flag uint
|
|
||||||
if frame_mbs_only_flag, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if frame_mbs_only_flag == 0 {
|
|
||||||
// mb_adaptive_frame_field_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// direct_8x8_inference_flag
|
|
||||||
if _, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var frame_cropping_flag uint
|
|
||||||
if frame_cropping_flag, err = r.ReadBit(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if frame_cropping_flag != 0 {
|
|
||||||
if self.CropLeft, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.CropRight, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.CropTop, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.CropBottom, err = r.ReadExponentialGolombCode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.Width = (self.MbWidth * 16) - self.CropLeft*2 - self.CropRight*2
|
|
||||||
self.Height = ((2 - frame_mbs_only_flag) * self.MbHeight * 16) - self.CropTop*2 - self.CropBottom*2
|
|
||||||
|
|
||||||
res = self
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type AVCDecoderConfRecord struct {
|
|
||||||
AVCProfileIndication int
|
|
||||||
ProfileCompatibility int
|
|
||||||
AVCLevelIndication int
|
|
||||||
LengthSizeMinusOne int
|
|
||||||
SPS [][]byte
|
|
||||||
PPS [][]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateAVCDecoderConfRecord(
|
|
||||||
SPS []byte,
|
|
||||||
PPS []byte,
|
|
||||||
) (self AVCDecoderConfRecord, err error) {
|
|
||||||
if len(SPS) < 4 {
|
|
||||||
err = fmt.Errorf("invalid SPS data")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.AVCProfileIndication = int(SPS[1])
|
|
||||||
self.ProfileCompatibility = int(SPS[2])
|
|
||||||
self.AVCLevelIndication = int(SPS[3])
|
|
||||||
self.SPS = [][]byte{SPS}
|
|
||||||
self.PPS = [][]byte{PPS}
|
|
||||||
self.LengthSizeMinusOne = 3
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteAVCDecoderConfRecord(w io.Writer, self AVCDecoderConfRecord) (err error) {
|
|
||||||
if err = WriteInt(w, 1, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteInt(w, self.AVCProfileIndication, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteInt(w, self.ProfileCompatibility, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteInt(w, self.AVCLevelIndication, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteInt(w, self.LengthSizeMinusOne|0xfc, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = WriteInt(w, len(self.SPS)|0xe0, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, data := range self.SPS {
|
|
||||||
if err = WriteInt(w, len(data), 2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteBytes(w, data, len(data)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = WriteInt(w, len(self.PPS), 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, data := range self.PPS {
|
|
||||||
if err = WriteInt(w, len(data), 2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = WriteBytes(w, data, len(data)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WalkAVCDecoderConfRecord(w Walker, self AVCDecoderConfRecord) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadAVCDecoderConfRecord(r *io.LimitedReader) (self AVCDecoderConfRecord, err error) {
|
|
||||||
if _, err = ReadDummy(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.AVCProfileIndication, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.ProfileCompatibility, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.AVCLevelIndication, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if self.LengthSizeMinusOne, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.LengthSizeMinusOne &= 0x03
|
|
||||||
|
|
||||||
var n, length int
|
|
||||||
var data []byte
|
|
||||||
|
|
||||||
if n, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n &= 0x1f
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if length, err = ReadInt(r, 2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if data, err = ReadBytes(r, length); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.SPS = append(self.SPS, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n, err = ReadInt(r, 1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if length, err = ReadInt(r, 2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if data, err = ReadBytes(r, length); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.PPS = append(self.PPS, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteSampleByNALU(w io.Writer, nalu []byte) (size int, err error) {
|
|
||||||
if err = WriteInt(w, len(nalu), 4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size += 4
|
|
||||||
if _, err = w.Write(nalu); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
size += len(nalu)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TFHD_BASE_DATA_OFFSET = 0x01
|
TFHD_BASE_DATA_OFFSET = 0x01
|
||||||
TFHD_STSD_ID = 0x02
|
TFHD_STSD_ID = 0x02
|
||||||
|
@ -565,11 +565,11 @@ func WalkTrackHeader(w Walker, self *TrackHeader) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HandlerRefer struct {
|
type HandlerRefer struct {
|
||||||
Version int
|
Version int
|
||||||
Flags int
|
Flags int
|
||||||
CodecType string
|
Type string
|
||||||
SubType string
|
SubType string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) {
|
func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) {
|
||||||
@ -581,7 +581,7 @@ func ReadHandlerRefer(r *io.LimitedReader) (res *HandlerRefer, err error) {
|
|||||||
if self.Flags, err = ReadInt(r, 3); err != nil {
|
if self.Flags, err = ReadInt(r, 3); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.CodecType, err = ReadString(r, 4); err != nil {
|
if self.Type, err = ReadString(r, 4); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.SubType, err = ReadString(r, 4); err != nil {
|
if self.SubType, err = ReadString(r, 4); err != nil {
|
||||||
@ -606,7 +606,7 @@ func WriteHandlerRefer(w io.WriteSeeker, self *HandlerRefer) (err error) {
|
|||||||
if err = WriteInt(w, self.Flags, 3); err != nil {
|
if err = WriteInt(w, self.Flags, 3); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = WriteString(w, self.CodecType, 4); err != nil {
|
if err = WriteString(w, self.Type, 4); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = WriteString(w, self.SubType, 4); err != nil {
|
if err = WriteString(w, self.SubType, 4); err != nil {
|
||||||
@ -628,7 +628,7 @@ func WalkHandlerRefer(w Walker, self *HandlerRefer) {
|
|||||||
w.Name("Flags")
|
w.Name("Flags")
|
||||||
w.Int(self.Flags)
|
w.Int(self.Flags)
|
||||||
w.Name("Type")
|
w.Name("Type")
|
||||||
w.String(self.CodecType)
|
w.String(self.Type)
|
||||||
w.Name("SubType")
|
w.Name("SubType")
|
||||||
w.String(self.SubType)
|
w.String(self.SubType)
|
||||||
w.Name("Name")
|
w.Name("Name")
|
||||||
@ -1874,13 +1874,13 @@ func WalkAvc1Desc(w Walker, self *Avc1Desc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Avc1Conf struct {
|
type Avc1Conf struct {
|
||||||
Record AVCDecoderConfRecord
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadAvc1Conf(r *io.LimitedReader) (res *Avc1Conf, err error) {
|
func ReadAvc1Conf(r *io.LimitedReader) (res *Avc1Conf, err error) {
|
||||||
|
|
||||||
self := &Avc1Conf{}
|
self := &Avc1Conf{}
|
||||||
if self.Record, err = ReadAVCDecoderConfRecord(r); err != nil {
|
if self.Data, err = ReadBytes(r, int(r.N)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res = self
|
res = self
|
||||||
@ -1893,7 +1893,7 @@ func WriteAvc1Conf(w io.WriteSeeker, self *Avc1Conf) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
w = aw
|
w = aw
|
||||||
if err = WriteAVCDecoderConfRecord(w, self.Record); err != nil {
|
if err = WriteBytes(w, self.Data, len(self.Data)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = aw.Close(); err != nil {
|
if err = aw.Close(); err != nil {
|
||||||
@ -1904,7 +1904,8 @@ func WriteAvc1Conf(w io.WriteSeeker, self *Avc1Conf) (err error) {
|
|||||||
func WalkAvc1Conf(w Walker, self *Avc1Conf) {
|
func WalkAvc1Conf(w Walker, self *Avc1Conf) {
|
||||||
|
|
||||||
w.StartStruct("Avc1Conf")
|
w.StartStruct("Avc1Conf")
|
||||||
WalkAVCDecoderConfRecord(w, self.Record)
|
w.Name("Data")
|
||||||
|
w.Bytes(self.Data)
|
||||||
w.EndStruct()
|
w.EndStruct()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
package atom
|
package atom
|
||||||
|
|
||||||
func GetAVCDecoderConfRecordByTrack(stream *Track) (record *AVCDecoderConfRecord) {
|
func GetAvc1ConfByTrack(stream *Track) (avc1 *Avc1Conf) {
|
||||||
if media := stream.Media; media != nil {
|
if media := stream.Media; media != nil {
|
||||||
if info := media.Info; info != nil {
|
if info := media.Info; info != nil {
|
||||||
if sample := info.Sample; sample != nil {
|
if sample := info.Sample; sample != nil {
|
||||||
if desc := sample.SampleDesc; desc != nil {
|
if desc := sample.SampleDesc; desc != nil {
|
||||||
if avc1 := desc.Avc1Desc; avc1 != nil {
|
if avc1 := desc.Avc1Desc; avc1 != nil {
|
||||||
if conf := avc1.Conf; conf != nil {
|
return avc1.Conf
|
||||||
return &conf.Record
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,9 +20,7 @@ func GetMp4aDescByTrack(stream *Track) (mp4a *Mp4aDesc) {
|
|||||||
if info := media.Info; info != nil {
|
if info := media.Info; info != nil {
|
||||||
if sample := info.Sample; sample != nil {
|
if sample := info.Sample; sample != nil {
|
||||||
if desc := sample.SampleDesc; desc != nil {
|
if desc := sample.SampleDesc; desc != nil {
|
||||||
if mp4a = desc.Mp4aDesc; mp4a != nil {
|
return desc.Mp4aDesc
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
demuxer.go
78
demuxer.go
@ -16,6 +16,13 @@ type Demuxer struct {
|
|||||||
movieAtom *atom.Movie
|
movieAtom *atom.Movie
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Demuxer) Streams() (streams []av.Stream) {
|
||||||
|
for _, stream := range(self.streams) {
|
||||||
|
streams = append(streams, stream)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Demuxer) ReadHeader() (err error) {
|
func (self *Demuxer) ReadHeader() (err error) {
|
||||||
var N int64
|
var N int64
|
||||||
var moov *atom.Movie
|
var moov *atom.Movie
|
||||||
@ -54,10 +61,11 @@ func (self *Demuxer) ReadHeader() (err error) {
|
|||||||
self.movieAtom = moov
|
self.movieAtom = moov
|
||||||
|
|
||||||
self.streams = []*Stream{}
|
self.streams = []*Stream{}
|
||||||
for _, atrack := range moov.Tracks {
|
for i, atrack := range moov.Tracks {
|
||||||
stream := &Stream{
|
stream := &Stream{
|
||||||
trackAtom: atrack,
|
trackAtom: atrack,
|
||||||
r: self.R,
|
r: self.R,
|
||||||
|
idx: i,
|
||||||
}
|
}
|
||||||
if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil {
|
if atrack.Media != nil && atrack.Media.Info != nil && atrack.Media.Info.Sample != nil {
|
||||||
stream.sample = atrack.Media.Info.Sample
|
stream.sample = atrack.Media.Info.Sample
|
||||||
@ -66,21 +74,25 @@ func (self *Demuxer) ReadHeader() (err error) {
|
|||||||
err = fmt.Errorf("sample table not found")
|
err = fmt.Errorf("sample table not found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if record := atom.GetAVCDecoderConfRecordByTrack(atrack); record != nil {
|
|
||||||
if len(record.PPS) > 0 {
|
if avc1 := atom.GetAvc1ConfByTrack(atrack); avc1 != nil {
|
||||||
stream.pps = record.PPS[0]
|
|
||||||
}
|
|
||||||
if len(record.SPS) > 0 {
|
|
||||||
stream.sps = record.SPS[0]
|
|
||||||
}
|
|
||||||
stream.SetType(av.H264)
|
stream.SetType(av.H264)
|
||||||
self.streams = append(self.streams, stream)
|
if err = stream.SetCodecData(avc1.Data); err != nil {
|
||||||
} else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil {
|
return
|
||||||
if config, err := isom.ReadElemStreamDescAAC(bytes.NewReader(mp4a.Conf.Data)); err == nil {
|
|
||||||
stream.mpeg4AudioConfig = config.Complete()
|
|
||||||
stream.SetType(av.AAC)
|
|
||||||
self.streams = append(self.streams, stream)
|
|
||||||
}
|
}
|
||||||
|
self.streams = append(self.streams, stream)
|
||||||
|
|
||||||
|
} else if mp4a := atom.GetMp4aDescByTrack(atrack); mp4a != nil && mp4a.Conf != nil {
|
||||||
|
stream.SetType(av.AAC)
|
||||||
|
var config []byte
|
||||||
|
if config, err = isom.ReadElemStreamDesc(bytes.NewReader(mp4a.Conf.Data)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = stream.SetCodecData(config); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.streams = append(self.streams, stream)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +262,7 @@ func (self *Stream) incSampleIndex() {
|
|||||||
self.sampleIndex++
|
self.sampleIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Stream) SampleCount() int {
|
func (self *Stream) sampleCount() int {
|
||||||
if self.sample.SampleSize.SampleSize == 0 {
|
if self.sample.SampleSize.SampleSize == 0 {
|
||||||
chunkGroupIndex := 0
|
chunkGroupIndex := 0
|
||||||
count := 0
|
count := 0
|
||||||
@ -269,6 +281,23 @@ func (self *Stream) SampleCount() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
|
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
|
||||||
|
var choose *Stream
|
||||||
|
for _, stream := range(self.streams) {
|
||||||
|
if choose == nil || stream.dts < choose.dts {
|
||||||
|
choose = stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pkt.StreamIdx = choose.idx
|
||||||
|
pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data, err = choose.readSample()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Demuxer) SeekToTime(time float64) (err error) {
|
||||||
|
for _, stream := range(self.streams) {
|
||||||
|
if err = stream.seekToTime(time); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,28 +343,20 @@ func (self *Stream) readSample() (pts int64, dts int64, isKeyFrame bool, data []
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Stream) Duration() float64 {
|
func (self *Stream) duration() float64 {
|
||||||
total := int64(0)
|
total := int64(0)
|
||||||
for _, entry := range self.sample.TimeToSample.Entries {
|
for _, entry := range self.sample.TimeToSample.Entries {
|
||||||
total += int64(entry.Duration * entry.Count)
|
total += int64(entry.Duration * entry.Count)
|
||||||
}
|
}
|
||||||
return float64(total) / float64(self.trackAtom.Media.Header.TimeScale)
|
return float64(total) / float64(self.TimeScale())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Stream) CurSampleIndex() int {
|
func (self *Stream) seekToTime(time float64) error {
|
||||||
return self.sampleIndex
|
index := self.timeToSampleIndex(time)
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Stream) SeekToTime(time float64) error {
|
|
||||||
index := self.TimeToSampleIndex(time)
|
|
||||||
return self.setSampleIndex(index)
|
return self.setSampleIndex(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Stream) SeekToSampleIndex(index int) error {
|
func (self *Stream) timeToSampleIndex(time float64) int {
|
||||||
return self.setSampleIndex(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *Stream) TimeToSampleIndex(time float64) int {
|
|
||||||
targetTs := self.TimeToTs(time)
|
targetTs := self.TimeToTs(time)
|
||||||
targetIndex := 0
|
targetIndex := 0
|
||||||
|
|
||||||
@ -374,3 +395,4 @@ func (self *Stream) TimeToSampleIndex(time float64) int {
|
|||||||
|
|
||||||
return targetIndex
|
return targetIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
312
isom/isom.go
312
isom/isom.go
@ -9,6 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// copied from libavformat/isom.h
|
// copied from libavformat/isom.h
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MP4ESDescrTag = 3
|
MP4ESDescrTag = 3
|
||||||
MP4DecConfigDescrTag = 4
|
MP4DecConfigDescrTag = 4
|
||||||
@ -18,295 +19,6 @@ const (
|
|||||||
var debugReader = false
|
var debugReader = false
|
||||||
var debugWriter = false
|
var debugWriter = false
|
||||||
|
|
||||||
// copied from libavcodec/mpeg4audio.h
|
|
||||||
const (
|
|
||||||
AOT_AAC_MAIN = 1 + iota ///< Y Main
|
|
||||||
AOT_AAC_LC ///< Y Low Complexity
|
|
||||||
AOT_AAC_SSR ///< N (code in SoC repo) Scalable Sample Rate
|
|
||||||
AOT_AAC_LTP ///< Y Long Term Prediction
|
|
||||||
AOT_SBR ///< Y Spectral Band Replication
|
|
||||||
AOT_AAC_SCALABLE ///< N Scalable
|
|
||||||
AOT_TWINVQ ///< N Twin Vector Quantizer
|
|
||||||
AOT_CELP ///< N Code Excited Linear Prediction
|
|
||||||
AOT_HVXC ///< N Harmonic Vector eXcitation Coding
|
|
||||||
AOT_TTSI = 12 + iota ///< N Text-To-Speech Interface
|
|
||||||
AOT_MAINSYNTH ///< N Main Synthesis
|
|
||||||
AOT_WAVESYNTH ///< N Wavetable Synthesis
|
|
||||||
AOT_MIDI ///< N General MIDI
|
|
||||||
AOT_SAFX ///< N Algorithmic Synthesis and Audio Effects
|
|
||||||
AOT_ER_AAC_LC ///< N Error Resilient Low Complexity
|
|
||||||
AOT_ER_AAC_LTP = 19 + iota ///< N Error Resilient Long Term Prediction
|
|
||||||
AOT_ER_AAC_SCALABLE ///< N Error Resilient Scalable
|
|
||||||
AOT_ER_TWINVQ ///< N Error Resilient Twin Vector Quantizer
|
|
||||||
AOT_ER_BSAC ///< N Error Resilient Bit-Sliced Arithmetic Coding
|
|
||||||
AOT_ER_AAC_LD ///< N Error Resilient Low Delay
|
|
||||||
AOT_ER_CELP ///< N Error Resilient Code Excited Linear Prediction
|
|
||||||
AOT_ER_HVXC ///< N Error Resilient Harmonic Vector eXcitation Coding
|
|
||||||
AOT_ER_HILN ///< N Error Resilient Harmonic and Individual Lines plus Noise
|
|
||||||
AOT_ER_PARAM ///< N Error Resilient Parametric
|
|
||||||
AOT_SSC ///< N SinuSoidal Coding
|
|
||||||
AOT_PS ///< N Parametric Stereo
|
|
||||||
AOT_SURROUND ///< N MPEG Surround
|
|
||||||
AOT_ESCAPE ///< Y Escape Value
|
|
||||||
AOT_L1 ///< Y Layer 1
|
|
||||||
AOT_L2 ///< Y Layer 2
|
|
||||||
AOT_L3 ///< Y Layer 3
|
|
||||||
AOT_DST ///< N Direct Stream Transfer
|
|
||||||
AOT_ALS ///< Y Audio LosslesS
|
|
||||||
AOT_SLS ///< N Scalable LosslesS
|
|
||||||
AOT_SLS_NON_CORE ///< N Scalable LosslesS (non core)
|
|
||||||
AOT_ER_AAC_ELD ///< N Error Resilient Enhanced Low Delay
|
|
||||||
AOT_SMR_SIMPLE ///< N Symbolic Music Representation Simple
|
|
||||||
AOT_SMR_MAIN ///< N Symbolic Music Representation Main
|
|
||||||
AOT_USAC_NOSBR ///< N Unified Speech and Audio Coding (no SBR)
|
|
||||||
AOT_SAOC ///< N Spatial Audio Object Coding
|
|
||||||
AOT_LD_SURROUND ///< N Low Delay MPEG Surround
|
|
||||||
AOT_USAC ///< N Unified Speech and Audio Coding
|
|
||||||
)
|
|
||||||
|
|
||||||
type MPEG4AudioConfig struct {
|
|
||||||
SampleRate int
|
|
||||||
ChannelCount int
|
|
||||||
ObjectType uint
|
|
||||||
SampleRateIndex uint
|
|
||||||
ChannelConfig uint
|
|
||||||
}
|
|
||||||
|
|
||||||
var sampleRateTable = []int{
|
|
||||||
96000, 88200, 64000, 48000, 44100, 32000,
|
|
||||||
24000, 22050, 16000, 12000, 11025, 8000, 7350,
|
|
||||||
}
|
|
||||||
|
|
||||||
var chanConfigTable = []int{
|
|
||||||
0, 1, 2, 3, 4, 5, 6, 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsADTSFrame(frames []byte) bool {
|
|
||||||
return len(frames) > 7 && frames[0] == 0xff && frames[1]&0xf0 == 0xf0
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadADTSFrame(frame []byte) (config MPEG4AudioConfig, payload []byte, samples int, framelen int, err error) {
|
|
||||||
if !IsADTSFrame(frame) {
|
|
||||||
err = fmt.Errorf("not adts frame")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
config.ObjectType = uint(frame[2]>>6) + 1
|
|
||||||
config.SampleRateIndex = uint(frame[2] >> 2 & 0xf)
|
|
||||||
config.ChannelConfig = uint(frame[2]<<2&0x4 | frame[3]>>6&0x3)
|
|
||||||
framelen = int(frame[3]&0x3)<<11 | int(frame[4])<<3 | int(frame[5]>>5)
|
|
||||||
samples = (int(frame[6]&0x3) + 1) * 1024
|
|
||||||
hdrlen := 7
|
|
||||||
if frame[1]&0x1 == 0 {
|
|
||||||
hdrlen = 9
|
|
||||||
}
|
|
||||||
if framelen < hdrlen || len(frame) < framelen {
|
|
||||||
err = fmt.Errorf("invalid adts header length")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
payload = frame[hdrlen:framelen]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeADTSHeader(config MPEG4AudioConfig, samples int, payloadLength int) (header []byte) {
|
|
||||||
payloadLength += 7
|
|
||||||
//AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
|
|
||||||
header = []byte{0xff, 0xf1, 0x50, 0x80, 0x043, 0xff, 0xcd}
|
|
||||||
//config.ObjectType = uint(frames[2]>>6)+1
|
|
||||||
//config.SampleRateIndex = uint(frames[2]>>2&0xf)
|
|
||||||
//config.ChannelConfig = uint(frames[2]<<2&0x4|frames[3]>>6&0x3)
|
|
||||||
header[2] = (byte(config.ObjectType-1)&0x3)<<6 | (byte(config.SampleRateIndex)&0xf)<<2 | byte(config.ChannelConfig>>2)&0x1
|
|
||||||
header[3] = header[3]&0x3f | byte(config.ChannelConfig&0x3)<<6
|
|
||||||
header[3] = header[3]&0xfc | byte(payloadLength>>11)&0x3
|
|
||||||
header[4] = byte(payloadLength >> 3)
|
|
||||||
header[5] = header[5]&0x1f | (byte(payloadLength)&0x7)<<5
|
|
||||||
header[6] = header[6]&0xfc | byte(samples/1024-1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractADTSFrames(frames []byte) (config MPEG4AudioConfig, payload []byte, samples int, err error) {
|
|
||||||
for len(frames) > 0 {
|
|
||||||
var n, framelen int
|
|
||||||
if config, payload, n, framelen, err = ReadADTSFrame(frames); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
frames = frames[framelen:]
|
|
||||||
samples += n
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ReadADTSHeader(data []byte) (config MPEG4AudioConfig, frameLength int) {
|
|
||||||
br := &bits.Reader{R: bytes.NewReader(data)}
|
|
||||||
var i uint
|
|
||||||
|
|
||||||
//Structure
|
|
||||||
//AAAAAAAA AAAABCCD EEFFFFGH HHIJKLMM MMMMMMMM MMMOOOOO OOOOOOPP (QQQQQQQQ QQQQQQQQ)
|
|
||||||
//Header consists of 7 or 9 bytes (without or with CRC).
|
|
||||||
|
|
||||||
// 2 bytes
|
|
||||||
//A 12 syncword 0xFFF, all bits must be 1
|
|
||||||
br.ReadBits(12)
|
|
||||||
//B 1 MPEG Version: 0 for MPEG-4, 1 for MPEG-2
|
|
||||||
br.ReadBits(1)
|
|
||||||
//C 2 Layer: always 0
|
|
||||||
br.ReadBits(2)
|
|
||||||
//D 1 protection absent, Warning, set to 1 if there is no CRC and 0 if there is CRC
|
|
||||||
br.ReadBits(1)
|
|
||||||
|
|
||||||
//E 2 profile, the MPEG-4 Audio Object Type minus 1
|
|
||||||
config.ObjectType, _ = br.ReadBits(2)
|
|
||||||
config.ObjectType++
|
|
||||||
//F 4 MPEG-4 Sampling Frequency Index (15 is forbidden)
|
|
||||||
config.SampleRateIndex, _ = br.ReadBits(4)
|
|
||||||
//G 1 private bit, guaranteed never to be used by MPEG, set to 0 when encoding, ignore when decoding
|
|
||||||
br.ReadBits(1)
|
|
||||||
//H 3 MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE)
|
|
||||||
config.ChannelConfig, _ = br.ReadBits(3)
|
|
||||||
//I 1 originality, set to 0 when encoding, ignore when decoding
|
|
||||||
br.ReadBits(1)
|
|
||||||
//J 1 home, set to 0 when encoding, ignore when decoding
|
|
||||||
br.ReadBits(1)
|
|
||||||
//K 1 copyrighted id bit, the next bit of a centrally registered copyright identifier, set to 0 when encoding, ignore when decoding
|
|
||||||
br.ReadBits(1)
|
|
||||||
//L 1 copyright id start, signals that this frame's copyright id bit is the first bit of the copyright id, set to 0 when encoding, ignore when decoding
|
|
||||||
br.ReadBits(1)
|
|
||||||
|
|
||||||
//M 13 frame length, this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame)
|
|
||||||
i, _ = br.ReadBits(13)
|
|
||||||
frameLength = int(i)
|
|
||||||
//O 11 Buffer fullness
|
|
||||||
br.ReadBits(11)
|
|
||||||
//P 2 Number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame
|
|
||||||
br.ReadBits(2)
|
|
||||||
|
|
||||||
//Q 16 CRC if protection absent is 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func readObjectType(r *bits.Reader) (objectType uint, err error) {
|
|
||||||
if objectType, err = r.ReadBits(5); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if objectType == AOT_ESCAPE {
|
|
||||||
var i uint
|
|
||||||
if i, err = r.ReadBits(6); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
objectType = 32 + i
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeObjectType(w *bits.Writer, objectType uint) (err error) {
|
|
||||||
if objectType >= 32 {
|
|
||||||
if err = w.WriteBits(AOT_ESCAPE, 5); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = w.WriteBits(objectType-32, 6); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = w.WriteBits(objectType, 5); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func readSampleRateIndex(r *bits.Reader) (index uint, err error) {
|
|
||||||
if index, err = r.ReadBits(4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if index == 0xf {
|
|
||||||
if index, err = r.ReadBits(24); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeSampleRateIndex(w *bits.Writer, index uint) (err error) {
|
|
||||||
if index >= 0xf {
|
|
||||||
if err = w.WriteBits(0xf, 4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = w.WriteBits(index, 24); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err = w.WriteBits(index, 4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self MPEG4AudioConfig) IsValid() bool {
|
|
||||||
return self.ObjectType > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self MPEG4AudioConfig) Complete() (config MPEG4AudioConfig) {
|
|
||||||
config = self
|
|
||||||
if int(config.SampleRateIndex) < len(sampleRateTable) {
|
|
||||||
config.SampleRate = sampleRateTable[config.SampleRateIndex]
|
|
||||||
}
|
|
||||||
if int(config.ChannelConfig) < len(chanConfigTable) {
|
|
||||||
config.ChannelCount = chanConfigTable[config.ChannelConfig]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// copied from libavcodec/mpeg4audio.c avpriv_mpeg4audio_get_config()
|
|
||||||
func ReadMPEG4AudioConfig(r io.Reader) (config MPEG4AudioConfig, err error) {
|
|
||||||
br := &bits.Reader{R: r}
|
|
||||||
|
|
||||||
if config.ObjectType, err = readObjectType(br); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if config.SampleRateIndex, err = readSampleRateIndex(br); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if config.ChannelConfig, err = br.ReadBits(4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteMPEG4AudioConfig(w io.Writer, config MPEG4AudioConfig) (err error) {
|
|
||||||
bw := &bits.Writer{W: w}
|
|
||||||
|
|
||||||
if err = writeObjectType(bw, config.ObjectType); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.SampleRateIndex == 0 {
|
|
||||||
for i, rate := range sampleRateTable {
|
|
||||||
if rate == config.SampleRate {
|
|
||||||
config.SampleRateIndex = uint(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = writeSampleRateIndex(bw, config.SampleRateIndex); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.ChannelConfig == 0 {
|
|
||||||
for i, count := range chanConfigTable {
|
|
||||||
if count == config.ChannelCount {
|
|
||||||
config.ChannelConfig = uint(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = bw.WriteBits(config.ChannelConfig, 4); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = bw.FlushBits(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func readDesc(r io.Reader) (tag uint, data []byte, err error) {
|
func readDesc(r io.Reader) (tag uint, data []byte, err error) {
|
||||||
if tag, err = bits.ReadUIntBE(r, 8); err != nil {
|
if tag, err = bits.ReadUIntBE(r, 8); err != nil {
|
||||||
return
|
return
|
||||||
@ -518,28 +230,12 @@ func ReadElemStreamDesc(r io.Reader) (decConfig []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadElemStreamDescAAC(r io.Reader) (config MPEG4AudioConfig, err error) {
|
func WriteElemStreamDesc(w io.Writer, decConfig []byte, trackId uint) (err error) {
|
||||||
var data []byte
|
|
||||||
if data, err = ReadElemStreamDesc(r); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if debugReader {
|
|
||||||
println("decConfig: ", len(data))
|
|
||||||
}
|
|
||||||
if config, err = ReadMPEG4AudioConfig(bytes.NewReader(data)); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func WriteElemStreamDescAAC(w io.Writer, config MPEG4AudioConfig, trackId uint) (err error) {
|
|
||||||
// MP4ESDescrTag(ESDesc MP4DecConfigDescrTag(objectId streamType bufSize avgBitrate MP4DecSpecificDescrTag(decConfig)))
|
// MP4ESDescrTag(ESDesc MP4DecConfigDescrTag(objectId streamType bufSize avgBitrate MP4DecSpecificDescrTag(decConfig)))
|
||||||
|
|
||||||
buf := &bytes.Buffer{}
|
data := decConfig
|
||||||
WriteMPEG4AudioConfig(buf, config)
|
|
||||||
data := buf.Bytes()
|
|
||||||
|
|
||||||
buf = &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
// 0x40 = ObjectType AAC
|
// 0x40 = ObjectType AAC
|
||||||
// 0x15 = Audiostream
|
// 0x15 = Audiostream
|
||||||
writeDecConfDesc(buf, 0x40, 0x15, data)
|
writeDecConfDesc(buf, 0x40, 0x15, data)
|
||||||
|
7
muxer.go
7
muxer.go
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/nareix/mp4/atom"
|
"github.com/nareix/mp4/atom"
|
||||||
"github.com/nareix/mp4/isom"
|
"github.com/nareix/mp4/isom"
|
||||||
"github.com/nareix/codec/h264parser"
|
"github.com/nareix/codec/h264parser"
|
||||||
|
"github.com/nareix/codec/aacparser"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -148,12 +149,12 @@ func (self *Muxer) WriteSample(pkt av.Packet) (err error) {
|
|||||||
pts, dts, isKeyFrame, frame := pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data
|
pts, dts, isKeyFrame, frame := pkt.Pts, pkt.Dts, pkt.IsKeyFrame, pkt.Data
|
||||||
stream := self.streams[pkt.StreamIdx]
|
stream := self.streams[pkt.StreamIdx]
|
||||||
|
|
||||||
if stream.Type() == av.AAC && isom.IsADTSFrame(frame) {
|
if stream.Type() == av.AAC && aacparser.IsADTSFrame(frame) {
|
||||||
for len(frame) > 0 {
|
for len(frame) > 0 {
|
||||||
var payload []byte
|
var payload []byte
|
||||||
var samples int
|
var samples int
|
||||||
var framelen int
|
var framelen int
|
||||||
if _, payload, samples, framelen, err = isom.ReadADTSFrame(frame); err != nil {
|
if _, payload, samples, framelen, err = aacparser.ReadADTSFrame(frame); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delta := int64(samples) * stream.TimeScale() / int64(stream.SampleRate())
|
delta := int64(samples) * stream.TimeScale() / int64(stream.SampleRate())
|
||||||
@ -247,7 +248,7 @@ func (self *Muxer) WriteTrailer() (err error) {
|
|||||||
if err = stream.fillTrackAtom(); err != nil {
|
if err = stream.fillTrackAtom(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dur := stream.Duration()
|
dur := stream.duration()
|
||||||
stream.trackAtom.Header.Duration = int(float64(timeScale) * dur)
|
stream.trackAtom.Header.Duration = int(float64(timeScale) * dur)
|
||||||
if dur > maxDur {
|
if dur > maxDur {
|
||||||
maxDur = dur
|
maxDur = dur
|
||||||
|
41
stream.go
Normal file
41
stream.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package mp4
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nareix/av"
|
||||||
|
"github.com/nareix/mp4/atom"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stream struct {
|
||||||
|
av.StreamCommon
|
||||||
|
|
||||||
|
trackAtom *atom.Track
|
||||||
|
r io.ReadSeeker
|
||||||
|
idx int
|
||||||
|
|
||||||
|
muxer *Muxer
|
||||||
|
|
||||||
|
sample *atom.SampleTable
|
||||||
|
sampleIndex int
|
||||||
|
|
||||||
|
sampleOffsetInChunk int64
|
||||||
|
syncSampleIndex int
|
||||||
|
|
||||||
|
dts int64
|
||||||
|
sttsEntryIndex int
|
||||||
|
sampleIndexInSttsEntry int
|
||||||
|
|
||||||
|
cttsEntryIndex int
|
||||||
|
sampleIndexInCttsEntry int
|
||||||
|
|
||||||
|
chunkGroupIndex int
|
||||||
|
chunkIndex int
|
||||||
|
sampleIndexInChunk int
|
||||||
|
|
||||||
|
sttsEntry *atom.TimeToSampleEntry
|
||||||
|
cttsEntry *atom.CompositionOffsetEntry
|
||||||
|
writeMdat func([]byte) (int64, error)
|
||||||
|
lastDts int64
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user