uplink/app/media/muxer.go

130 lines
2.7 KiB
Go
Raw Normal View History

2018-04-10 11:48:51 +00:00
package media
import (
2018-04-10 14:02:14 +00:00
"errors"
2018-04-10 11:48:51 +00:00
"io"
"reflect"
"github.com/3d0c/gmf"
)
type Muxer struct {
writers []io.WriteCloser
err chan error
}
func Mux(muxer string, readers ...io.ReadCloser) (retval io.Reader) {
retval, w := io.Pipe()
type Instance struct {
Ctx *gmf.FmtCtx
AvioCtx *gmf.AVIOContext
}
go func() {
var err error
defer func() {
if err != nil {
w.CloseWithError(err)
} else {
w.CloseWithError(io.EOF)
}
}()
output := Instance{}
output.Ctx, err = gmf.NewOutputCtxWithFormatName("test.dat", muxer)
if err != nil {
return
}
defer output.Ctx.CloseOutputAndRelease()
if output.AvioCtx, err = gmf.NewAVIOContext(output.Ctx, &gmf.AVIOHandlers{
WritePacket: func(p []byte) {
2018-04-10 14:02:04 +00:00
w.Write(p)
2018-04-10 11:48:51 +00:00
},
}); err != nil {
return
}
defer output.AvioCtx.Release()
output.Ctx.SetPb(output.AvioCtx)
2018-04-10 14:03:39 +00:00
output.Ctx.SetFlag(0x0080) // AVFMT_FLAG_CUSTOM_IO
2018-04-10 11:48:51 +00:00
inputs := make([]Instance, len(readers))
for i, r := range readers {
input := Instance{Ctx: gmf.NewCtx()}
defer input.Ctx.CloseInputAndRelease()
buffer := make([]byte, 8*1024)
if input.AvioCtx, err = gmf.NewAVIOContext(input.Ctx, &gmf.AVIOHandlers{
ReadPacket: func(r io.Reader) func() ([]byte, int) {
return func() ([]byte, int) {
n, err := r.Read(buffer)
if err != nil {
n = -1
}
return buffer, n
}
}(r),
}); err != nil {
return
}
defer input.AvioCtx.Release()
input.Ctx.SetPb(input.AvioCtx)
if err = input.Ctx.OpenInput(""); err != nil {
return
}
inputs[i] = input
2018-04-10 14:00:42 +00:00
if input.Ctx.StreamsCnt() > 1 {
err = errors.New("Too many streams found in input")
return
}
2018-04-10 11:48:51 +00:00
var stream *gmf.Stream
if stream, err = input.Ctx.GetStream(0); err != nil {
return
}
stream, err = output.Ctx.AddStreamWithCodeCtx(stream.CodecCtx())
if err != nil {
return
}
}
if err = output.Ctx.WriteHeader(); err != nil {
return
}
//first := true
cases := make([]reflect.SelectCase, len(readers))
for i, c := range inputs {
cases[i] = reflect.SelectCase{
Dir: reflect.SelectRecv,
Chan: reflect.ValueOf(c.Ctx.GetNewPackets()),
}
}
var closedStreamIndex = 0
defer func() {
for i, r := range readers {
if i == closedStreamIndex {
continue
}
r.Close()
}
}()
2018-04-10 11:48:51 +00:00
for err == nil {
streamIndex, packetVal, ok := reflect.Select(cases)
if !ok {
closedStreamIndex = streamIndex
2018-04-10 11:48:51 +00:00
break // some stream has been closed, just close them all
}
packet := packetVal.Interface().(*gmf.Packet)
packet.SetStreamIndex(streamIndex)
err = output.Ctx.WritePacket(packet)
packet.Release()
}
output.Ctx.WriteTrailer()
}()
return
}