mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-12-24 13:48:04 +08:00
fix: demuxer mp4
This commit is contained in:
BIN
architecture.jpg
BIN
architecture.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 625 KiB After Width: | Height: | Size: 659 KiB |
@@ -25,12 +25,12 @@ mp4:
|
||||
# enable: false
|
||||
publish:
|
||||
delayclosetimeout: 3s
|
||||
# onpub:
|
||||
# record:
|
||||
# ^live/.+:
|
||||
# fragment: 10s
|
||||
# filepath: record/$0
|
||||
# type: mp4
|
||||
onpub:
|
||||
record:
|
||||
^live/.+:
|
||||
fragment: 10s
|
||||
filepath: record/$0
|
||||
# type: fmp4
|
||||
onsub:
|
||||
pull:
|
||||
^vod_mp4_\d+/(.+)$: $1
|
||||
|
||||
@@ -142,9 +142,9 @@
|
||||
try {
|
||||
// Try different codec combinations
|
||||
const codecConfigs = [
|
||||
'video/mp4; codecs="avc1.64001f"', // Video only
|
||||
//'video/mp4; codecs="avc1.64001f"', // Video only
|
||||
'video/mp4; codecs="avc1.64001f,mp4a.40.2"', // Video + AAC
|
||||
'video/mp4' // Let the browser figure it out
|
||||
// 'video/mp4' // Let the browser figure it out
|
||||
];
|
||||
|
||||
let sourceBufferCreated = false;
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package bits
|
||||
|
||||
// Reader is a bit stream reader
|
||||
type Reader struct {
|
||||
Data []byte
|
||||
Offset int
|
||||
}
|
||||
|
||||
// Skip skips n bits
|
||||
func (r *Reader) Skip(n int) {
|
||||
r.Offset += n
|
||||
}
|
||||
|
||||
// ReadBit reads a single bit
|
||||
func (r *Reader) ReadBit() (uint, error) {
|
||||
if r.Offset/8 >= len(r.Data) {
|
||||
return 0, nil
|
||||
}
|
||||
b := r.Data[r.Offset/8]
|
||||
v := (b >> (7 - (r.Offset % 8))) & 0x01
|
||||
r.Offset++
|
||||
return uint(v), nil
|
||||
}
|
||||
|
||||
// ReadExpGolomb reads an Exp-Golomb code
|
||||
func (r *Reader) ReadExpGolomb() (uint, error) {
|
||||
leadingZeroBits := 0
|
||||
for {
|
||||
b, err := r.ReadBit()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if b == 1 {
|
||||
break
|
||||
}
|
||||
leadingZeroBits++
|
||||
}
|
||||
|
||||
result := uint(1<<leadingZeroBits) - 1
|
||||
for i := 0; i < leadingZeroBits; i++ {
|
||||
b, err := r.ReadBit()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
result = (result << 1) | uint(b)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ReadSE reads a signed Exp-Golomb code
|
||||
func (r *Reader) ReadSE() (int, error) {
|
||||
val, err := r.ReadExpGolomb()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sign := ((val & 0x01) << 1) - 1
|
||||
val = ((val >> 1) + (val & 0x01)) * uint(sign)
|
||||
return int(val), nil
|
||||
}
|
||||
@@ -172,8 +172,8 @@ func WriteTo(w io.Writer, box ...IBox) (n int64, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if n1 + n2 != int64(b.Size()) {
|
||||
panic(fmt.Sprintf("write to %s size error, %d != %d", b.Type(), n1 + n2, b.Size()))
|
||||
if n1+n2 != int64(b.Size()) {
|
||||
panic(fmt.Sprintf("write to %s size error, %d != %d", b.Type(), n1+n2, b.Size()))
|
||||
}
|
||||
n += n1 + n2
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("unknown box type: %s", baseBox.typ)
|
||||
}
|
||||
b := reflect.New(t).Interface().(IBox)
|
||||
b := reflect.New(t.Elem()).Interface().(IBox)
|
||||
var payload []byte
|
||||
if baseBox.size == 1 {
|
||||
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
||||
@@ -203,8 +203,10 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
||||
} else {
|
||||
payload = make([]byte, baseBox.size-BasicBoxLen)
|
||||
}
|
||||
|
||||
_, err = io.ReadFull(r, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
boxHeader := b.Header()
|
||||
switch header := boxHeader.(type) {
|
||||
case *BaseBox:
|
||||
@@ -216,6 +218,9 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
||||
header.Flags = [3]byte(payload[1:4])
|
||||
box, err = b.Unmarshal(payload[4:])
|
||||
}
|
||||
if err == io.EOF {
|
||||
return box, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -56,5 +56,5 @@ func (box *CTTSBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[CTTSBox](TypeCTTS)
|
||||
RegisterBox[*CTTSBox](TypeCTTS)
|
||||
}
|
||||
|
||||
@@ -105,9 +105,8 @@ func (box *DataInformationBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, box.Dref)
|
||||
}
|
||||
|
||||
func (box *DataInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
b, err := ReadFrom(r)
|
||||
func (box *DataInformationBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||
b, err = ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -91,3 +91,7 @@ func MakeHdlrBox(hdt HandlerType) *HandlerBox {
|
||||
}
|
||||
return hdlr
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*HandlerBox](TypeHDLR)
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@ func (m *MdiaBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, m.MDHD, m.MINF, m.HDLR)
|
||||
}
|
||||
|
||||
func (m *MdiaBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
func (m *MdiaBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
b, err = ReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return m, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *MediaHeaderBox:
|
||||
@@ -46,12 +47,10 @@ func (m *MediaInformationBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, m.VMHD, m.SMHD, m.HMHD, m.STBL, m.DINF)
|
||||
}
|
||||
|
||||
func (m *MediaInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (m *MediaInformationBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for err == nil {
|
||||
b, err = ReadFrom(r)
|
||||
switch box := b.(type) {
|
||||
case *VideoMediaHeaderBox:
|
||||
m.VMHD = box
|
||||
@@ -65,8 +64,10 @@ func (m *MediaInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
m.DINF = box
|
||||
}
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MdiaBox](TypeMDIA)
|
||||
RegisterBox[*MediaInformationBox](TypeMINF)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ func (m *MoovBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
}
|
||||
|
||||
func (m *MoovBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
|
||||
b, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return m, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *TrakBox:
|
||||
@@ -51,17 +51,19 @@ func (e *EdtsBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, e.Elst)
|
||||
}
|
||||
|
||||
func (e *EdtsBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
func (e *EdtsBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for err == nil {
|
||||
b, err = ReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return e, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *EditListBox:
|
||||
e.Elst = box
|
||||
}
|
||||
}
|
||||
return e, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -210,9 +210,9 @@ func (visual *VisualSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
||||
visual.FrameCount = binary.BigEndian.Uint16(buf[16:])
|
||||
copy(visual.Compressorname[:], buf[18:50])
|
||||
visual.Depth = binary.BigEndian.Uint16(buf[50:])
|
||||
|
||||
if len(buf) > 52 {
|
||||
box, err := ReadFrom(bytes.NewReader(buf[52:]))
|
||||
// 52 pre-defined
|
||||
if len(buf) > 54 {
|
||||
box, err := ReadFrom(bytes.NewReader(buf[54:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -38,12 +38,10 @@ func (t *TrakBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, t.MDIA, t.EDTS, t.TKHD)
|
||||
}
|
||||
|
||||
func (t *TrakBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
func (t *TrakBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for err == nil {
|
||||
b, err = ReadFrom(r)
|
||||
switch box := b.(type) {
|
||||
case *MdiaBox:
|
||||
t.MDIA = box
|
||||
@@ -53,6 +51,7 @@ func (t *TrakBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
t.TKHD = box
|
||||
}
|
||||
}
|
||||
return t, err
|
||||
}
|
||||
|
||||
// ParseSamples parses the sample table and builds the sample list
|
||||
|
||||
@@ -50,6 +50,7 @@ type (
|
||||
IsFragment bool
|
||||
// pssh []*PsshBox
|
||||
moov *MoovBox
|
||||
mdat *MediaDataBox
|
||||
QuicTime bool
|
||||
}
|
||||
)
|
||||
@@ -94,11 +95,14 @@ func (d *Demuxer) Demux() (err error) {
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
var b IBox
|
||||
for {
|
||||
b, err := box.ReadFrom(d.reader)
|
||||
b, err = box.ReadFrom(d.reader)
|
||||
if err != nil {
|
||||
break
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *FileTypeBox:
|
||||
@@ -107,6 +111,7 @@ func (d *Demuxer) Demux() (err error) {
|
||||
}
|
||||
case *FreeBox:
|
||||
case *MediaDataBox:
|
||||
d.mdat = box
|
||||
case *MoovBox:
|
||||
if box.MVEX != nil {
|
||||
d.IsFragment = true
|
||||
|
||||
76
plugin/mp4/pkg/demuxer_test.go
Normal file
76
plugin/mp4/pkg/demuxer_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package mp4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"m7s.live/v5/plugin/mp4/pkg/box"
|
||||
)
|
||||
|
||||
// TestDemuxerBoxTree tests the Demuxer by reading a test MP4 file and printing the box tree structure.
|
||||
func TestDemuxerBoxTree(t *testing.T) {
|
||||
// Open the test mp4 file. It is assumed to be located in 'testdata/test_regular.mp4'.
|
||||
f, err := os.Open("/Users/dexter/project/v5/monibuca/example/default/dump/test_regular.mp4")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open test mp4 file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Create a new Demuxer with the file reader
|
||||
d := NewDemuxer(f)
|
||||
|
||||
// Call Demux to process the file; we don't use the result directly here.
|
||||
err = d.Demux()
|
||||
if err != nil {
|
||||
t.Fatalf("demuxing failed: %v", err)
|
||||
}
|
||||
|
||||
// Reset the file pointer to the beginning to re-read boxes for tree display
|
||||
if _, err := f.Seek(0, 0); err != nil {
|
||||
t.Fatalf("failed to seek to beginning: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("MP4 Box Tree:")
|
||||
// Read and print each top-level box
|
||||
for {
|
||||
b, err := box.ReadFrom(f)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
t.Fatalf("failed to read box: %v", err)
|
||||
}
|
||||
printBox(b, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// printBox prints a box's type and size, and recursively prints its children if available.
|
||||
func printBox(b interface{}, indent int) {
|
||||
ind := strings.Repeat(" ", indent)
|
||||
// Determine the box type name using reflection
|
||||
typeOfBox := reflect.TypeOf(b)
|
||||
if typeOfBox.Kind() == reflect.Ptr {
|
||||
typeOfBox = typeOfBox.Elem()
|
||||
}
|
||||
// Try to get the size from a method Size() int
|
||||
var size int
|
||||
if s, ok := b.(interface{ Size() int }); ok {
|
||||
size = s.Size()
|
||||
}
|
||||
fmt.Printf("%s%s (size: %d)\n", ind, typeOfBox.Name(), size)
|
||||
|
||||
// If the box is a container and has child boxes, print them recursively.
|
||||
if container, ok := b.(interface{ ChildrenBoxes() []interface{} }); ok {
|
||||
for _, child := range container.ChildrenBoxes() {
|
||||
printBox(child, indent+1)
|
||||
}
|
||||
} else if container, ok := b.(interface{ ChildrenBoxes() []any }); ok {
|
||||
for _, child := range container.ChildrenBoxes() {
|
||||
printBox(child, indent+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user