mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-09-27 03:25:56 +08:00
136 lines
3.9 KiB
Go
136 lines
3.9 KiB
Go
package mp4
|
|
|
|
import (
|
|
"github.com/deepch/vdk/codec/h265parser"
|
|
"io"
|
|
"m7s.live/m7s/v5"
|
|
"m7s.live/m7s/v5/pkg/codec"
|
|
"m7s.live/m7s/v5/pkg/util"
|
|
"m7s.live/m7s/v5/plugin/mp4/pkg/box"
|
|
rtmp "m7s.live/m7s/v5/plugin/rtmp/pkg"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type MP4Puller struct {
|
|
*util.ScalableMemoryAllocator
|
|
*box.MovDemuxer
|
|
}
|
|
|
|
func NewMP4Puller() *MP4Puller {
|
|
return &MP4Puller{
|
|
ScalableMemoryAllocator: util.NewScalableMemoryAllocator(1 << 10),
|
|
}
|
|
}
|
|
|
|
func (puller *MP4Puller) Connect(p *m7s.Client) (err error) {
|
|
if strings.HasPrefix(p.RemoteURL, "http") {
|
|
var res *http.Response
|
|
client := http.DefaultClient
|
|
if proxyConf := p.Proxy; proxyConf != "" {
|
|
proxy, err := url.Parse(proxyConf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
transport := &http.Transport{Proxy: http.ProxyURL(proxy)}
|
|
client = &http.Client{Transport: transport}
|
|
}
|
|
if res, err = client.Get(p.RemoteURL); err == nil {
|
|
if res.StatusCode != http.StatusOK {
|
|
return io.EOF
|
|
}
|
|
p.Closer = res.Body
|
|
|
|
content, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
puller.MovDemuxer = box.CreateMp4Demuxer(strings.NewReader(string(content)))
|
|
}
|
|
} else {
|
|
var res *os.File
|
|
if res, err = os.Open(p.RemoteURL); err == nil {
|
|
p.Closer = res
|
|
}
|
|
puller.MovDemuxer = box.CreateMp4Demuxer(res)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (puller *MP4Puller) Pull(p *m7s.Puller) (err error) {
|
|
var tracks []box.TrackInfo
|
|
if tracks, err = puller.ReadHead(); err != nil {
|
|
return
|
|
}
|
|
for _, track := range tracks {
|
|
switch track.Cid {
|
|
case box.MP4_CODEC_H264:
|
|
var sequece rtmp.RTMPVideo
|
|
sequece.Append([]byte{0x17, 0x00, 0x00, 0x00, 0x00}, track.ExtraData)
|
|
p.WriteVideo(&sequece)
|
|
case box.MP4_CODEC_H265:
|
|
var sequece rtmp.RTMPVideo
|
|
sequece.Append([]byte{0b1001_0000 | rtmp.PacketTypeSequenceStart}, codec.FourCC_H265[:], track.ExtraData)
|
|
p.WriteVideo(&sequece)
|
|
case box.MP4_CODEC_AAC:
|
|
var sequence rtmp.RTMPAudio
|
|
sequence.Append([]byte{0xaf, 0x00}, track.ExtraData)
|
|
p.WriteAudio(&sequence)
|
|
}
|
|
}
|
|
for {
|
|
pkg, err := puller.ReadPacket(puller.ScalableMemoryAllocator)
|
|
if err != nil {
|
|
p.Error("Error reading MP4 packet", "err", err)
|
|
return err
|
|
}
|
|
switch track := tracks[pkg.TrackId-1]; track.Cid {
|
|
case box.MP4_CODEC_H264:
|
|
var videoFrame rtmp.RTMPVideo
|
|
videoFrame.SetAllocator(puller.ScalableMemoryAllocator)
|
|
videoFrame.CTS = uint32(pkg.Pts - pkg.Dts)
|
|
videoFrame.Timestamp = uint32(pkg.Dts)
|
|
keyFrame := codec.H264NALUType(pkg.Data[5]&0x1F) == codec.NALU_IDR_Picture
|
|
videoFrame.AppendOne([]byte{util.Conditoinal[byte](keyFrame, 0x17, 0x27), 0x01, byte(videoFrame.CTS >> 24), byte(videoFrame.CTS >> 8), byte(videoFrame.CTS)})
|
|
videoFrame.AddRecycleBytes(pkg.Data)
|
|
p.WriteVideo(&videoFrame)
|
|
case box.MP4_CODEC_H265:
|
|
var videoFrame rtmp.RTMPVideo
|
|
videoFrame.SetAllocator(puller.ScalableMemoryAllocator)
|
|
videoFrame.CTS = uint32(pkg.Pts - pkg.Dts)
|
|
videoFrame.Timestamp = uint32(pkg.Dts)
|
|
var head []byte
|
|
var b0 byte = 0b1010_0000
|
|
switch codec.ParseH265NALUType(pkg.Data[5]) {
|
|
case h265parser.NAL_UNIT_CODED_SLICE_BLA_W_LP,
|
|
h265parser.NAL_UNIT_CODED_SLICE_BLA_W_RADL,
|
|
h265parser.NAL_UNIT_CODED_SLICE_BLA_N_LP,
|
|
h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
|
|
h265parser.NAL_UNIT_CODED_SLICE_IDR_N_LP,
|
|
h265parser.NAL_UNIT_CODED_SLICE_CRA:
|
|
b0 = 0b1001_0000
|
|
}
|
|
if videoFrame.CTS == 0 {
|
|
head = videoFrame.NextN(5)
|
|
head[0] = b0 | rtmp.PacketTypeCodedFramesX
|
|
} else {
|
|
head = videoFrame.NextN(8)
|
|
head[0] = b0 | rtmp.PacketTypeCodedFrames
|
|
util.PutBE(head[5:8], videoFrame.CTS) // cts
|
|
}
|
|
copy(head[1:], codec.FourCC_H265[:])
|
|
videoFrame.AddRecycleBytes(pkg.Data)
|
|
p.WriteVideo(&videoFrame)
|
|
case box.MP4_CODEC_AAC:
|
|
var audioFrame rtmp.RTMPAudio
|
|
audioFrame.SetAllocator(puller.ScalableMemoryAllocator)
|
|
audioFrame.Timestamp = uint32(pkg.Dts)
|
|
audioFrame.AppendOne([]byte{0xaf, 0x01})
|
|
audioFrame.AddRecycleBytes(pkg.Data)
|
|
p.WriteAudio(&audioFrame)
|
|
}
|
|
}
|
|
}
|