mirror of
https://github.com/gwuhaolin/livego.git
synced 2021-06-01 09:10:22 +03:00
174 lines
3.4 KiB
Go
Executable File
174 lines
3.4 KiB
Go
Executable File
package flv
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gwuhaolin/livego/av"
|
|
"github.com/gwuhaolin/livego/configure"
|
|
"github.com/gwuhaolin/livego/protocol/amf"
|
|
"github.com/gwuhaolin/livego/utils/pio"
|
|
"github.com/gwuhaolin/livego/utils/uid"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
flvHeader = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0x00, 0x00, 0x00, 0x09}
|
|
)
|
|
|
|
/*
|
|
func NewFlv(handler av.Handler, info av.Info) {
|
|
patths := strings.SplitN(info.Key, "/", 2)
|
|
|
|
if len(patths) != 2 {
|
|
log.Warning("invalid info")
|
|
return
|
|
}
|
|
|
|
w, err := os.OpenFile(*flvFile, os.O_CREATE|os.O_RDWR, 0755)
|
|
if err != nil {
|
|
log.Error("open file error: ", err)
|
|
}
|
|
|
|
writer := NewFLVWriter(patths[0], patths[1], info.URL, w)
|
|
|
|
handler.HandleWriter(writer)
|
|
|
|
writer.Wait()
|
|
// close flv file
|
|
log.Debug("close flv file")
|
|
writer.ctx.Close()
|
|
}
|
|
*/
|
|
|
|
const (
|
|
headerLen = 11
|
|
)
|
|
|
|
type FLVWriter struct {
|
|
Uid string
|
|
av.RWBaser
|
|
app, title, url string
|
|
buf []byte
|
|
closed chan struct{}
|
|
ctx *os.File
|
|
}
|
|
|
|
func NewFLVWriter(app, title, url string, ctx *os.File) *FLVWriter {
|
|
ret := &FLVWriter{
|
|
Uid: uid.NewId(),
|
|
app: app,
|
|
title: title,
|
|
url: url,
|
|
ctx: ctx,
|
|
RWBaser: av.NewRWBaser(time.Second * 10),
|
|
closed: make(chan struct{}),
|
|
buf: make([]byte, headerLen),
|
|
}
|
|
|
|
ret.ctx.Write(flvHeader)
|
|
pio.PutI32BE(ret.buf[:4], 0)
|
|
ret.ctx.Write(ret.buf[:4])
|
|
|
|
return ret
|
|
}
|
|
|
|
func (writer *FLVWriter) Write(p *av.Packet) error {
|
|
writer.RWBaser.SetPreTime()
|
|
h := writer.buf[:headerLen]
|
|
typeID := av.TAG_VIDEO
|
|
if !p.IsVideo {
|
|
if p.IsMetadata {
|
|
var err error
|
|
typeID = av.TAG_SCRIPTDATAAMF0
|
|
p.Data, err = amf.MetaDataReform(p.Data, amf.DEL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
typeID = av.TAG_AUDIO
|
|
}
|
|
}
|
|
dataLen := len(p.Data)
|
|
timestamp := p.TimeStamp
|
|
timestamp += writer.BaseTimeStamp()
|
|
writer.RWBaser.RecTimeStamp(timestamp, uint32(typeID))
|
|
|
|
preDataLen := dataLen + headerLen
|
|
timestampbase := timestamp & 0xffffff
|
|
timestampExt := timestamp >> 24 & 0xff
|
|
|
|
pio.PutU8(h[0:1], uint8(typeID))
|
|
pio.PutI24BE(h[1:4], int32(dataLen))
|
|
pio.PutI24BE(h[4:7], int32(timestampbase))
|
|
pio.PutU8(h[7:8], uint8(timestampExt))
|
|
|
|
if _, err := writer.ctx.Write(h); err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := writer.ctx.Write(p.Data); err != nil {
|
|
return err
|
|
}
|
|
|
|
pio.PutI32BE(h[:4], int32(preDataLen))
|
|
if _, err := writer.ctx.Write(h[:4]); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (writer *FLVWriter) Wait() {
|
|
select {
|
|
case <-writer.closed:
|
|
return
|
|
}
|
|
}
|
|
|
|
func (writer *FLVWriter) Close(error) {
|
|
writer.ctx.Close()
|
|
close(writer.closed)
|
|
}
|
|
|
|
func (writer *FLVWriter) Info() (ret av.Info) {
|
|
ret.UID = writer.Uid
|
|
ret.URL = writer.url
|
|
ret.Key = writer.app + "/" + writer.title
|
|
return
|
|
}
|
|
|
|
type FlvDvr struct{}
|
|
|
|
func (f *FlvDvr) GetWriter(info av.Info) av.WriteCloser {
|
|
paths := strings.SplitN(info.Key, "/", 2)
|
|
if len(paths) != 2 {
|
|
log.Warning("invalid info")
|
|
return nil
|
|
}
|
|
|
|
flvDir := configure.Config.GetString("flv_dir")
|
|
|
|
err := os.MkdirAll(path.Join(flvDir, paths[0]), 0755)
|
|
if err != nil {
|
|
log.Error("mkdir error: ", err)
|
|
return nil
|
|
}
|
|
|
|
fileName := fmt.Sprintf("%s_%d.%s", path.Join(flvDir, info.Key), time.Now().Unix(), "flv")
|
|
log.Debug("flv dvr save stream to: ", fileName)
|
|
w, err := os.OpenFile(fileName, os.O_CREATE|os.O_RDWR, 0755)
|
|
if err != nil {
|
|
log.Error("open file error: ", err)
|
|
return nil
|
|
}
|
|
|
|
writer := NewFLVWriter(paths[0], paths[1], info.URL, w)
|
|
log.Debug("new flv dvr: ", writer.Info())
|
|
return writer
|
|
}
|