mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
339 lines
8.4 KiB
Go
339 lines
8.4 KiB
Go
package box
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
)
|
|
|
|
// aligned(8) abstract class SampleEntry (unsigned int(32) format) extends Box(format){
|
|
// const unsigned int(8)[6] reserved = 0;
|
|
// unsigned int(16) data_reference_index;
|
|
// }
|
|
|
|
type SampleEntry struct {
|
|
BaseBox
|
|
DataReferenceIndex uint16
|
|
}
|
|
|
|
// class HintSampleEntry() extends SampleEntry (protocol) {
|
|
// unsigned int(8) data [];
|
|
// }
|
|
|
|
type HintSampleEntry struct {
|
|
SampleEntry
|
|
|
|
Data []byte
|
|
}
|
|
|
|
// class AudioSampleEntry(codingname) extends SampleEntry (codingname){
|
|
// const unsigned int(32)[2] reserved = 0;
|
|
// template unsigned int(16) channelcount = 2;
|
|
// template unsigned int(16) samplesize = 16;
|
|
// unsigned int(16) pre_defined = 0;
|
|
// const unsigned int(16) reserved = 0 ;
|
|
// template unsigned int(32) samplerate = { default samplerate of media}<<16;
|
|
// }
|
|
|
|
type AudioSampleEntry struct {
|
|
SampleEntry
|
|
Version uint16 // ffmpeg mov.c mov_parse_stsd_audio
|
|
ChannelCount uint16
|
|
SampleSize uint16
|
|
Samplerate uint32
|
|
ExtraData IBox
|
|
}
|
|
|
|
func (s *SampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
|
var tmp [8]byte
|
|
binary.BigEndian.PutUint16(tmp[6:], s.DataReferenceIndex)
|
|
_, err = w.Write(tmp[:])
|
|
return 8, err
|
|
}
|
|
|
|
func (s *SampleEntry) Unmarshal(buf []byte) {
|
|
s.DataReferenceIndex = binary.BigEndian.Uint16(buf[6:])
|
|
}
|
|
|
|
func CreateAudioSampleEntry(codecName BoxType, channelCount uint16, sampleSize uint16, samplerate uint32, extraData IBox) *AudioSampleEntry {
|
|
size := 28 + BasicBoxLen
|
|
if extraData != nil {
|
|
size += int(extraData.Size())
|
|
}
|
|
return &AudioSampleEntry{
|
|
SampleEntry: SampleEntry{
|
|
BaseBox: BaseBox{
|
|
typ: codecName,
|
|
size: uint32(size),
|
|
},
|
|
DataReferenceIndex: 1,
|
|
},
|
|
Version: 0,
|
|
ChannelCount: channelCount,
|
|
SampleSize: sampleSize,
|
|
Samplerate: samplerate,
|
|
ExtraData: extraData,
|
|
}
|
|
}
|
|
|
|
func (audio *AudioSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
|
n, err = audio.SampleEntry.WriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var buf [20]byte
|
|
binary.BigEndian.PutUint16(buf[:], audio.Version)
|
|
binary.BigEndian.PutUint16(buf[8:], audio.ChannelCount)
|
|
binary.BigEndian.PutUint16(buf[10:], audio.SampleSize)
|
|
binary.BigEndian.PutUint32(buf[16:], audio.Samplerate<<16)
|
|
_, err = w.Write(buf[:])
|
|
n += 20
|
|
var nn int64
|
|
if audio.ExtraData != nil {
|
|
nn, err = WriteTo(w, audio.ExtraData)
|
|
if err != nil {
|
|
return
|
|
}
|
|
n += nn
|
|
}
|
|
return
|
|
|
|
}
|
|
|
|
func (audio *AudioSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
|
audio.SampleEntry.Unmarshal(buf)
|
|
buf = buf[8:]
|
|
audio.Version = binary.BigEndian.Uint16(buf)
|
|
audio.ChannelCount = binary.BigEndian.Uint16(buf[8:])
|
|
audio.SampleSize = binary.BigEndian.Uint16(buf[10:])
|
|
|
|
audio.Samplerate = binary.BigEndian.Uint32(buf[16:]) >> 16
|
|
if len(buf) > 20 {
|
|
box, err := ReadFrom(bytes.NewReader(buf[20:]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
audio.ExtraData = box
|
|
}
|
|
|
|
return audio, nil
|
|
}
|
|
|
|
// class VisualSampleEntry(codingname) extends SampleEntry (codingname){
|
|
// unsigned int(16) pre_defined = 0;
|
|
// const unsigned int(16) reserved = 0;
|
|
// unsigned int(32)[3] pre_defined = 0;
|
|
// unsigned int(16) width;
|
|
// unsigned int(16) height;
|
|
// template unsigned int(32) horizresolution = 0x00480000; // 72 dpi
|
|
// template unsigned int(32) vertresolution = 0x00480000; // 72 dpi
|
|
// const unsigned int(32) reserved = 0;
|
|
// template unsigned int(16) frame_count = 1;
|
|
// string[32] compressorname;
|
|
// template unsigned int(16) depth = 0x0018;
|
|
// int(16) pre_defined = -1;
|
|
// // other boxes from derived specifications
|
|
// CleanApertureBox clap; // optional
|
|
// PixelAspectRatioBox pasp; // optional
|
|
// }
|
|
|
|
type VisualSampleEntry struct {
|
|
SampleEntry
|
|
Width, Height uint16
|
|
Horizresolution, Vertresolution uint32
|
|
FrameCount uint16
|
|
Compressorname [32]byte
|
|
Depth uint16
|
|
ExtraData IBox
|
|
}
|
|
|
|
func CreateVisualSampleEntry(codecName BoxType, width, height uint16, extraData IBox) *VisualSampleEntry {
|
|
size := 78 + BasicBoxLen
|
|
if extraData != nil {
|
|
size += int(extraData.Size())
|
|
}
|
|
return &VisualSampleEntry{
|
|
SampleEntry: SampleEntry{
|
|
BaseBox: BaseBox{
|
|
typ: codecName,
|
|
size: uint32(size),
|
|
},
|
|
DataReferenceIndex: 1,
|
|
},
|
|
Width: width,
|
|
Height: height,
|
|
Horizresolution: 0x00480000,
|
|
Vertresolution: 0x00480000,
|
|
FrameCount: 1,
|
|
Depth: 0x0018,
|
|
ExtraData: extraData,
|
|
}
|
|
}
|
|
|
|
func (visual *VisualSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
|
n, err = visual.SampleEntry.WriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var buf [70]byte // 16(pre_defined) + 2(width) + 2(height) + 4(horiz) + 4(vert) + 4(reserved) + 2(frame) + 32(compressor) + 2(depth) + 2(pre_defined)
|
|
binary.BigEndian.PutUint16(buf[16:], visual.Width)
|
|
binary.BigEndian.PutUint16(buf[18:], visual.Height)
|
|
binary.BigEndian.PutUint32(buf[20:], visual.Horizresolution)
|
|
binary.BigEndian.PutUint32(buf[24:], visual.Vertresolution)
|
|
binary.BigEndian.PutUint16(buf[32:], visual.FrameCount)
|
|
copy(buf[34:66], visual.Compressorname[:])
|
|
binary.BigEndian.PutUint16(buf[66:], visual.Depth)
|
|
binary.BigEndian.PutUint16(buf[68:], 0xFFFF) // pre_defined = -1
|
|
|
|
_, err = w.Write(buf[:])
|
|
n += 70
|
|
var nn int64
|
|
if visual.ExtraData != nil {
|
|
nn, err = WriteTo(w, visual.ExtraData)
|
|
if err != nil {
|
|
return
|
|
}
|
|
n += nn
|
|
}
|
|
return
|
|
}
|
|
|
|
func (visual *VisualSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
|
visual.SampleEntry.Unmarshal(buf)
|
|
buf = buf[24:] // Skip 8 bytes from SampleEntry + 16 bytes pre_defined
|
|
visual.Width = binary.BigEndian.Uint16(buf[0:])
|
|
visual.Height = binary.BigEndian.Uint16(buf[2:])
|
|
|
|
visual.Horizresolution = binary.BigEndian.Uint32(buf[4:])
|
|
visual.Vertresolution = binary.BigEndian.Uint32(buf[8:])
|
|
visual.FrameCount = binary.BigEndian.Uint16(buf[16:])
|
|
copy(visual.Compressorname[:], buf[18:50])
|
|
visual.Depth = binary.BigEndian.Uint16(buf[50:])
|
|
// 52 pre-defined
|
|
if len(buf) > 54 {
|
|
box, err := ReadFrom(bytes.NewReader(buf[54:]))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
visual.ExtraData = box
|
|
}
|
|
|
|
return visual, nil
|
|
}
|
|
|
|
// aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox('stsd', 0, 0){
|
|
// int i ;
|
|
// unsigned int(32) entry_count;
|
|
// for (i = 1 ; i <= entry_count ; i++){
|
|
// switch (handler_type){
|
|
// case 'soun': // for audio tracks
|
|
// AudioSampleEntry();
|
|
// break;
|
|
// case 'vide': // for video tracks
|
|
// VisualSampleEntry();
|
|
// break;
|
|
// case 'hint': // Hint track
|
|
// HintSampleEntry();
|
|
// break;
|
|
// case 'meta': // Metadata track
|
|
// MetadataSampleEntry();
|
|
// break;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
type SampleEntryType uint8
|
|
|
|
const (
|
|
SAMPLE_AUDIO SampleEntryType = iota
|
|
SAMPLE_VIDEO
|
|
)
|
|
|
|
type STSDBox struct {
|
|
FullBox
|
|
Entries []IBox
|
|
}
|
|
|
|
func CreateSTSDBox(entries ...IBox) *STSDBox {
|
|
childSize := 0
|
|
for _, entry := range entries {
|
|
childSize += int(entry.Size())
|
|
}
|
|
return &STSDBox{
|
|
FullBox: FullBox{
|
|
BaseBox: BaseBox{
|
|
typ: TypeSTSD,
|
|
size: uint32(FullBoxLen + 4 + childSize),
|
|
},
|
|
},
|
|
Entries: entries,
|
|
}
|
|
}
|
|
|
|
func (stsd *STSDBox) Unmarshal(buf []byte) (IBox, error) {
|
|
stsd.Entries = make([]IBox, 0, binary.BigEndian.Uint32(buf))
|
|
r := bytes.NewReader(buf[4:])
|
|
for {
|
|
box, err := ReadFrom(r)
|
|
if err != nil {
|
|
break
|
|
}
|
|
stsd.Entries = append(stsd.Entries, box)
|
|
}
|
|
return stsd, nil
|
|
}
|
|
|
|
func (stsd *STSDBox) WriteTo(w io.Writer) (n int64, err error) {
|
|
var tmp [4]byte
|
|
var nn int64
|
|
binary.BigEndian.PutUint32(tmp[:], uint32(len(stsd.Entries)))
|
|
_, err = w.Write(tmp[:])
|
|
if err != nil {
|
|
return
|
|
}
|
|
n += 4
|
|
for _, entry := range stsd.Entries {
|
|
nn, err = WriteTo(w, entry)
|
|
if err != nil {
|
|
return
|
|
}
|
|
n += nn
|
|
}
|
|
return int64(n), nil
|
|
}
|
|
|
|
func (h *HintSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
|
h.SampleEntry.Unmarshal(buf)
|
|
h.Data = buf[8:]
|
|
return h, nil
|
|
}
|
|
|
|
func (h *HintSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
|
var offset int64
|
|
offset, err = h.SampleEntry.WriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
_, err = w.Write(h.Data)
|
|
return offset + int64(len(h.Data)), err
|
|
}
|
|
|
|
func init() {
|
|
RegisterBox[*STSDBox](TypeSTSD)
|
|
RegisterBox[*AudioSampleEntry](TypeMP4A, TypeULAW, TypeALAW, TypeOPUS, TypeENCA)
|
|
RegisterBox[*VisualSampleEntry](TypeAVC1, TypeHVC1, TypeHEV1, TypeENCV)
|
|
RegisterBox[*HintSampleEntry](TypeHINT)
|
|
RegisterBox[*DataBox](TypeAVCC, TypeHVCC)
|
|
// RegisterBox[*MetadataSampleEntry](TypeMETA)
|
|
}
|
|
|
|
//ffmpeg mov_write_wave_tag
|
|
// avio_wb32(pb, 12); /* size */
|
|
// ffio_wfourcc(pb, "frma");
|
|
// avio_wl32(pb, track->tag);
|
|
|
|
// avio_wb32(pb, 12); /* size */
|
|
// ffio_wfourcc(pb, "mp4a");
|
|
// avio_wb32(pb, 0);
|