first commit

This commit is contained in:
Andrey Semochkin
2019-11-30 21:53:21 +01:00
commit 087a2b4c2d
60 changed files with 19091 additions and 0 deletions

316
av/av.go Normal file
View File

@@ -0,0 +1,316 @@
// Package av defines basic interfaces and data structures of container demux/mux and audio encode/decode.
package av
import (
"fmt"
"time"
)
// Audio sample format.
type SampleFormat uint8
const (
U8 = SampleFormat(iota + 1) // 8-bit unsigned integer
S16 // signed 16-bit integer
S32 // signed 32-bit integer
FLT // 32-bit float
DBL // 64-bit float
U8P // 8-bit unsigned integer in planar
S16P // signed 16-bit integer in planar
S32P // signed 32-bit integer in planar
FLTP // 32-bit float in planar
DBLP // 64-bit float in planar
U32 // unsigned 32-bit integer
)
func (self SampleFormat) BytesPerSample() int {
switch self {
case U8, U8P:
return 1
case S16, S16P:
return 2
case FLT, FLTP, S32, S32P, U32:
return 4
case DBL, DBLP:
return 8
default:
return 0
}
}
func (self SampleFormat) String() string {
switch self {
case U8:
return "U8"
case S16:
return "S16"
case S32:
return "S32"
case FLT:
return "FLT"
case DBL:
return "DBL"
case U8P:
return "U8P"
case S16P:
return "S16P"
case FLTP:
return "FLTP"
case DBLP:
return "DBLP"
case U32:
return "U32"
default:
return "?"
}
}
// Check if this sample format is in planar.
func (self SampleFormat) IsPlanar() bool {
switch self {
case S16P, S32P, FLTP, DBLP:
return true
default:
return false
}
}
// Audio channel layout.
type ChannelLayout uint16
func (self ChannelLayout) String() string {
return fmt.Sprintf("%dch", self.Count())
}
const (
CH_FRONT_CENTER = ChannelLayout(1 << iota)
CH_FRONT_LEFT
CH_FRONT_RIGHT
CH_BACK_CENTER
CH_BACK_LEFT
CH_BACK_RIGHT
CH_SIDE_LEFT
CH_SIDE_RIGHT
CH_LOW_FREQ
CH_NR
CH_MONO = ChannelLayout(CH_FRONT_CENTER)
CH_STEREO = ChannelLayout(CH_FRONT_LEFT | CH_FRONT_RIGHT)
CH_2_1 = ChannelLayout(CH_STEREO | CH_BACK_CENTER)
CH_2POINT1 = ChannelLayout(CH_STEREO | CH_LOW_FREQ)
CH_SURROUND = ChannelLayout(CH_STEREO | CH_FRONT_CENTER)
CH_3POINT1 = ChannelLayout(CH_SURROUND | CH_LOW_FREQ)
// TODO: add all channel_layout in ffmpeg
)
func (self ChannelLayout) Count() (n int) {
for self != 0 {
n++
self = (self - 1) & self
}
return
}
// Video/Audio codec type. can be H264/AAC/SPEEX/...
type CodecType uint32
var (
H264 = MakeVideoCodecType(avCodecTypeMagic + 1)
AAC = MakeAudioCodecType(avCodecTypeMagic + 1)
PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2)
PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3)
SPEEX = MakeAudioCodecType(avCodecTypeMagic + 4)
NELLYMOSER = MakeAudioCodecType(avCodecTypeMagic + 5)
)
const codecTypeAudioBit = 0x1
const codecTypeOtherBits = 1
func (self CodecType) String() string {
switch self {
case H264:
return "H264"
case AAC:
return "AAC"
case PCM_MULAW:
return "PCM_MULAW"
case PCM_ALAW:
return "PCM_ALAW"
case SPEEX:
return "SPEEX"
case NELLYMOSER:
return "NELLYMOSER"
}
return ""
}
func (self CodecType) IsAudio() bool {
return self&codecTypeAudioBit != 0
}
func (self CodecType) IsVideo() bool {
return self&codecTypeAudioBit == 0
}
// Make a new audio codec type.
func MakeAudioCodecType(base uint32) (c CodecType) {
c = CodecType(base)<<codecTypeOtherBits | CodecType(codecTypeAudioBit)
return
}
// Make a new video codec type.
func MakeVideoCodecType(base uint32) (c CodecType) {
c = CodecType(base) << codecTypeOtherBits
return
}
const avCodecTypeMagic = 233333
// CodecData is some important bytes for initializing audio/video decoder,
// can be converted to VideoCodecData or AudioCodecData using:
//
// codecdata.(AudioCodecData) or codecdata.(VideoCodecData)
//
// for H264, CodecData is AVCDecoderConfigure bytes, includes SPS/PPS.
type CodecData interface {
Type() CodecType // Video/Audio codec type
}
type VideoCodecData interface {
CodecData
Width() int // Video width
Height() int // Video height
}
type AudioCodecData interface {
CodecData
SampleFormat() SampleFormat // audio sample format
SampleRate() int // audio sample rate
ChannelLayout() ChannelLayout // audio channel layout
PacketDuration([]byte) (time.Duration, error) // get audio compressed packet duration
}
type PacketWriter interface {
WritePacket(Packet) error
}
type PacketReader interface {
ReadPacket() (Packet,error)
}
// Muxer describes the steps of writing compressed audio/video packets into container formats like MP4/FLV/MPEG-TS.
//
// Container formats, rtmp.Conn, and transcode.Muxer implements Muxer interface.
type Muxer interface {
WriteHeader([]CodecData) error // write the file header
PacketWriter // write compressed audio/video packets
WriteTrailer() error // finish writing file, this func can be called only once
}
// Muxer with Close() method
type MuxCloser interface {
Muxer
Close() error
}
// Demuxer can read compressed audio/video packets from container formats like MP4/FLV/MPEG-TS.
type Demuxer interface {
PacketReader // read compressed audio/video packets
Streams() ([]CodecData, error) // reads the file header, contains video/audio meta infomations
}
// Demuxer with Close() method
type DemuxCloser interface {
Demuxer
Close() error
}
// Packet stores compressed audio/video data.
type Packet struct {
IsKeyFrame bool // video packet is key frame
Idx int8 // stream index in container format
CompositionTime time.Duration // packet presentation time minus decode time for H264 B-Frame
Time time.Duration // packet decode time
Data []byte // packet data
}
// Raw audio frame.
type AudioFrame struct {
SampleFormat SampleFormat // audio sample format, e.g: S16,FLTP,...
ChannelLayout ChannelLayout // audio channel layout, e.g: CH_MONO,CH_STEREO,...
SampleCount int // sample count in this frame
SampleRate int // sample rate
Data [][]byte // data array for planar format len(Data) > 1
}
func (self AudioFrame) Duration() time.Duration {
return time.Second * time.Duration(self.SampleCount) / time.Duration(self.SampleRate)
}
// Check this audio frame has same format as other audio frame.
func (self AudioFrame) HasSameFormat(other AudioFrame) bool {
if self.SampleRate != other.SampleRate {
return false
}
if self.ChannelLayout != other.ChannelLayout {
return false
}
if self.SampleFormat != other.SampleFormat {
return false
}
return true
}
// Split sample audio sample from this frame.
func (self AudioFrame) Slice(start int, end int) (out AudioFrame) {
if start > end {
panic(fmt.Sprintf("av: AudioFrame split failed start=%d end=%d invalid", start, end))
}
out = self
out.Data = append([][]byte(nil), out.Data...)
out.SampleCount = end - start
size := self.SampleFormat.BytesPerSample()
for i := range out.Data {
out.Data[i] = out.Data[i][start*size : end*size]
}
return
}
// Concat two audio frames.
func (self AudioFrame) Concat(in AudioFrame) (out AudioFrame) {
out = self
out.Data = append([][]byte(nil), out.Data...)
out.SampleCount += in.SampleCount
for i := range out.Data {
out.Data[i] = append(out.Data[i], in.Data[i]...)
}
return
}
// AudioEncoder can encode raw audio frame into compressed audio packets.
// cgo/ffmpeg inplements AudioEncoder, using ffmpeg.NewAudioEncoder to create it.
type AudioEncoder interface {
CodecData() (AudioCodecData, error) // encoder's codec data can put into container
Encode(AudioFrame) ([][]byte, error) // encode raw audio frame into compressed pakcet(s)
Close() // close encoder, free cgo contexts
SetSampleRate(int) (error) // set encoder sample rate
SetChannelLayout(ChannelLayout) (error) // set encoder channel layout
SetSampleFormat(SampleFormat) (error) // set encoder sample format
SetBitrate(int) (error) // set encoder bitrate
SetOption(string,interface{}) (error) // encoder setopt, in ffmpeg is av_opt_set_dict()
GetOption(string,interface{}) (error) // encoder getopt
}
// AudioDecoder can decode compressed audio packets into raw audio frame.
// use ffmpeg.NewAudioDecoder to create it.
type AudioDecoder interface {
Decode([]byte) (bool, AudioFrame, error) // decode one compressed audio packet
Close() // close decode, free cgo contexts
}
// AudioResampler can convert raw audio frames in different sample rate/format/channel layout.
type AudioResampler interface {
Resample(AudioFrame) (AudioFrame, error) // convert raw audio frames
}

255
av/avconv/avconv.go Normal file
View File

@@ -0,0 +1,255 @@
package avconv
import (
"fmt"
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
"github.com/deepch/vdk/av/pktque"
"github.com/deepch/vdk/av/transcode"
)
var Debug bool
type Option struct {
Transcode bool
Args []string
}
type Options struct {
OutputCodecTypes []av.CodecType
}
type Demuxer struct {
transdemux *transcode.Demuxer
streams []av.CodecData
Options
Demuxer av.Demuxer
}
func (self *Demuxer) Close() (err error) {
if self.transdemux != nil {
return self.transdemux.Close()
}
return
}
func (self *Demuxer) Streams() (streams []av.CodecData, err error) {
if err = self.prepare(); err != nil {
return
}
streams = self.streams
return
}
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
if err = self.prepare(); err != nil {
return
}
return self.transdemux.ReadPacket()
}
func (self *Demuxer) prepare() (err error) {
if self.transdemux != nil {
return
}
/*
var streams []av.CodecData
if streams, err = self.Demuxer.Streams(); err != nil {
return
}
*/
supports := self.Options.OutputCodecTypes
transopts := transcode.Options{}
transopts.FindAudioDecoderEncoder = func(codec av.AudioCodecData, i int) (ok bool, dec av.AudioDecoder, enc av.AudioEncoder, err error) {
if len(supports) == 0 {
return
}
support := false
for _, typ := range supports {
if typ == codec.Type() {
support = true
}
}
if support {
return
}
ok = true
var enctype av.CodecType
for _, typ := range supports {
if typ.IsAudio() {
if enc, _ = avutil.DefaultHandlers.NewAudioEncoder(typ); enc != nil {
enctype = typ
break
}
}
}
if enc == nil {
err = fmt.Errorf("avconv: convert %s->%s failed", codec.Type(), enctype)
return
}
// TODO: support per stream option
// enc.SetSampleRate ...
if dec, err = avutil.DefaultHandlers.NewAudioDecoder(codec); err != nil {
err = fmt.Errorf("avconv: decode %s failed", codec.Type())
return
}
return
}
self.transdemux = &transcode.Demuxer{
Options: transopts,
Demuxer: self.Demuxer,
}
if self.streams, err = self.transdemux.Streams(); err != nil {
return
}
return
}
func ConvertCmdline(args []string) (err error) {
output := ""
input := ""
flagi := false
flagv := false
flagt := false
flagre := false
duration := time.Duration(0)
options := Options{}
for _, arg := range args {
switch arg {
case "-i":
flagi = true
case "-v":
flagv = true
case "-t":
flagt = true
case "-re":
flagre = true
default:
switch {
case flagi:
flagi = false
input = arg
case flagt:
flagt = false
var f float64
fmt.Sscanf(arg, "%f", &f)
duration = time.Duration(f * float64(time.Second))
default:
output = arg
}
}
}
if input == "" {
err = fmt.Errorf("avconv: input file not specified")
return
}
if output == "" {
err = fmt.Errorf("avconv: output file not specified")
return
}
var demuxer av.DemuxCloser
var muxer av.MuxCloser
if demuxer, err = avutil.Open(input); err != nil {
return
}
defer demuxer.Close()
var handler avutil.RegisterHandler
if handler, muxer, err = avutil.DefaultHandlers.FindCreate(output); err != nil {
return
}
defer muxer.Close()
options.OutputCodecTypes = handler.CodecTypes
convdemux := &Demuxer{
Options: options,
Demuxer: demuxer,
}
defer convdemux.Close()
var streams []av.CodecData
if streams, err = demuxer.Streams(); err != nil {
return
}
var convstreams []av.CodecData
if convstreams, err = convdemux.Streams(); err != nil {
return
}
if flagv {
for _, stream := range streams {
fmt.Print(stream.Type(), " ")
}
fmt.Print("-> ")
for _, stream := range convstreams {
fmt.Print(stream.Type(), " ")
}
fmt.Println()
}
if err = muxer.WriteHeader(convstreams); err != nil {
return
}
filters := pktque.Filters{}
if flagre {
filters = append(filters, &pktque.Walltime{})
}
filterdemux := &pktque.FilterDemuxer{
Demuxer: convdemux,
Filter: filters,
}
for {
var pkt av.Packet
if pkt, err = filterdemux.ReadPacket(); err != nil {
if err == io.EOF {
err = nil
break
}
return
}
if flagv {
fmt.Println(pkt.Idx, pkt.Time, len(pkt.Data), pkt.IsKeyFrame)
}
if duration != 0 && pkt.Time > duration {
break
}
if err = muxer.WritePacket(pkt); err != nil {
return
}
}
if err = muxer.WriteTrailer(); err != nil {
return
}
return
}

312
av/avutil/avutil.go Normal file
View File

@@ -0,0 +1,312 @@
package avutil
import (
"bytes"
"fmt"
"io"
"net/url"
"os"
"path"
"strings"
"github.com/deepch/vdk/av"
)
type HandlerDemuxer struct {
av.Demuxer
r io.ReadCloser
}
func (self *HandlerDemuxer) Close() error {
return self.r.Close()
}
type HandlerMuxer struct {
av.Muxer
w io.WriteCloser
stage int
}
func (self *HandlerMuxer) WriteHeader(streams []av.CodecData) (err error) {
if self.stage == 0 {
if err = self.Muxer.WriteHeader(streams); err != nil {
return
}
self.stage++
}
return
}
func (self *HandlerMuxer) WriteTrailer() (err error) {
if self.stage == 1 {
self.stage++
if err = self.Muxer.WriteTrailer(); err != nil {
return
}
}
return
}
func (self *HandlerMuxer) Close() (err error) {
if err = self.WriteTrailer(); err != nil {
return
}
return self.w.Close()
}
type RegisterHandler struct {
Ext string
ReaderDemuxer func(io.Reader) av.Demuxer
WriterMuxer func(io.Writer) av.Muxer
UrlMuxer func(string) (bool, av.MuxCloser, error)
UrlDemuxer func(string) (bool, av.DemuxCloser, error)
UrlReader func(string) (bool, io.ReadCloser, error)
Probe func([]byte) bool
AudioEncoder func(av.CodecType) (av.AudioEncoder, error)
AudioDecoder func(av.AudioCodecData) (av.AudioDecoder, error)
ServerDemuxer func(string) (bool, av.DemuxCloser, error)
ServerMuxer func(string) (bool, av.MuxCloser, error)
CodecTypes []av.CodecType
}
type Handlers struct {
handlers []RegisterHandler
}
func (self *Handlers) Add(fn func(*RegisterHandler)) {
handler := &RegisterHandler{}
fn(handler)
self.handlers = append(self.handlers, *handler)
}
func (self *Handlers) openUrl(u *url.URL, uri string) (r io.ReadCloser, err error) {
if u != nil && u.Scheme != "" {
for _, handler := range self.handlers {
if handler.UrlReader != nil {
var ok bool
if ok, r, err = handler.UrlReader(uri); ok {
return
}
}
}
err = fmt.Errorf("avutil: openUrl %s failed", uri)
} else {
r, err = os.Open(uri)
}
return
}
func (self *Handlers) createUrl(u *url.URL, uri string) (w io.WriteCloser, err error) {
w, err = os.Create(uri)
return
}
func (self *Handlers) NewAudioEncoder(typ av.CodecType) (enc av.AudioEncoder, err error) {
for _, handler := range self.handlers {
if handler.AudioEncoder != nil {
if enc, _ = handler.AudioEncoder(typ); enc != nil {
return
}
}
}
err = fmt.Errorf("avutil: encoder", typ, "not found")
return
}
func (self *Handlers) NewAudioDecoder(codec av.AudioCodecData) (dec av.AudioDecoder, err error) {
for _, handler := range self.handlers {
if handler.AudioDecoder != nil {
if dec, _ = handler.AudioDecoder(codec); dec != nil {
return
}
}
}
err = fmt.Errorf("avutil: decoder", codec.Type(), "not found")
return
}
func (self *Handlers) Open(uri string) (demuxer av.DemuxCloser, err error) {
listen := false
if strings.HasPrefix(uri, "listen:") {
uri = uri[len("listen:"):]
listen = true
}
for _, handler := range self.handlers {
if listen {
if handler.ServerDemuxer != nil {
var ok bool
if ok, demuxer, err = handler.ServerDemuxer(uri); ok {
return
}
}
} else {
if handler.UrlDemuxer != nil {
var ok bool
if ok, demuxer, err = handler.UrlDemuxer(uri); ok {
return
}
}
}
}
var r io.ReadCloser
var ext string
var u *url.URL
if u, _ = url.Parse(uri); u != nil && u.Scheme != "" {
ext = path.Ext(u.Path)
} else {
ext = path.Ext(uri)
}
if ext != "" {
for _, handler := range self.handlers {
if handler.Ext == ext {
if handler.ReaderDemuxer != nil {
if r, err = self.openUrl(u, uri); err != nil {
return
}
demuxer = &HandlerDemuxer{
Demuxer: handler.ReaderDemuxer(r),
r: r,
}
return
}
}
}
}
var probebuf [1024]byte
if r, err = self.openUrl(u, uri); err != nil {
return
}
if _, err = io.ReadFull(r, probebuf[:]); err != nil {
return
}
for _, handler := range self.handlers {
if handler.Probe != nil && handler.Probe(probebuf[:]) && handler.ReaderDemuxer != nil {
var _r io.Reader
if rs, ok := r.(io.ReadSeeker); ok {
if _, err = rs.Seek(0, 0); err != nil {
return
}
_r = rs
} else {
_r = io.MultiReader(bytes.NewReader(probebuf[:]), r)
}
demuxer = &HandlerDemuxer{
Demuxer: handler.ReaderDemuxer(_r),
r: r,
}
return
}
}
r.Close()
err = fmt.Errorf("avutil: open %s failed", uri)
return
}
func (self *Handlers) Create(uri string) (muxer av.MuxCloser, err error) {
_, muxer, err = self.FindCreate(uri)
return
}
func (self *Handlers) FindCreate(uri string) (handler RegisterHandler, muxer av.MuxCloser, err error) {
listen := false
if strings.HasPrefix(uri, "listen:") {
uri = uri[len("listen:"):]
listen = true
}
for _, handler = range self.handlers {
if listen {
if handler.ServerMuxer != nil {
var ok bool
if ok, muxer, err = handler.ServerMuxer(uri); ok {
return
}
}
} else {
if handler.UrlMuxer != nil {
var ok bool
if ok, muxer, err = handler.UrlMuxer(uri); ok {
return
}
}
}
}
var ext string
var u *url.URL
if u, _ = url.Parse(uri); u != nil && u.Scheme != "" {
ext = path.Ext(u.Path)
} else {
ext = path.Ext(uri)
}
if ext != "" {
for _, handler = range self.handlers {
if handler.Ext == ext && handler.WriterMuxer != nil {
var w io.WriteCloser
if w, err = self.createUrl(u, uri); err != nil {
return
}
muxer = &HandlerMuxer{
Muxer: handler.WriterMuxer(w),
w: w,
}
return
}
}
}
err = fmt.Errorf("avutil: create muxer %s failed", uri)
return
}
var DefaultHandlers = &Handlers{}
func Open(url string) (demuxer av.DemuxCloser, err error) {
return DefaultHandlers.Open(url)
}
func Create(url string) (muxer av.MuxCloser, err error) {
return DefaultHandlers.Create(url)
}
func CopyPackets(dst av.PacketWriter, src av.PacketReader) (err error) {
for {
var pkt av.Packet
if pkt, err = src.ReadPacket(); err != nil {
if err == io.EOF {
break
}
return
}
if err = dst.WritePacket(pkt); err != nil {
return
}
}
return
}
func CopyFile(dst av.Muxer, src av.Demuxer) (err error) {
var streams []av.CodecData
if streams, err = src.Streams(); err != nil {
return
}
if err = dst.WriteHeader(streams); err != nil {
return
}
if err = CopyPackets(dst, src); err != nil {
if err != io.EOF {
return
}
}
if err = dst.WriteTrailer(); err != nil {
return
}
return
}

73
av/pktque/buf.go Normal file
View File

@@ -0,0 +1,73 @@
package pktque
import (
"github.com/deepch/vdk/av"
)
type Buf struct {
Head, Tail BufPos
pkts []av.Packet
Size int
Count int
}
func NewBuf() *Buf {
return &Buf{
pkts: make([]av.Packet, 64),
}
}
func (self *Buf) Pop() av.Packet {
if self.Count == 0 {
panic("pktque.Buf: Pop() when count == 0")
}
i := int(self.Head) & (len(self.pkts) - 1)
pkt := self.pkts[i]
self.pkts[i] = av.Packet{}
self.Size -= len(pkt.Data)
self.Head++
self.Count--
return pkt
}
func (self *Buf) grow() {
newpkts := make([]av.Packet, len(self.pkts)*2)
for i := self.Head; i.LT(self.Tail); i++ {
newpkts[int(i)&(len(newpkts)-1)] = self.pkts[int(i)&(len(self.pkts)-1)]
}
self.pkts = newpkts
}
func (self *Buf) Push(pkt av.Packet) {
if self.Count == len(self.pkts) {
self.grow()
}
self.pkts[int(self.Tail)&(len(self.pkts)-1)] = pkt
self.Tail++
self.Count++
self.Size += len(pkt.Data)
}
func (self *Buf) Get(pos BufPos) av.Packet {
return self.pkts[int(pos)&(len(self.pkts)-1)]
}
func (self *Buf) IsValidPos(pos BufPos) bool {
return pos.GE(self.Head) && pos.LT(self.Tail)
}
type BufPos int
func (self BufPos) LT(pos BufPos) bool {
return self-pos < 0
}
func (self BufPos) GE(pos BufPos) bool {
return self-pos >= 0
}
func (self BufPos) GT(pos BufPos) bool {
return self-pos > 0
}

190
av/pktque/filters.go Normal file
View File

@@ -0,0 +1,190 @@
// Package pktque provides packet Filter interface and structures used by other components.
package pktque
import (
"time"
"github.com/deepch/vdk/av"
)
type Filter interface {
// Change packet time or drop packet
ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error)
}
// Combine multiple Filters into one, ModifyPacket will be called in order.
type Filters []Filter
func (self Filters) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
for _, filter := range self {
if drop, err = filter.ModifyPacket(pkt, streams, videoidx, audioidx); err != nil {
return
}
if drop {
return
}
}
return
}
// Wrap origin Demuxer and Filter into a new Demuxer, when read this Demuxer filters will be called.
type FilterDemuxer struct {
av.Demuxer
Filter Filter
streams []av.CodecData
videoidx int
audioidx int
}
func (self FilterDemuxer) ReadPacket() (pkt av.Packet, err error) {
if self.streams == nil {
if self.streams, err = self.Demuxer.Streams(); err != nil {
return
}
for i, stream := range self.streams {
if stream.Type().IsVideo() {
self.videoidx = i
} else if stream.Type().IsAudio() {
self.audioidx = i
}
}
}
for {
if pkt, err = self.Demuxer.ReadPacket(); err != nil {
return
}
var drop bool
if drop, err = self.Filter.ModifyPacket(&pkt, self.streams, self.videoidx, self.audioidx); err != nil {
return
}
if !drop {
break
}
}
return
}
// Drop packets until first video key frame arrived.
type WaitKeyFrame struct {
ok bool
}
func (self *WaitKeyFrame) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
if !self.ok && pkt.Idx == int8(videoidx) && pkt.IsKeyFrame {
self.ok = true
}
drop = !self.ok
return
}
// Fix incorrect packet timestamps.
type FixTime struct {
zerobase time.Duration
incrbase time.Duration
lasttime time.Duration
StartFromZero bool // make timestamp start from zero
MakeIncrement bool // force timestamp increment
}
func (self *FixTime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
if self.StartFromZero {
if self.zerobase == 0 {
self.zerobase = pkt.Time
}
pkt.Time -= self.zerobase
}
if self.MakeIncrement {
pkt.Time -= self.incrbase
if self.lasttime == 0 {
self.lasttime = pkt.Time
}
if pkt.Time < self.lasttime || pkt.Time > self.lasttime+time.Millisecond*500 {
self.incrbase += pkt.Time - self.lasttime
pkt.Time = self.lasttime
}
self.lasttime = pkt.Time
}
return
}
// Drop incorrect packets to make A/V sync.
type AVSync struct {
MaxTimeDiff time.Duration
time []time.Duration
}
func (self *AVSync) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
if self.time == nil {
self.time = make([]time.Duration, len(streams))
if self.MaxTimeDiff == 0 {
self.MaxTimeDiff = time.Millisecond * 500
}
}
start, end, correctable, correcttime := self.check(int(pkt.Idx))
if pkt.Time >= start && pkt.Time < end {
self.time[pkt.Idx] = pkt.Time
} else {
if correctable {
pkt.Time = correcttime
for i := range self.time {
self.time[i] = correcttime
}
} else {
drop = true
}
}
return
}
func (self *AVSync) check(i int) (start time.Duration, end time.Duration, correctable bool, correcttime time.Duration) {
minidx := -1
maxidx := -1
for j := range self.time {
if minidx == -1 || self.time[j] < self.time[minidx] {
minidx = j
}
if maxidx == -1 || self.time[j] > self.time[maxidx] {
maxidx = j
}
}
allthesame := self.time[minidx] == self.time[maxidx]
if i == maxidx {
if allthesame {
correctable = true
} else {
correctable = false
}
} else {
correctable = true
}
start = self.time[minidx]
end = start + self.MaxTimeDiff
correcttime = start + time.Millisecond*40
return
}
// Make packets reading speed as same as walltime, effect like ffmpeg -re option.
type Walltime struct {
firsttime time.Time
}
func (self *Walltime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
if pkt.Idx == 0 {
if self.firsttime.IsZero() {
self.firsttime = time.Now()
}
pkttime := self.firsttime.Add(pkt.Time)
delta := pkttime.Sub(time.Now())
if delta > 0 {
time.Sleep(delta)
}
}
return
}

61
av/pktque/timeline.go Normal file
View File

@@ -0,0 +1,61 @@
package pktque
import (
"time"
)
/*
pop push
seg seg seg
|--------| |---------| |---|
20ms 40ms 5ms
----------------- time -------------------->
headtm tailtm
*/
type tlSeg struct {
tm, dur time.Duration
}
type Timeline struct {
segs []tlSeg
headtm time.Duration
}
func (self *Timeline) Push(tm time.Duration, dur time.Duration) {
if len(self.segs) > 0 {
tail := self.segs[len(self.segs)-1]
diff := tm-(tail.tm+tail.dur)
if diff < 0 {
tm -= diff
}
}
self.segs = append(self.segs, tlSeg{tm, dur})
}
func (self *Timeline) Pop(dur time.Duration) (tm time.Duration) {
if len(self.segs) == 0 {
return self.headtm
}
tm = self.segs[0].tm
for dur > 0 && len(self.segs) > 0 {
seg := &self.segs[0]
sub := dur
if seg.dur < sub {
sub = seg.dur
}
seg.dur -= sub
dur -= sub
seg.tm += sub
self.headtm += sub
if seg.dur == 0 {
copy(self.segs[0:], self.segs[1:])
self.segs = self.segs[:len(self.segs)-1]
}
}
return
}

218
av/pubsub/queue.go Normal file
View File

@@ -0,0 +1,218 @@
// Packege pubsub implements publisher-subscribers model used in multi-channel streaming.
package pubsub
import (
"io"
"sync"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/pktque"
)
// time
// ----------------->
//
// V-A-V-V-A-V-V-A-V-V
// | |
// 0 5 10
// head tail
// oldest latest
//
// One publisher and multiple subscribers thread-safe packet buffer queue.
type Queue struct {
buf *pktque.Buf
head, tail int
lock *sync.RWMutex
cond *sync.Cond
curgopcount, maxgopcount int
streams []av.CodecData
videoidx int
closed bool
}
func NewQueue() *Queue {
q := &Queue{}
q.buf = pktque.NewBuf()
q.maxgopcount = 2
q.lock = &sync.RWMutex{}
q.cond = sync.NewCond(q.lock.RLocker())
q.videoidx = -1
return q
}
func (self *Queue) SetMaxGopCount(n int) {
self.lock.Lock()
self.maxgopcount = n
self.lock.Unlock()
return
}
func (self *Queue) WriteHeader(streams []av.CodecData) error {
self.lock.Lock()
self.streams = streams
for i, stream := range streams {
if stream.Type().IsVideo() {
self.videoidx = i
}
}
self.cond.Broadcast()
self.lock.Unlock()
return nil
}
func (self *Queue) WriteTrailer() error {
return nil
}
// After Close() called, all QueueCursor's ReadPacket will return io.EOF.
func (self *Queue) Close() (err error) {
self.lock.Lock()
self.closed = true
self.cond.Broadcast()
self.lock.Unlock()
return
}
// Put packet into buffer, old packets will be discared.
func (self *Queue) WritePacket(pkt av.Packet) (err error) {
self.lock.Lock()
self.buf.Push(pkt)
if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {
self.curgopcount++
}
for self.curgopcount >= self.maxgopcount && self.buf.Count > 1 {
pkt := self.buf.Pop()
if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {
self.curgopcount--
}
if self.curgopcount < self.maxgopcount {
break
}
}
//println("shrink", self.curgopcount, self.maxgopcount, self.buf.Head, self.buf.Tail, "count", self.buf.Count, "size", self.buf.Size)
self.cond.Broadcast()
self.lock.Unlock()
return
}
type QueueCursor struct {
que *Queue
pos pktque.BufPos
gotpos bool
init func(buf *pktque.Buf, videoidx int) pktque.BufPos
}
func (self *Queue) newCursor() *QueueCursor {
return &QueueCursor{
que: self,
}
}
// Create cursor position at latest packet.
func (self *Queue) Latest() *QueueCursor {
cursor := self.newCursor()
cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {
return buf.Tail
}
return cursor
}
// Create cursor position at oldest buffered packet.
func (self *Queue) Oldest() *QueueCursor {
cursor := self.newCursor()
cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {
return buf.Head
}
return cursor
}
// Create cursor position at specific time in buffered packets.
func (self *Queue) DelayedTime(dur time.Duration) *QueueCursor {
cursor := self.newCursor()
cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {
i := buf.Tail - 1
if buf.IsValidPos(i) {
end := buf.Get(i)
for buf.IsValidPos(i) {
if end.Time-buf.Get(i).Time > dur {
break
}
i--
}
}
return i
}
return cursor
}
// Create cursor position at specific delayed GOP count in buffered packets.
func (self *Queue) DelayedGopCount(n int) *QueueCursor {
cursor := self.newCursor()
cursor.init = func(buf *pktque.Buf, videoidx int) pktque.BufPos {
i := buf.Tail - 1
if videoidx != -1 {
for gop := 0; buf.IsValidPos(i) && gop < n; i-- {
pkt := buf.Get(i)
if pkt.Idx == int8(self.videoidx) && pkt.IsKeyFrame {
gop++
}
}
}
return i
}
return cursor
}
func (self *QueueCursor) Streams() (streams []av.CodecData, err error) {
self.que.cond.L.Lock()
for self.que.streams == nil && !self.que.closed {
self.que.cond.Wait()
}
if self.que.streams != nil {
streams = self.que.streams
} else {
err = io.EOF
}
self.que.cond.L.Unlock()
return
}
// ReadPacket will not consume packets in Queue, it's just a cursor.
func (self *QueueCursor) ReadPacket() (pkt av.Packet, err error) {
self.que.cond.L.Lock()
buf := self.que.buf
if !self.gotpos {
self.pos = self.init(buf, self.que.videoidx)
self.gotpos = true
}
for {
if self.pos.LT(buf.Head) {
self.pos = buf.Head
} else if self.pos.GT(buf.Tail) {
self.pos = buf.Tail
}
if buf.IsValidPos(self.pos) {
pkt = buf.Get(self.pos)
self.pos++
break
}
if self.que.closed {
err = io.EOF
break
}
self.que.cond.Wait()
}
self.que.cond.L.Unlock()
return
}

247
av/transcode/transcode.go Normal file
View File

@@ -0,0 +1,247 @@
// Package transcoder implements Transcoder based on Muxer/Demuxer and AudioEncoder/AudioDecoder interface.
package transcode
import (
"fmt"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/pktque"
)
var Debug bool
type tStream struct {
codec av.CodecData
timeline *pktque.Timeline
aencodec, adecodec av.AudioCodecData
aenc av.AudioEncoder
adec av.AudioDecoder
}
type Options struct {
// check if transcode is needed, and create the AudioDecoder and AudioEncoder.
FindAudioDecoderEncoder func(codec av.AudioCodecData, i int) (
need bool, dec av.AudioDecoder, enc av.AudioEncoder, err error,
)
}
type Transcoder struct {
streams []*tStream
}
func NewTranscoder(streams []av.CodecData, options Options) (_self *Transcoder, err error) {
self := &Transcoder{}
self.streams = []*tStream{}
for i, stream := range streams {
ts := &tStream{codec: stream}
if stream.Type().IsAudio() {
if options.FindAudioDecoderEncoder != nil {
var ok bool
var enc av.AudioEncoder
var dec av.AudioDecoder
ok, dec, enc, err = options.FindAudioDecoderEncoder(stream.(av.AudioCodecData), i)
if ok {
if err != nil {
return
}
ts.timeline = &pktque.Timeline{}
if ts.codec, err = enc.CodecData(); err != nil {
return
}
ts.aencodec = ts.codec.(av.AudioCodecData)
ts.adecodec = stream.(av.AudioCodecData)
ts.aenc = enc
ts.adec = dec
}
}
}
self.streams = append(self.streams, ts)
}
_self = self
return
}
func (self *tStream) audioDecodeAndEncode(inpkt av.Packet) (outpkts []av.Packet, err error) {
var dur time.Duration
var frame av.AudioFrame
var ok bool
if ok, frame, err = self.adec.Decode(inpkt.Data); err != nil {
return
}
if !ok {
return
}
if dur, err = self.adecodec.PacketDuration(inpkt.Data); err != nil {
err = fmt.Errorf("transcode: PacketDuration() failed for input stream #%d", inpkt.Idx)
return
}
if Debug {
fmt.Println("transcode: push", inpkt.Time, dur)
}
self.timeline.Push(inpkt.Time, dur)
var _outpkts [][]byte
if _outpkts, err = self.aenc.Encode(frame); err != nil {
return
}
for _, _outpkt := range _outpkts {
if dur, err = self.aencodec.PacketDuration(_outpkt); err != nil {
err = fmt.Errorf("transcode: PacketDuration() failed for output stream #%d", inpkt.Idx)
return
}
outpkt := av.Packet{Idx: inpkt.Idx, Data: _outpkt}
outpkt.Time = self.timeline.Pop(dur)
if Debug {
fmt.Println("transcode: pop", outpkt.Time, dur)
}
outpkts = append(outpkts, outpkt)
}
return
}
// Do the transcode.
//
// In audio transcoding one Packet may transcode into many Packets
// packet time will be adjusted automatically.
func (self *Transcoder) Do(pkt av.Packet) (out []av.Packet, err error) {
stream := self.streams[pkt.Idx]
if stream.aenc != nil && stream.adec != nil {
if out, err = stream.audioDecodeAndEncode(pkt); err != nil {
return
}
} else {
out = append(out, pkt)
}
return
}
// Get CodecDatas after transcoding.
func (self *Transcoder) Streams() (streams []av.CodecData, err error) {
for _, stream := range self.streams {
streams = append(streams, stream.codec)
}
return
}
// Close transcoder, close related encoder and decoders.
func (self *Transcoder) Close() (err error) {
for _, stream := range self.streams {
if stream.aenc != nil {
stream.aenc.Close()
stream.aenc = nil
}
if stream.adec != nil {
stream.adec.Close()
stream.adec = nil
}
}
self.streams = nil
return
}
// Wrap transcoder and origin Muxer into new Muxer.
// Write to new Muxer will do transcoding automatically.
type Muxer struct {
av.Muxer // origin Muxer
Options // transcode options
transcoder *Transcoder
}
func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {
if self.transcoder, err = NewTranscoder(streams, self.Options); err != nil {
return
}
var newstreams []av.CodecData
if newstreams, err = self.transcoder.Streams(); err != nil {
return
}
if err = self.Muxer.WriteHeader(newstreams); err != nil {
return
}
return
}
func (self *Muxer) WritePacket(pkt av.Packet) (err error) {
var outpkts []av.Packet
if outpkts, err = self.transcoder.Do(pkt); err != nil {
return
}
for _, pkt := range outpkts {
if err = self.Muxer.WritePacket(pkt); err != nil {
return
}
}
return
}
func (self *Muxer) Close() (err error) {
if self.transcoder != nil {
return self.transcoder.Close()
}
return
}
// Wrap transcoder and origin Demuxer into new Demuxer.
// Read this Demuxer will do transcoding automatically.
type Demuxer struct {
av.Demuxer
Options
transcoder *Transcoder
outpkts []av.Packet
}
func (self *Demuxer) prepare() (err error) {
if self.transcoder == nil {
var streams []av.CodecData
if streams, err = self.Demuxer.Streams(); err != nil {
return
}
if self.transcoder, err = NewTranscoder(streams, self.Options); err != nil {
return
}
}
return
}
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
if err = self.prepare(); err != nil {
return
}
for {
if len(self.outpkts) > 0 {
pkt = self.outpkts[0]
self.outpkts = self.outpkts[1:]
return
}
var rpkt av.Packet
if rpkt, err = self.Demuxer.ReadPacket(); err != nil {
return
}
if self.outpkts, err = self.transcoder.Do(rpkt); err != nil {
return
}
}
return
}
func (self *Demuxer) Streams() (streams []av.CodecData, err error) {
if err = self.prepare(); err != nil {
return
}
return self.transcoder.Streams()
}
func (self *Demuxer) Close() (err error) {
if self.transcoder != nil {
return self.transcoder.Close()
}
return
}