2b7682cdb3
- replace traditional for loop with range-based for loop for clarity
refactor(ffmpeg): simplify cut function loop
- utilize range-based for loop instead of traditional for loop
refactor(ring): update API response mapping type
- change map type from `interface{}` to `any` for better type safety
refactor(stream): handle nil source in NewStream
- add nil check for source elements before processing
refactor(webrtc): unify payload handling in GetToken
- change map type from `interface{}` to `any` for consistency
refactor(ascii): optimize nested loops in Write function
- replace traditional for loops with range-based for loops for readability
refactor(bits): enhance readability in Reader methods
- replace traditional for loops with range-based for loops in Read functions
refactor(h264): modernize loop structures in DecodeConfig
- switch to range-based for loops for cleaner code
refactor(h265): streamline profile_tier_level loops
- utilize range-based for loops instead of traditional for loops
chore(core): remove commented-out test function for clarity
refactor(core): simplify RandString function loop
- change traditional for loop to range-based for loop
refactor(flvt): optimize timestamp handling in TestTimeToRTP
- switch to range-based for loop for iterating frames
refactor(nest): improve error handling in ExchangeSDP
- format error message with printf-style formatting for clarity
refactor(tapo): enhance securityEncode function
- change traditional for loop to range-based for loop for readability
fix(tcp): correct masking in websocket Write method
- replace traditional for loop with range-based for loop
refactor(tutk): modernize encoding loops in crypto functions
- utilize range-based for loops for better readability
refactor(tuya): unify data types in MQTT message struct
- change map type from `interface{}` to `any` for consistency
refactor(webrtc): standardize codec registration
- change map type from `interface{}` to `any` for type safety
refactor(yaml): simplify Unmarshal function signature
- update parameter type from `interface{}` to `any` for better clarity
117 lines
2.3 KiB
Go
117 lines
2.3 KiB
Go
package mp4
|
|
|
|
import (
|
|
"github.com/AlexxIT/go2rtc/pkg/aac"
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
"github.com/AlexxIT/go2rtc/pkg/h264"
|
|
"github.com/AlexxIT/go2rtc/pkg/iso"
|
|
"github.com/pion/rtp"
|
|
)
|
|
|
|
type Demuxer struct {
|
|
codecs map[uint32]*core.Codec
|
|
timeScales map[uint32]float32
|
|
}
|
|
|
|
func (d *Demuxer) Probe(init []byte) (medias []*core.Media) {
|
|
var trackID, timeScale uint32
|
|
|
|
if d.codecs == nil {
|
|
d.codecs = make(map[uint32]*core.Codec)
|
|
d.timeScales = make(map[uint32]float32)
|
|
}
|
|
|
|
atoms, _ := iso.DecodeAtoms(init)
|
|
for _, atom := range atoms {
|
|
var codec *core.Codec
|
|
|
|
switch atom := atom.(type) {
|
|
case *iso.AtomTkhd:
|
|
trackID = atom.TrackID
|
|
case *iso.AtomMdhd:
|
|
timeScale = atom.TimeScale
|
|
case *iso.AtomVideo:
|
|
switch atom.Name {
|
|
case "avc1":
|
|
codec = h264.ConfigToCodec(atom.Config)
|
|
}
|
|
case *iso.AtomAudio:
|
|
switch atom.Name {
|
|
case "mp4a":
|
|
codec = aac.ConfigToCodec(atom.Config)
|
|
}
|
|
}
|
|
|
|
if codec != nil {
|
|
d.codecs[trackID] = codec
|
|
d.timeScales[trackID] = float32(codec.ClockRate) / float32(timeScale)
|
|
|
|
medias = append(medias, &core.Media{
|
|
Kind: codec.Kind(),
|
|
Direction: core.DirectionRecvonly,
|
|
Codecs: []*core.Codec{codec},
|
|
})
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (d *Demuxer) GetTrackID(codec *core.Codec) uint32 {
|
|
for trackID, c := range d.codecs {
|
|
if c == codec {
|
|
return trackID
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (d *Demuxer) Demux(data2 []byte) (trackID uint32, packets []*core.Packet) {
|
|
atoms, err := iso.DecodeAtoms(data2)
|
|
if err != nil {
|
|
return 0, nil
|
|
}
|
|
|
|
var ts uint32
|
|
var trun *iso.AtomTrun
|
|
var data []byte
|
|
|
|
for _, atom := range atoms {
|
|
switch atom := atom.(type) {
|
|
case *iso.AtomTfhd:
|
|
trackID = atom.TrackID
|
|
case *iso.AtomTfdt:
|
|
ts = uint32(atom.DecodeTime)
|
|
case *iso.AtomTrun:
|
|
trun = atom
|
|
case *iso.AtomMdat:
|
|
data = atom.Data
|
|
}
|
|
}
|
|
|
|
timeScale := d.timeScales[trackID]
|
|
if timeScale == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
n := len(trun.SamplesDuration)
|
|
packets = make([]*core.Packet, n)
|
|
|
|
for i := range n {
|
|
duration := trun.SamplesDuration[i]
|
|
size := trun.SamplesSize[i]
|
|
|
|
// can be SPS, PPS and IFrame in one packet
|
|
timestamp := uint32(float32(ts) * timeScale)
|
|
packets[i] = &rtp.Packet{
|
|
Header: rtp.Header{Timestamp: timestamp},
|
|
Payload: data[:size],
|
|
}
|
|
|
|
data = data[size:]
|
|
ts += duration
|
|
}
|
|
|
|
return
|
|
}
|