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
|
# enable: false
|
||||||
publish:
|
publish:
|
||||||
delayclosetimeout: 3s
|
delayclosetimeout: 3s
|
||||||
# onpub:
|
onpub:
|
||||||
# record:
|
record:
|
||||||
# ^live/.+:
|
^live/.+:
|
||||||
# fragment: 10s
|
fragment: 10s
|
||||||
# filepath: record/$0
|
filepath: record/$0
|
||||||
# type: mp4
|
# type: fmp4
|
||||||
onsub:
|
onsub:
|
||||||
pull:
|
pull:
|
||||||
^vod_mp4_\d+/(.+)$: $1
|
^vod_mp4_\d+/(.+)$: $1
|
||||||
|
|||||||
@@ -142,9 +142,9 @@
|
|||||||
try {
|
try {
|
||||||
// Try different codec combinations
|
// Try different codec combinations
|
||||||
const codecConfigs = [
|
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; 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;
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n1 + n2 != int64(b.Size()) {
|
if n1+n2 != int64(b.Size()) {
|
||||||
panic(fmt.Sprintf("write to %s size error, %d != %d", b.Type(), n1 + n2, b.Size()))
|
panic(fmt.Sprintf("write to %s size error, %d != %d", b.Type(), n1+n2, b.Size()))
|
||||||
}
|
}
|
||||||
n += n1 + n2
|
n += n1 + n2
|
||||||
}
|
}
|
||||||
@@ -193,7 +193,7 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
|||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("unknown box type: %s", baseBox.typ)
|
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
|
var payload []byte
|
||||||
if baseBox.size == 1 {
|
if baseBox.size == 1 {
|
||||||
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
||||||
@@ -203,8 +203,10 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
|||||||
} else {
|
} else {
|
||||||
payload = make([]byte, baseBox.size-BasicBoxLen)
|
payload = make([]byte, baseBox.size-BasicBoxLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = io.ReadFull(r, payload)
|
_, err = io.ReadFull(r, payload)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
boxHeader := b.Header()
|
boxHeader := b.Header()
|
||||||
switch header := boxHeader.(type) {
|
switch header := boxHeader.(type) {
|
||||||
case *BaseBox:
|
case *BaseBox:
|
||||||
@@ -216,6 +218,9 @@ func ReadFrom(r io.Reader) (box IBox, err error) {
|
|||||||
header.Flags = [3]byte(payload[1:4])
|
header.Flags = [3]byte(payload[1:4])
|
||||||
box, err = b.Unmarshal(payload[4:])
|
box, err = b.Unmarshal(payload[4:])
|
||||||
}
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
return box, nil
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,5 +56,5 @@ func (box *CTTSBox) Unmarshal(buf []byte) (IBox, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
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)
|
return WriteTo(w, box.Dref)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (box *DataInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
func (box *DataInformationBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||||
r := bytes.NewReader(buf)
|
b, err = ReadFrom(bytes.NewReader(buf))
|
||||||
b, err := ReadFrom(r)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,3 +91,7 @@ func MakeHdlrBox(hdt HandlerType) *HandlerBox {
|
|||||||
}
|
}
|
||||||
return hdlr
|
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)
|
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 {
|
for {
|
||||||
b, err := ReadFrom(bytes.NewReader(buf))
|
b, err = ReadFrom(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return m, err
|
||||||
}
|
}
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *MediaHeaderBox:
|
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)
|
return WriteTo(w, m.VMHD, m.SMHD, m.HMHD, m.STBL, m.DINF)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MediaInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
func (m *MediaInformationBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||||
for {
|
r := bytes.NewReader(buf)
|
||||||
b, err := ReadFrom(bytes.NewReader(buf))
|
for err == nil {
|
||||||
if err != nil {
|
b, err = ReadFrom(r)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *VideoMediaHeaderBox:
|
case *VideoMediaHeaderBox:
|
||||||
m.VMHD = box
|
m.VMHD = box
|
||||||
@@ -65,8 +64,10 @@ func (m *MediaInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
|||||||
m.DINF = box
|
m.DINF = box
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return m, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterBox[*MdiaBox](TypeMDIA)
|
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) {
|
func (m *MoovBox) Unmarshal(buf []byte) (IBox, error) {
|
||||||
|
r := bytes.NewReader(buf)
|
||||||
for {
|
for {
|
||||||
b, err := ReadFrom(bytes.NewReader(buf))
|
b, err := ReadFrom(r)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return m, err
|
||||||
}
|
}
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *TrakBox:
|
case *TrakBox:
|
||||||
@@ -51,17 +51,19 @@ func (e *EdtsBox) WriteTo(w io.Writer) (n int64, err error) {
|
|||||||
return WriteTo(w, e.Elst)
|
return WriteTo(w, e.Elst)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *EdtsBox) Unmarshal(buf []byte) (IBox, error) {
|
func (e *EdtsBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||||
for {
|
r := bytes.NewReader(buf)
|
||||||
b, err := ReadFrom(bytes.NewReader(buf))
|
for err == nil {
|
||||||
|
b, err = ReadFrom(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return e, err
|
||||||
}
|
}
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *EditListBox:
|
case *EditListBox:
|
||||||
e.Elst = box
|
e.Elst = box
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return e, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -210,9 +210,9 @@ func (visual *VisualSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
|||||||
visual.FrameCount = binary.BigEndian.Uint16(buf[16:])
|
visual.FrameCount = binary.BigEndian.Uint16(buf[16:])
|
||||||
copy(visual.Compressorname[:], buf[18:50])
|
copy(visual.Compressorname[:], buf[18:50])
|
||||||
visual.Depth = binary.BigEndian.Uint16(buf[50:])
|
visual.Depth = binary.BigEndian.Uint16(buf[50:])
|
||||||
|
// 52 pre-defined
|
||||||
if len(buf) > 52 {
|
if len(buf) > 54 {
|
||||||
box, err := ReadFrom(bytes.NewReader(buf[52:]))
|
box, err := ReadFrom(bytes.NewReader(buf[54:]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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)
|
return WriteTo(w, t.MDIA, t.EDTS, t.TKHD)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrakBox) Unmarshal(buf []byte) (IBox, error) {
|
func (t *TrakBox) Unmarshal(buf []byte) (b IBox, err error) {
|
||||||
for {
|
r := bytes.NewReader(buf)
|
||||||
b, err := ReadFrom(bytes.NewReader(buf))
|
for err == nil {
|
||||||
if err != nil {
|
b, err = ReadFrom(r)
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *MdiaBox:
|
case *MdiaBox:
|
||||||
t.MDIA = box
|
t.MDIA = box
|
||||||
@@ -53,6 +51,7 @@ func (t *TrakBox) Unmarshal(buf []byte) (IBox, error) {
|
|||||||
t.TKHD = box
|
t.TKHD = box
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSamples parses the sample table and builds the sample list
|
// ParseSamples parses the sample table and builds the sample list
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ type (
|
|||||||
IsFragment bool
|
IsFragment bool
|
||||||
// pssh []*PsshBox
|
// pssh []*PsshBox
|
||||||
moov *MoovBox
|
moov *MoovBox
|
||||||
|
mdat *MediaDataBox
|
||||||
QuicTime bool
|
QuicTime bool
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -94,11 +95,14 @@ func (d *Demuxer) Demux() (err error) {
|
|||||||
// }
|
// }
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
var b IBox
|
||||||
for {
|
for {
|
||||||
b, err := box.ReadFrom(d.reader)
|
b, err = box.ReadFrom(d.reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
switch box := b.(type) {
|
switch box := b.(type) {
|
||||||
case *FileTypeBox:
|
case *FileTypeBox:
|
||||||
@@ -107,6 +111,7 @@ func (d *Demuxer) Demux() (err error) {
|
|||||||
}
|
}
|
||||||
case *FreeBox:
|
case *FreeBox:
|
||||||
case *MediaDataBox:
|
case *MediaDataBox:
|
||||||
|
d.mdat = box
|
||||||
case *MoovBox:
|
case *MoovBox:
|
||||||
if box.MVEX != nil {
|
if box.MVEX != nil {
|
||||||
d.IsFragment = true
|
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