Compare commits
19 Commits
Author | SHA1 | Date |
---|---|---|
|
3bad711693 | |
|
f5a86e9282 | |
|
4dc3a544f7 | |
|
f9c5b5e0c6 | |
|
426c26c613 | |
|
4187d0dfda | |
|
a4e70df617 | |
|
994c0995e0 | |
|
a99fa94072 | |
|
331286f9d0 | |
|
c0774e8ba6 | |
|
0d8ecc64ad | |
|
6481884b73 | |
|
f8dce5ce9d | |
|
7f13d014b9 | |
|
78f280e743 | |
|
69147a81b3 | |
|
ae6b69d40d | |
|
50e7125bce |
|
@ -22,8 +22,11 @@ _testmain.go
|
|||
# built binaries
|
||||
uplink
|
||||
*.exe
|
||||
*.dll
|
||||
*.test
|
||||
|
||||
*.orig
|
||||
|
||||
###############################################################################
|
||||
|
||||
# Windows image file caches
|
||||
|
|
|
@ -17,6 +17,8 @@ type Channel struct {
|
|||
OutputStreams map[string]*media.MediaStream
|
||||
|
||||
Events *pubsub.PubSub
|
||||
|
||||
lastMetadata map[string]string
|
||||
}
|
||||
|
||||
func (channel *Channel) AddInputStream(id string) *media.MediaStream {
|
||||
|
@ -50,12 +52,17 @@ func (channel *Channel) AddOutputContainer(id string) *media.MediaStreamContaine
|
|||
}
|
||||
|
||||
func (channel *Channel) SetMetadata(data map[string]string) {
|
||||
channel.lastMetadata = data
|
||||
channel.Events.Pub(data, "metadata")
|
||||
}
|
||||
|
||||
func (channel *Channel) Metadata() chan map[string]string {
|
||||
outC := make(chan map[string]string)
|
||||
go func() {
|
||||
defer close(outC)
|
||||
if channel.lastMetadata != nil {
|
||||
outC <- channel.lastMetadata
|
||||
}
|
||||
c := channel.Events.Sub("metadata")
|
||||
forloop:
|
||||
for event := range c {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package genericapi
|
||||
|
||||
type DynamicallyConfigurable interface {
|
||||
MakeConfigurationObject() interface{}
|
||||
ValidateConfiguration(config interface{}) bool
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package codecs
|
||||
|
||||
//go:generate go run ./internal/generator/main.go
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,83 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func must(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := exec.Command("ffmpeg", "-codecs")
|
||||
outputBytes, err := cmd.Output()
|
||||
must(err)
|
||||
|
||||
code := `package codecs
|
||||
|
||||
var (
|
||||
`
|
||||
|
||||
bufreader := bufio.NewScanner(bytes.NewReader(outputBytes))
|
||||
|
||||
/*Encoders:
|
||||
V..... = Video
|
||||
A..... = Audio
|
||||
S..... = Subtitle
|
||||
.F.... = Frame-level multithreading
|
||||
..S... = Slice-level multithreading
|
||||
...X.. = Codec is experimental
|
||||
....B. = Supports draw_horiz_band
|
||||
.....D = Supports direct rendering method 1
|
||||
------
|
||||
V..... a64multi Multicolor charset for Commodore 64 (codec a64_multi)
|
||||
V..... a64multi5 Multicolor charset for Commodore 64, extended with 5th color (colram) (codec a64_multi5)*/
|
||||
|
||||
waitingForList := true
|
||||
for bufreader.Scan() && waitingForList {
|
||||
text := bufreader.Text()
|
||||
if text == " -------" {
|
||||
waitingForList = false
|
||||
}
|
||||
}
|
||||
|
||||
for bufreader.Scan() {
|
||||
text := bufreader.Text()
|
||||
//isVideo := text[1] == 'V'
|
||||
//isAudio := text[1] == 'A'
|
||||
//isSubtitle := text[1] == 'S'
|
||||
|
||||
text = text[8:]
|
||||
cols := strings.Fields(text)
|
||||
|
||||
id := cols[0]
|
||||
name := strings.Join(cols[1:], " ")
|
||||
code += fmt.Sprintf(`
|
||||
// %s
|
||||
Codec_%s = %q
|
||||
`, name, strings.ToUpper(id), id)
|
||||
}
|
||||
|
||||
code += `
|
||||
)
|
||||
`
|
||||
|
||||
codeBytes, err := format.Source([]byte(code))
|
||||
must(err)
|
||||
|
||||
f, err := os.Create("gen_codecs.go")
|
||||
must(err)
|
||||
defer f.Close()
|
||||
_, err = f.Write(codeBytes)
|
||||
must(err)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package demux
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"git.icedream.tech/icedream/uplink/app/media"
|
||||
)
|
||||
|
||||
type Demuxer interface {
|
||||
NewInstance(r io.Reader) <-DemuxerInstance
|
||||
}
|
||||
|
||||
type DemuxerInstance interface {
|
||||
Error() error
|
||||
Reader() io.Reader
|
||||
ContainerInfo() *media.MediaStreamContainerInfo
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
|
@ -13,7 +14,7 @@ import (
|
|||
func Test_Demux(t *testing.T) {
|
||||
Convey("Demuxer", t, func() {
|
||||
Convey("audio-only", func() {
|
||||
reader, _ := os.Open("mpthreetest.mp3")
|
||||
reader, _ := os.Open(filepath.Join("testassets", "mpthreetest.mp3"))
|
||||
defer reader.Close()
|
||||
|
||||
demuxer := Demux(reader)
|
||||
|
@ -28,7 +29,7 @@ func Test_Demux(t *testing.T) {
|
|||
So(audioStream, ShouldBeNil)
|
||||
So(stream.StreamId, ShouldEqual, 0)
|
||||
So(stream.Pts, ShouldEqual, 0)
|
||||
So(stream.CodecInfo.CodecName, ShouldEqual, "mp3")
|
||||
So(stream.CodecInfo.CodecName, ShouldBeIn, []string{"mp3", "mp3float"})
|
||||
So(stream.CodecInfo.Type, ShouldEqual, Audio)
|
||||
audioStream = stream
|
||||
}
|
||||
|
@ -43,7 +44,7 @@ func Test_Demux(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("video and audio", func() {
|
||||
reader, _ := os.Open("small.ogv")
|
||||
reader, _ := os.Open(filepath.Join("testassets", "small.ogv"))
|
||||
defer reader.Close()
|
||||
|
||||
demuxer := Demux(reader)
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package formats
|
||||
|
||||
//go:generate go run ./internal/generator/main.go
|
|
@ -0,0 +1,839 @@
|
|||
package formats
|
||||
|
||||
var (
|
||||
|
||||
// 3GP (3GPP file format)
|
||||
Format_3GP = "3gp"
|
||||
// 3GP (3GPP file format)
|
||||
Mux_Format_3GP = Format_3GP
|
||||
|
||||
// a64 - video for Commodore 64
|
||||
Format_A64 = "a64"
|
||||
// a64 - video for Commodore 64
|
||||
Mux_Format_A64 = Format_A64
|
||||
|
||||
// raw AC-3
|
||||
Format_AC3 = "ac3"
|
||||
// raw AC-3
|
||||
Mux_Format_AC3 = Format_AC3
|
||||
|
||||
// ADTS AAC (Advanced Audio Coding)
|
||||
Format_ADTS = "adts"
|
||||
// ADTS AAC (Advanced Audio Coding)
|
||||
Mux_Format_ADTS = Format_ADTS
|
||||
|
||||
// CRI ADX
|
||||
Format_ADX = "adx"
|
||||
// CRI ADX
|
||||
Mux_Format_ADX = Format_ADX
|
||||
|
||||
// Audio IFF
|
||||
Format_AIFF = "aiff"
|
||||
// Audio IFF
|
||||
Mux_Format_AIFF = Format_AIFF
|
||||
|
||||
// PCM A-law
|
||||
Format_ALAW = "alaw"
|
||||
// PCM A-law
|
||||
Mux_Format_ALAW = Format_ALAW
|
||||
|
||||
// ALSA audio output
|
||||
Format_ALSA = "alsa"
|
||||
// ALSA audio output
|
||||
Mux_Format_ALSA = Format_ALSA
|
||||
|
||||
// 3GPP AMR
|
||||
Format_AMR = "amr"
|
||||
// 3GPP AMR
|
||||
Mux_Format_AMR = Format_AMR
|
||||
|
||||
// Animated Portable Network Graphics
|
||||
Format_APNG = "apng"
|
||||
// Animated Portable Network Graphics
|
||||
Mux_Format_APNG = Format_APNG
|
||||
|
||||
// raw aptX (Audio Processing Technology for Bluetooth)
|
||||
Format_APTX = "aptx"
|
||||
// raw aptX (Audio Processing Technology for Bluetooth)
|
||||
Mux_Format_APTX = Format_APTX
|
||||
|
||||
// raw aptX HD (Audio Processing Technology for Bluetooth)
|
||||
Format_APTX_HD = "aptx_hd"
|
||||
// raw aptX HD (Audio Processing Technology for Bluetooth)
|
||||
Mux_Format_APTX_HD = Format_APTX_HD
|
||||
|
||||
// ASF (Advanced / Active Streaming Format)
|
||||
Format_ASF = "asf"
|
||||
// ASF (Advanced / Active Streaming Format)
|
||||
Mux_Format_ASF = Format_ASF
|
||||
|
||||
// ASF (Advanced / Active Streaming Format)
|
||||
Format_ASF_STREAM = "asf_stream"
|
||||
// ASF (Advanced / Active Streaming Format)
|
||||
Mux_Format_ASF_STREAM = Format_ASF_STREAM
|
||||
|
||||
// SSA (SubStation Alpha) subtitle
|
||||
Format_ASS = "ass"
|
||||
// SSA (SubStation Alpha) subtitle
|
||||
Mux_Format_ASS = Format_ASS
|
||||
|
||||
// AST (Audio Stream)
|
||||
Format_AST = "ast"
|
||||
// AST (Audio Stream)
|
||||
Mux_Format_AST = Format_AST
|
||||
|
||||
// Sun AU
|
||||
Format_AU = "au"
|
||||
// Sun AU
|
||||
Mux_Format_AU = Format_AU
|
||||
|
||||
// AVI (Audio Video Interleaved)
|
||||
Format_AVI = "avi"
|
||||
// AVI (Audio Video Interleaved)
|
||||
Mux_Format_AVI = Format_AVI
|
||||
|
||||
// SWF (ShockWave Flash) (AVM2)
|
||||
Format_AVM2 = "avm2"
|
||||
// SWF (ShockWave Flash) (AVM2)
|
||||
Mux_Format_AVM2 = Format_AVM2
|
||||
|
||||
// G.729 BIT file format
|
||||
Format_BIT = "bit"
|
||||
// G.729 BIT file format
|
||||
Mux_Format_BIT = Format_BIT
|
||||
|
||||
// Apple CAF (Core Audio Format)
|
||||
Format_CAF = "caf"
|
||||
// Apple CAF (Core Audio Format)
|
||||
Mux_Format_CAF = Format_CAF
|
||||
|
||||
// raw Chinese AVS (Audio Video Standard) video
|
||||
Format_CAVSVIDEO = "cavsvideo"
|
||||
// raw Chinese AVS (Audio Video Standard) video
|
||||
Mux_Format_CAVSVIDEO = Format_CAVSVIDEO
|
||||
|
||||
// codec2 .c2 muxer
|
||||
Format_CODEC2 = "codec2"
|
||||
// codec2 .c2 muxer
|
||||
Mux_Format_CODEC2 = Format_CODEC2
|
||||
|
||||
// raw codec2 muxer
|
||||
Format_CODEC2RAW = "codec2raw"
|
||||
// raw codec2 muxer
|
||||
Mux_Format_CODEC2RAW = Format_CODEC2RAW
|
||||
|
||||
// CRC testing
|
||||
Format_CRC = "crc"
|
||||
// CRC testing
|
||||
Mux_Format_CRC = Format_CRC
|
||||
|
||||
// DASH Muxer
|
||||
Format_DASH = "dash"
|
||||
// DASH Muxer
|
||||
Mux_Format_DASH = Format_DASH
|
||||
|
||||
// raw data
|
||||
Format_DATA = "data"
|
||||
// raw data
|
||||
Mux_Format_DATA = Format_DATA
|
||||
|
||||
// D-Cinema audio
|
||||
Format_DAUD = "daud"
|
||||
// D-Cinema audio
|
||||
Mux_Format_DAUD = Format_DAUD
|
||||
|
||||
// raw Dirac
|
||||
Format_DIRAC = "dirac"
|
||||
// raw Dirac
|
||||
Mux_Format_DIRAC = Format_DIRAC
|
||||
|
||||
// raw DNxHD (SMPTE VC-3)
|
||||
Format_DNXHD = "dnxhd"
|
||||
// raw DNxHD (SMPTE VC-3)
|
||||
Mux_Format_DNXHD = Format_DNXHD
|
||||
|
||||
// raw DTS
|
||||
Format_DTS = "dts"
|
||||
// raw DTS
|
||||
Mux_Format_DTS = Format_DTS
|
||||
|
||||
// DV (Digital Video)
|
||||
Format_DV = "dv"
|
||||
// DV (Digital Video)
|
||||
Mux_Format_DV = Format_DV
|
||||
|
||||
// MPEG-2 PS (DVD VOB)
|
||||
Format_DVD = "dvd"
|
||||
// MPEG-2 PS (DVD VOB)
|
||||
Mux_Format_DVD = Format_DVD
|
||||
|
||||
// raw E-AC-3
|
||||
Format_EAC3 = "eac3"
|
||||
// raw E-AC-3
|
||||
Mux_Format_EAC3 = Format_EAC3
|
||||
|
||||
// PCM 32-bit floating-point big-endian
|
||||
Format_F32BE = "f32be"
|
||||
// PCM 32-bit floating-point big-endian
|
||||
Mux_Format_F32BE = Format_F32BE
|
||||
|
||||
// PCM 32-bit floating-point little-endian
|
||||
Format_F32LE = "f32le"
|
||||
// PCM 32-bit floating-point little-endian
|
||||
Mux_Format_F32LE = Format_F32LE
|
||||
|
||||
// F4V Adobe Flash Video
|
||||
Format_F4V = "f4v"
|
||||
// F4V Adobe Flash Video
|
||||
Mux_Format_F4V = Format_F4V
|
||||
|
||||
// PCM 64-bit floating-point big-endian
|
||||
Format_F64BE = "f64be"
|
||||
// PCM 64-bit floating-point big-endian
|
||||
Mux_Format_F64BE = Format_F64BE
|
||||
|
||||
// PCM 64-bit floating-point little-endian
|
||||
Format_F64LE = "f64le"
|
||||
// PCM 64-bit floating-point little-endian
|
||||
Mux_Format_F64LE = Format_F64LE
|
||||
|
||||
// Linux framebuffer
|
||||
Format_FBDEV = "fbdev"
|
||||
// Linux framebuffer
|
||||
Mux_Format_FBDEV = Format_FBDEV
|
||||
|
||||
// FFmpeg metadata in text
|
||||
Format_FFMETADATA = "ffmetadata"
|
||||
// FFmpeg metadata in text
|
||||
Mux_Format_FFMETADATA = Format_FFMETADATA
|
||||
|
||||
// FIFO queue pseudo-muxer
|
||||
Format_FIFO = "fifo"
|
||||
// FIFO queue pseudo-muxer
|
||||
Mux_Format_FIFO = Format_FIFO
|
||||
|
||||
// Fifo test muxer
|
||||
Format_FIFO_TEST = "fifo_test"
|
||||
// Fifo test muxer
|
||||
Mux_Format_FIFO_TEST = Format_FIFO_TEST
|
||||
|
||||
// Sega FILM / CPK
|
||||
Format_FILM_CPK = "film_cpk"
|
||||
// Sega FILM / CPK
|
||||
Mux_Format_FILM_CPK = Format_FILM_CPK
|
||||
|
||||
// Adobe Filmstrip
|
||||
Format_FILMSTRIP = "filmstrip"
|
||||
// Adobe Filmstrip
|
||||
Mux_Format_FILMSTRIP = Format_FILMSTRIP
|
||||
|
||||
// Flexible Image Transport System
|
||||
Format_FITS = "fits"
|
||||
// Flexible Image Transport System
|
||||
Mux_Format_FITS = Format_FITS
|
||||
|
||||
// raw FLAC
|
||||
Format_FLAC = "flac"
|
||||
// raw FLAC
|
||||
Mux_Format_FLAC = Format_FLAC
|
||||
|
||||
// FLV (Flash Video)
|
||||
Format_FLV = "flv"
|
||||
// FLV (Flash Video)
|
||||
Mux_Format_FLV = Format_FLV
|
||||
|
||||
// framecrc testing
|
||||
Format_FRAMECRC = "framecrc"
|
||||
// framecrc testing
|
||||
Mux_Format_FRAMECRC = Format_FRAMECRC
|
||||
|
||||
// Per-frame hash testing
|
||||
Format_FRAMEHASH = "framehash"
|
||||
// Per-frame hash testing
|
||||
Mux_Format_FRAMEHASH = Format_FRAMEHASH
|
||||
|
||||
// Per-frame MD5 testing
|
||||
Format_FRAMEMD5 = "framemd5"
|
||||
// Per-frame MD5 testing
|
||||
Mux_Format_FRAMEMD5 = Format_FRAMEMD5
|
||||
|
||||
// raw G.722
|
||||
Format_G722 = "g722"
|
||||
// raw G.722
|
||||
Mux_Format_G722 = Format_G722
|
||||
|
||||
// raw G.723.1
|
||||
Format_G723_1 = "g723_1"
|
||||
// raw G.723.1
|
||||
Mux_Format_G723_1 = Format_G723_1
|
||||
|
||||
// raw big-endian G.726 ("left-justified")
|
||||
Format_G726 = "g726"
|
||||
// raw big-endian G.726 ("left-justified")
|
||||
Mux_Format_G726 = Format_G726
|
||||
|
||||
// raw little-endian G.726 ("right-justified")
|
||||
Format_G726LE = "g726le"
|
||||
// raw little-endian G.726 ("right-justified")
|
||||
Mux_Format_G726LE = Format_G726LE
|
||||
|
||||
// GIF Animation
|
||||
Format_GIF = "gif"
|
||||
// GIF Animation
|
||||
Mux_Format_GIF = Format_GIF
|
||||
|
||||
// raw GSM
|
||||
Format_GSM = "gsm"
|
||||
// raw GSM
|
||||
Mux_Format_GSM = Format_GSM
|
||||
|
||||
// GXF (General eXchange Format)
|
||||
Format_GXF = "gxf"
|
||||
// GXF (General eXchange Format)
|
||||
Mux_Format_GXF = Format_GXF
|
||||
|
||||
// raw H.261
|
||||
Format_H261 = "h261"
|
||||
// raw H.261
|
||||
Mux_Format_H261 = Format_H261
|
||||
|
||||
// raw H.263
|
||||
Format_H263 = "h263"
|
||||
// raw H.263
|
||||
Mux_Format_H263 = Format_H263
|
||||
|
||||
// raw H.264 video
|
||||
Format_H264 = "h264"
|
||||
// raw H.264 video
|
||||
Mux_Format_H264 = Format_H264
|
||||
|
||||
// Hash testing
|
||||
Format_HASH = "hash"
|
||||
// Hash testing
|
||||
Mux_Format_HASH = Format_HASH
|
||||
|
||||
// HDS Muxer
|
||||
Format_HDS = "hds"
|
||||
// HDS Muxer
|
||||
Mux_Format_HDS = Format_HDS
|
||||
|
||||
// raw HEVC video
|
||||
Format_HEVC = "hevc"
|
||||
// raw HEVC video
|
||||
Mux_Format_HEVC = Format_HEVC
|
||||
|
||||
// Apple HTTP Live Streaming
|
||||
Format_HLS = "hls"
|
||||
// Apple HTTP Live Streaming
|
||||
Mux_Format_HLS = Format_HLS
|
||||
|
||||
// Microsoft Windows ICO
|
||||
Format_ICO = "ico"
|
||||
// Microsoft Windows ICO
|
||||
Mux_Format_ICO = Format_ICO
|
||||
|
||||
// iLBC storage
|
||||
Format_ILBC = "ilbc"
|
||||
// iLBC storage
|
||||
Mux_Format_ILBC = Format_ILBC
|
||||
|
||||
// image2 sequence
|
||||
Format_IMAGE2 = "image2"
|
||||
// image2 sequence
|
||||
Mux_Format_IMAGE2 = Format_IMAGE2
|
||||
|
||||
// piped image2 sequence
|
||||
Format_IMAGE2PIPE = "image2pipe"
|
||||
// piped image2 sequence
|
||||
Mux_Format_IMAGE2PIPE = Format_IMAGE2PIPE
|
||||
|
||||
// iPod H.264 MP4 (MPEG-4 Part 14)
|
||||
Format_IPOD = "ipod"
|
||||
// iPod H.264 MP4 (MPEG-4 Part 14)
|
||||
Mux_Format_IPOD = Format_IPOD
|
||||
|
||||
// Berkeley/IRCAM/CARL Sound Format
|
||||
Format_IRCAM = "ircam"
|
||||
// Berkeley/IRCAM/CARL Sound Format
|
||||
Mux_Format_IRCAM = Format_IRCAM
|
||||
|
||||
// ISMV/ISMA (Smooth Streaming)
|
||||
Format_ISMV = "ismv"
|
||||
// ISMV/ISMA (Smooth Streaming)
|
||||
Mux_Format_ISMV = Format_ISMV
|
||||
|
||||
// On2 IVF
|
||||
Format_IVF = "ivf"
|
||||
// On2 IVF
|
||||
Mux_Format_IVF = Format_IVF
|
||||
|
||||
// JACOsub subtitle format
|
||||
Format_JACOSUB = "jacosub"
|
||||
// JACOsub subtitle format
|
||||
Mux_Format_JACOSUB = Format_JACOSUB
|
||||
|
||||
// LOAS/LATM
|
||||
Format_LATM = "latm"
|
||||
// LOAS/LATM
|
||||
Mux_Format_LATM = Format_LATM
|
||||
|
||||
// LRC lyrics
|
||||
Format_LRC = "lrc"
|
||||
// LRC lyrics
|
||||
Mux_Format_LRC = Format_LRC
|
||||
|
||||
// raw MPEG-4 video
|
||||
Format_M4V = "m4v"
|
||||
// raw MPEG-4 video
|
||||
Mux_Format_M4V = Format_M4V
|
||||
|
||||
// Matroska
|
||||
Format_MATROSKA = "matroska"
|
||||
// Matroska
|
||||
Mux_Format_MATROSKA = Format_MATROSKA
|
||||
|
||||
// MD5 testing
|
||||
Format_MD5 = "md5"
|
||||
// MD5 testing
|
||||
Mux_Format_MD5 = Format_MD5
|
||||
|
||||
// MicroDVD subtitle format
|
||||
Format_MICRODVD = "microdvd"
|
||||
// MicroDVD subtitle format
|
||||
Mux_Format_MICRODVD = Format_MICRODVD
|
||||
|
||||
// raw MJPEG video
|
||||
Format_MJPEG = "mjpeg"
|
||||
// raw MJPEG video
|
||||
Mux_Format_MJPEG = Format_MJPEG
|
||||
|
||||
// extract pts as timecode v2 format, as defined by mkvtoolnix
|
||||
Format_MKVTIMESTAMP_V2 = "mkvtimestamp_v2"
|
||||
// extract pts as timecode v2 format, as defined by mkvtoolnix
|
||||
Mux_Format_MKVTIMESTAMP_V2 = Format_MKVTIMESTAMP_V2
|
||||
|
||||
// raw MLP
|
||||
Format_MLP = "mlp"
|
||||
// raw MLP
|
||||
Mux_Format_MLP = Format_MLP
|
||||
|
||||
// Yamaha SMAF
|
||||
Format_MMF = "mmf"
|
||||
// Yamaha SMAF
|
||||
Mux_Format_MMF = Format_MMF
|
||||
|
||||
// QuickTime / MOV
|
||||
Format_MOV = "mov"
|
||||
// QuickTime / MOV
|
||||
Mux_Format_MOV = Format_MOV
|
||||
|
||||
// MP2 (MPEG audio layer 2)
|
||||
Format_MP2 = "mp2"
|
||||
// MP2 (MPEG audio layer 2)
|
||||
Mux_Format_MP2 = Format_MP2
|
||||
|
||||
// MP3 (MPEG audio layer 3)
|
||||
Format_MP3 = "mp3"
|
||||
// MP3 (MPEG audio layer 3)
|
||||
Mux_Format_MP3 = Format_MP3
|
||||
|
||||
// MP4 (MPEG-4 Part 14)
|
||||
Format_MP4 = "mp4"
|
||||
// MP4 (MPEG-4 Part 14)
|
||||
Mux_Format_MP4 = Format_MP4
|
||||
|
||||
// MPEG-1 Systems / MPEG program stream
|
||||
Format_MPEG = "mpeg"
|
||||
// MPEG-1 Systems / MPEG program stream
|
||||
Mux_Format_MPEG = Format_MPEG
|
||||
|
||||
// raw MPEG-1 video
|
||||
Format_MPEG1VIDEO = "mpeg1video"
|
||||
// raw MPEG-1 video
|
||||
Mux_Format_MPEG1VIDEO = Format_MPEG1VIDEO
|
||||
|
||||
// raw MPEG-2 video
|
||||
Format_MPEG2VIDEO = "mpeg2video"
|
||||
// raw MPEG-2 video
|
||||
Mux_Format_MPEG2VIDEO = Format_MPEG2VIDEO
|
||||
|
||||
// MPEG-TS (MPEG-2 Transport Stream)
|
||||
Format_MPEGTS = "mpegts"
|
||||
// MPEG-TS (MPEG-2 Transport Stream)
|
||||
Mux_Format_MPEGTS = Format_MPEGTS
|
||||
|
||||
// MIME multipart JPEG
|
||||
Format_MPJPEG = "mpjpeg"
|
||||
// MIME multipart JPEG
|
||||
Mux_Format_MPJPEG = Format_MPJPEG
|
||||
|
||||
// PCM mu-law
|
||||
Format_MULAW = "mulaw"
|
||||
// PCM mu-law
|
||||
Mux_Format_MULAW = Format_MULAW
|
||||
|
||||
// MXF (Material eXchange Format)
|
||||
Format_MXF = "mxf"
|
||||
// MXF (Material eXchange Format)
|
||||
Mux_Format_MXF = Format_MXF
|
||||
|
||||
// MXF (Material eXchange Format) D-10 Mapping
|
||||
Format_MXF_D10 = "mxf_d10"
|
||||
// MXF (Material eXchange Format) D-10 Mapping
|
||||
Mux_Format_MXF_D10 = Format_MXF_D10
|
||||
|
||||
// MXF (Material eXchange Format) Operational Pattern Atom
|
||||
Format_MXF_OPATOM = "mxf_opatom"
|
||||
// MXF (Material eXchange Format) Operational Pattern Atom
|
||||
Mux_Format_MXF_OPATOM = Format_MXF_OPATOM
|
||||
|
||||
// raw null video
|
||||
Format_NULL = "null"
|
||||
// raw null video
|
||||
Mux_Format_NULL = Format_NULL
|
||||
|
||||
// NUT
|
||||
Format_NUT = "nut"
|
||||
// NUT
|
||||
Mux_Format_NUT = Format_NUT
|
||||
|
||||
// Ogg Audio
|
||||
Format_OGA = "oga"
|
||||
// Ogg Audio
|
||||
Mux_Format_OGA = Format_OGA
|
||||
|
||||
// Ogg
|
||||
Format_OGG = "ogg"
|
||||
// Ogg
|
||||
Mux_Format_OGG = Format_OGG
|
||||
|
||||
// Ogg Video
|
||||
Format_OGV = "ogv"
|
||||
// Ogg Video
|
||||
Mux_Format_OGV = Format_OGV
|
||||
|
||||
// Sony OpenMG audio
|
||||
Format_OMA = "oma"
|
||||
// Sony OpenMG audio
|
||||
Mux_Format_OMA = Format_OMA
|
||||
|
||||
// Ogg Opus
|
||||
Format_OPUS = "opus"
|
||||
// Ogg Opus
|
||||
Mux_Format_OPUS = Format_OPUS
|
||||
|
||||
// OSS (Open Sound System) playback
|
||||
Format_OSS = "oss"
|
||||
// OSS (Open Sound System) playback
|
||||
Mux_Format_OSS = Format_OSS
|
||||
|
||||
// PSP MP4 (MPEG-4 Part 14)
|
||||
Format_PSP = "psp"
|
||||
// PSP MP4 (MPEG-4 Part 14)
|
||||
Mux_Format_PSP = Format_PSP
|
||||
|
||||
// Pulse audio output
|
||||
Format_PULSE = "pulse"
|
||||
// Pulse audio output
|
||||
Mux_Format_PULSE = Format_PULSE
|
||||
|
||||
// raw video
|
||||
Format_RAWVIDEO = "rawvideo"
|
||||
// raw video
|
||||
Mux_Format_RAWVIDEO = Format_RAWVIDEO
|
||||
|
||||
// RealMedia
|
||||
Format_RM = "rm"
|
||||
// RealMedia
|
||||
Mux_Format_RM = Format_RM
|
||||
|
||||
// raw id RoQ
|
||||
Format_ROQ = "roq"
|
||||
// raw id RoQ
|
||||
Mux_Format_ROQ = Format_ROQ
|
||||
|
||||
// Lego Mindstorms RSO
|
||||
Format_RSO = "rso"
|
||||
// Lego Mindstorms RSO
|
||||
Mux_Format_RSO = Format_RSO
|
||||
|
||||
// RTP output
|
||||
Format_RTP = "rtp"
|
||||
// RTP output
|
||||
Mux_Format_RTP = Format_RTP
|
||||
|
||||
// RTP/mpegts output format
|
||||
Format_RTP_MPEGTS = "rtp_mpegts"
|
||||
// RTP/mpegts output format
|
||||
Mux_Format_RTP_MPEGTS = Format_RTP_MPEGTS
|
||||
|
||||
// RTSP output
|
||||
Format_RTSP = "rtsp"
|
||||
// RTSP output
|
||||
Mux_Format_RTSP = Format_RTSP
|
||||
|
||||
// PCM signed 16-bit big-endian
|
||||
Format_S16BE = "s16be"
|
||||
// PCM signed 16-bit big-endian
|
||||
Mux_Format_S16BE = Format_S16BE
|
||||
|
||||
// PCM signed 16-bit little-endian
|
||||
Format_S16LE = "s16le"
|
||||
// PCM signed 16-bit little-endian
|
||||
Mux_Format_S16LE = Format_S16LE
|
||||
|
||||
// PCM signed 24-bit big-endian
|
||||
Format_S24BE = "s24be"
|
||||
// PCM signed 24-bit big-endian
|
||||
Mux_Format_S24BE = Format_S24BE
|
||||
|
||||
// PCM signed 24-bit little-endian
|
||||
Format_S24LE = "s24le"
|
||||
// PCM signed 24-bit little-endian
|
||||
Mux_Format_S24LE = Format_S24LE
|
||||
|
||||
// PCM signed 32-bit big-endian
|
||||
Format_S32BE = "s32be"
|
||||
// PCM signed 32-bit big-endian
|
||||
Mux_Format_S32BE = Format_S32BE
|
||||
|
||||
// PCM signed 32-bit little-endian
|
||||
Format_S32LE = "s32le"
|
||||
// PCM signed 32-bit little-endian
|
||||
Mux_Format_S32LE = Format_S32LE
|
||||
|
||||
// PCM signed 8-bit
|
||||
Format_S8 = "s8"
|
||||
// PCM signed 8-bit
|
||||
Mux_Format_S8 = Format_S8
|
||||
|
||||
// SAP output
|
||||
Format_SAP = "sap"
|
||||
// SAP output
|
||||
Mux_Format_SAP = Format_SAP
|
||||
|
||||
// raw SBC
|
||||
Format_SBC = "sbc"
|
||||
// raw SBC
|
||||
Mux_Format_SBC = Format_SBC
|
||||
|
||||
// Scenarist Closed Captions
|
||||
Format_SCC = "scc"
|
||||
// Scenarist Closed Captions
|
||||
Mux_Format_SCC = Format_SCC
|
||||
|
||||
// SDL2 output device
|
||||
Format_SDL = "sdl"
|
||||
// SDL2 output device
|
||||
Mux_Format_SDL = Format_SDL
|
||||
|
||||
// SDL2 output device
|
||||
Format_SDL2 = "sdl2"
|
||||
// SDL2 output device
|
||||
Mux_Format_SDL2 = Format_SDL2
|
||||
|
||||
// segment
|
||||
Format_SEGMENT = "segment"
|
||||
// segment
|
||||
Mux_Format_SEGMENT = Format_SEGMENT
|
||||
|
||||
// JPEG single image
|
||||
Format_SINGLEJPEG = "singlejpeg"
|
||||
// JPEG single image
|
||||
Mux_Format_SINGLEJPEG = Format_SINGLEJPEG
|
||||
|
||||
// Loki SDL MJPEG
|
||||
Format_SMJPEG = "smjpeg"
|
||||
// Loki SDL MJPEG
|
||||
Mux_Format_SMJPEG = Format_SMJPEG
|
||||
|
||||
// Smooth Streaming Muxer
|
||||
Format_SMOOTHSTREAMING = "smoothstreaming"
|
||||
// Smooth Streaming Muxer
|
||||
Mux_Format_SMOOTHSTREAMING = Format_SMOOTHSTREAMING
|
||||
|
||||
// SoX native
|
||||
Format_SOX = "sox"
|
||||
// SoX native
|
||||
Mux_Format_SOX = Format_SOX
|
||||
|
||||
// IEC 61937 (used on S/PDIF - IEC958)
|
||||
Format_SPDIF = "spdif"
|
||||
// IEC 61937 (used on S/PDIF - IEC958)
|
||||
Mux_Format_SPDIF = Format_SPDIF
|
||||
|
||||
// Ogg Speex
|
||||
Format_SPX = "spx"
|
||||
// Ogg Speex
|
||||
Mux_Format_SPX = Format_SPX
|
||||
|
||||
// SubRip subtitle
|
||||
Format_SRT = "srt"
|
||||
// SubRip subtitle
|
||||
Mux_Format_SRT = Format_SRT
|
||||
|
||||
// streaming segment muxer
|
||||
Format_STREAM_SEGMENT = "stream_segment"
|
||||
// streaming segment muxer
|
||||
Mux_Format_STREAM_SEGMENT = Format_STREAM_SEGMENT
|
||||
|
||||
// streaming segment muxer
|
||||
Format_SSEGMENT = "ssegment"
|
||||
// streaming segment muxer
|
||||
Mux_Format_SSEGMENT = Format_SSEGMENT
|
||||
|
||||
// raw HDMV Presentation Graphic Stream subtitles
|
||||
Format_SUP = "sup"
|
||||
// raw HDMV Presentation Graphic Stream subtitles
|
||||
Mux_Format_SUP = Format_SUP
|
||||
|
||||
// MPEG-2 PS (SVCD)
|
||||
Format_SVCD = "svcd"
|
||||
// MPEG-2 PS (SVCD)
|
||||
Mux_Format_SVCD = Format_SVCD
|
||||
|
||||
// SWF (ShockWave Flash)
|
||||
Format_SWF = "swf"
|
||||
// SWF (ShockWave Flash)
|
||||
Mux_Format_SWF = Format_SWF
|
||||
|
||||
// Multiple muxer tee
|
||||
Format_TEE = "tee"
|
||||
// Multiple muxer tee
|
||||
Mux_Format_TEE = Format_TEE
|
||||
|
||||
// raw TrueHD
|
||||
Format_TRUEHD = "truehd"
|
||||
// raw TrueHD
|
||||
Mux_Format_TRUEHD = Format_TRUEHD
|
||||
|
||||
// TTA (True Audio)
|
||||
Format_TTA = "tta"
|
||||
// TTA (True Audio)
|
||||
Mux_Format_TTA = Format_TTA
|
||||
|
||||
// PCM unsigned 16-bit big-endian
|
||||
Format_U16BE = "u16be"
|
||||
// PCM unsigned 16-bit big-endian
|
||||
Mux_Format_U16BE = Format_U16BE
|
||||
|
||||
// PCM unsigned 16-bit little-endian
|
||||
Format_U16LE = "u16le"
|
||||
// PCM unsigned 16-bit little-endian
|
||||
Mux_Format_U16LE = Format_U16LE
|
||||
|
||||
// PCM unsigned 24-bit big-endian
|
||||
Format_U24BE = "u24be"
|
||||
// PCM unsigned 24-bit big-endian
|
||||
Mux_Format_U24BE = Format_U24BE
|
||||
|
||||
// PCM unsigned 24-bit little-endian
|
||||
Format_U24LE = "u24le"
|
||||
// PCM unsigned 24-bit little-endian
|
||||
Mux_Format_U24LE = Format_U24LE
|
||||
|
||||
// PCM unsigned 32-bit big-endian
|
||||
Format_U32BE = "u32be"
|
||||
// PCM unsigned 32-bit big-endian
|
||||
Mux_Format_U32BE = Format_U32BE
|
||||
|
||||
// PCM unsigned 32-bit little-endian
|
||||
Format_U32LE = "u32le"
|
||||
// PCM unsigned 32-bit little-endian
|
||||
Mux_Format_U32LE = Format_U32LE
|
||||
|
||||
// PCM unsigned 8-bit
|
||||
Format_U8 = "u8"
|
||||
// PCM unsigned 8-bit
|
||||
Mux_Format_U8 = Format_U8
|
||||
|
||||
// uncoded framecrc testing
|
||||
Format_UNCODEDFRAMECRC = "uncodedframecrc"
|
||||
// uncoded framecrc testing
|
||||
Mux_Format_UNCODEDFRAMECRC = Format_UNCODEDFRAMECRC
|
||||
|
||||
// Video4Linux2 output device
|
||||
Format_V4L2 = "v4l2"
|
||||
// Video4Linux2 output device
|
||||
Mux_Format_V4L2 = Format_V4L2
|
||||
|
||||
// raw VC-1 video
|
||||
Format_VC1 = "vc1"
|
||||
// raw VC-1 video
|
||||
Mux_Format_VC1 = Format_VC1
|
||||
|
||||
// VC-1 test bitstream
|
||||
Format_VC1TEST = "vc1test"
|
||||
// VC-1 test bitstream
|
||||
Mux_Format_VC1TEST = Format_VC1TEST
|
||||
|
||||
// MPEG-1 Systems / MPEG program stream (VCD)
|
||||
Format_VCD = "vcd"
|
||||
// MPEG-1 Systems / MPEG program stream (VCD)
|
||||
Mux_Format_VCD = Format_VCD
|
||||
|
||||
// MPEG-2 PS (VOB)
|
||||
Format_VOB = "vob"
|
||||
// MPEG-2 PS (VOB)
|
||||
Mux_Format_VOB = Format_VOB
|
||||
|
||||
// Creative Voice
|
||||
Format_VOC = "voc"
|
||||
// Creative Voice
|
||||
Mux_Format_VOC = Format_VOC
|
||||
|
||||
// Sony Wave64
|
||||
Format_W64 = "w64"
|
||||
// Sony Wave64
|
||||
Mux_Format_W64 = Format_W64
|
||||
|
||||
// WAV / WAVE (Waveform Audio)
|
||||
Format_WAV = "wav"
|
||||
// WAV / WAVE (Waveform Audio)
|
||||
Mux_Format_WAV = Format_WAV
|
||||
|
||||
// WebM
|
||||
Format_WEBM = "webm"
|
||||
// WebM
|
||||
Mux_Format_WEBM = Format_WEBM
|
||||
|
||||
// WebM Chunk Muxer
|
||||
Format_WEBM_CHUNK = "webm_chunk"
|
||||
// WebM Chunk Muxer
|
||||
Mux_Format_WEBM_CHUNK = Format_WEBM_CHUNK
|
||||
|
||||
// WebM DASH Manifest
|
||||
Format_WEBM_DASH_MANIFEST = "webm_dash_manifest"
|
||||
// WebM DASH Manifest
|
||||
Mux_Format_WEBM_DASH_MANIFEST = Format_WEBM_DASH_MANIFEST
|
||||
|
||||
// WebP
|
||||
Format_WEBP = "webp"
|
||||
// WebP
|
||||
Mux_Format_WEBP = Format_WEBP
|
||||
|
||||
// WebVTT subtitle
|
||||
Format_WEBVTT = "webvtt"
|
||||
// WebVTT subtitle
|
||||
Mux_Format_WEBVTT = Format_WEBVTT
|
||||
|
||||
// Windows Television (WTV)
|
||||
Format_WTV = "wtv"
|
||||
// Windows Television (WTV)
|
||||
Mux_Format_WTV = Format_WTV
|
||||
|
||||
// raw WavPack
|
||||
Format_WV = "wv"
|
||||
// raw WavPack
|
||||
Mux_Format_WV = Format_WV
|
||||
|
||||
// XV (XVideo) output device
|
||||
Format_XV = "xv"
|
||||
// XV (XVideo) output device
|
||||
Mux_Format_XV = Format_XV
|
||||
|
||||
// YUV4MPEG pipe
|
||||
Format_YUV4MPEGPIPE = "yuv4mpegpipe"
|
||||
// YUV4MPEG pipe
|
||||
Mux_Format_YUV4MPEGPIPE = Format_YUV4MPEGPIPE
|
||||
)
|
|
@ -0,0 +1,86 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func must(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd := exec.Command("ffmpeg", "-muxers")
|
||||
outputBytes, err := cmd.Output()
|
||||
must(err)
|
||||
|
||||
code := `package formats
|
||||
|
||||
var (
|
||||
`
|
||||
|
||||
bufreader := bufio.NewScanner(bytes.NewReader(outputBytes))
|
||||
|
||||
/*File formats:
|
||||
D. = Demuxing supported
|
||||
.E = Muxing supported
|
||||
--
|
||||
E 3g2 3GP2 (3GPP2 file format)
|
||||
E 3gp 3GP (3GPP file format)*/
|
||||
|
||||
waitingForList := true
|
||||
for bufreader.Scan() && waitingForList {
|
||||
text := bufreader.Text()
|
||||
if text == " --" {
|
||||
waitingForList = false
|
||||
}
|
||||
}
|
||||
|
||||
for bufreader.Scan() {
|
||||
text := bufreader.Text()
|
||||
demuxingSupported := text[1] == 'D'
|
||||
muxingSupported := text[2] == 'E'
|
||||
text = text[4:]
|
||||
cols := strings.Fields(text)
|
||||
ids := strings.Split(cols[0], ",")
|
||||
name := strings.Join(cols[1:], " ")
|
||||
for _, id := range ids {
|
||||
code += fmt.Sprintf(`
|
||||
// %s
|
||||
Format_%s = %q
|
||||
`, name, strings.ToUpper(id), id)
|
||||
if demuxingSupported {
|
||||
code += fmt.Sprintf(` // %s
|
||||
Demux_Format_%s = Format_%s
|
||||
`, name, strings.ToUpper(id), strings.ToUpper(id))
|
||||
}
|
||||
if muxingSupported {
|
||||
code += fmt.Sprintf(` // %s
|
||||
Mux_Format_%s = Format_%s
|
||||
`, name, strings.ToUpper(id), strings.ToUpper(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code += `
|
||||
)
|
||||
`
|
||||
|
||||
codeBytes, err := format.Source([]byte(code))
|
||||
must(err)
|
||||
|
||||
f, err := os.Create("gen_formats.go")
|
||||
must(err)
|
||||
defer f.Close()
|
||||
_, err = f.Write(codeBytes)
|
||||
must(err)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package mux
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"git.icedream.tech/icedream/uplink/app/media"
|
||||
)
|
||||
|
||||
type Muxer interface {
|
||||
AcceptsCodec(info media.MediaStreamCodecInfo) bool
|
||||
GeneratesFormat(info media.MediaStreamContainerInfo) bool
|
||||
|
||||
NewInstance(streams ...*media.MediaStream) <-MuxerInstance
|
||||
}
|
||||
|
||||
type MuxerInstance interface {
|
||||
Error() error
|
||||
Container() *media.MediaStreamContainerInfo
|
||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
func Test_Muxer(t *testing.T) {
|
||||
Convey("Muxer", t, func() {
|
||||
Convey("audio-only", func() {
|
||||
reader, _ := os.Open("mpthreetest.mp3")
|
||||
reader, _ := os.Open(filepath.Join("testassets", "mpthreetest.mp3"))
|
||||
defer reader.Close()
|
||||
|
||||
demuxer := Demux(reader)
|
||||
|
@ -27,7 +28,7 @@ func Test_Muxer(t *testing.T) {
|
|||
So(audioStream, ShouldBeNil)
|
||||
So(stream.StreamId, ShouldEqual, 0)
|
||||
So(stream.Pts, ShouldEqual, 0)
|
||||
So(stream.CodecInfo.CodecName, ShouldEqual, "mp3")
|
||||
So(stream.CodecInfo.CodecName, ShouldBeIn, []string{"mp3", "mp3float"})
|
||||
So(stream.CodecInfo.Type, ShouldEqual, Audio)
|
||||
audioStream = stream
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package transcode
|
||||
|
||||
import "git.icedream.tech/icedream/uplink/app/genericapi"
|
||||
|
||||
type Transcoder interface {
|
||||
genericapi.DynamicallyConfigurable
|
||||
|
||||
AcceptedInputCodecs() []string
|
||||
AcceptedOutputCodecs() []string
|
||||
|
||||
NewInstance(config interface{}) <-TranscoderInstance
|
||||
}
|
|
@ -9,12 +9,17 @@ import (
|
|||
"git.icedream.tech/icedream/uplink/plugins"
|
||||
)
|
||||
|
||||
type registeredPlugin struct {
|
||||
Instance plugins.PluginInstance
|
||||
Descriptor plugins.PluginDescriptor
|
||||
}
|
||||
|
||||
type App struct {
|
||||
Server *httpserver.Server
|
||||
Authenticator authentication.Authenticator
|
||||
ChannelManager *channels.ChannelManager
|
||||
|
||||
plugins []plugins.PluginInstance
|
||||
plugins []registeredPlugin
|
||||
}
|
||||
|
||||
func New() *App {
|
||||
|
@ -23,7 +28,7 @@ func New() *App {
|
|||
Authenticator: new(authentication.DummyAuthenticator),
|
||||
ChannelManager: channels.NewChannelManager(),
|
||||
|
||||
plugins: []plugins.PluginInstance{},
|
||||
plugins: []registeredPlugin{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,14 +45,18 @@ func (app *App) UsePlugin(plugin *plugins.Plugin) {
|
|||
p.SetAuthenticator(app.Authenticator)
|
||||
}
|
||||
|
||||
log.Println("Plugin initialized:", plugin.Descriptor.Name)
|
||||
log.Println("Plugin registered:", plugin.Descriptor.Name)
|
||||
|
||||
app.plugins = append(app.plugins, pluginInstance)
|
||||
app.plugins = append(app.plugins, registeredPlugin{
|
||||
Instance: pluginInstance,
|
||||
Descriptor: plugin.Descriptor,
|
||||
})
|
||||
}
|
||||
|
||||
func (app *App) Init() {
|
||||
for _, plugin := range app.plugins {
|
||||
plugin.Init()
|
||||
plugin.Instance.Init()
|
||||
log.Println("Plugin initialized:", plugin.Descriptor.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ func NewServer() *Server {
|
|||
Router: gin.New(),
|
||||
}
|
||||
|
||||
server.Router.Use(gin.Logger())
|
||||
server.Router.Use(gin.Recovery())
|
||||
|
||||
server.Http.Handler = server.Router
|
||||
server.Http.Addr = ":8000"
|
||||
|
||||
|
|
|
@ -39,22 +39,23 @@ func (mi *MetadataInjector) writeMetadata() (n int, err error) {
|
|||
|
||||
func (mi *MetadataInjector) Write(data []byte) (n int, err error) {
|
||||
for n < len(data) {
|
||||
restLen := len(data) - n
|
||||
toWrite := mi.MetadataInterval - mi.offset
|
||||
if toWrite > restLen {
|
||||
toWrite = restLen
|
||||
}
|
||||
|
||||
if toWrite <= 0 {
|
||||
_, cerr := mi.writeMetadata()
|
||||
//n += cn
|
||||
if cerr != nil {
|
||||
err = cerr
|
||||
_, err = mi.writeMetadata()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mi.offset = 0
|
||||
// toWrite = mi.MetadataInterval
|
||||
continue
|
||||
}
|
||||
|
||||
outBytes := make([]byte, toWrite)
|
||||
copy(outBytes, data[mi.offset:mi.offset+toWrite])
|
||||
copy(outBytes, data[n:n+toWrite])
|
||||
cn, cerr := mi.Writer.Write(outBytes)
|
||||
n += cn
|
||||
mi.offset += cn
|
||||
|
|
3
main.go
3
main.go
|
@ -4,6 +4,7 @@ import (
|
|||
"log"
|
||||
|
||||
"git.icedream.tech/icedream/uplink/app"
|
||||
"git.icedream.tech/icedream/uplink/plugins/icecast/input"
|
||||
"git.icedream.tech/icedream/uplink/plugins/icecast/output"
|
||||
"git.icedream.tech/icedream/uplink/plugins/test/sine"
|
||||
)
|
||||
|
@ -16,7 +17,7 @@ func main() {
|
|||
|
||||
func run() (err error) {
|
||||
backend := app.New()
|
||||
// backend.UsePlugin(icecast_input.Plugin)
|
||||
backend.UsePlugin(icecast_input.Plugin)
|
||||
backend.UsePlugin(icecast_output.Plugin)
|
||||
backend.UsePlugin(sine.Plugin)
|
||||
backend.Init()
|
||||
|
|
|
@ -2,13 +2,24 @@ package icecast_input
|
|||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"git.icedream.tech/icedream/uplink/app/authentication"
|
||||
"git.icedream.tech/icedream/uplink/app/channels"
|
||||
"git.icedream.tech/icedream/uplink/app/servers/http"
|
||||
"git.icedream.tech/icedream/uplink/app/streams"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var allowedCopyHeaders = []string{
|
||||
"icy-br",
|
||||
"icy-name",
|
||||
"icy-description",
|
||||
"icy-pub",
|
||||
"icy-url",
|
||||
"icy-genre",
|
||||
}
|
||||
|
||||
type pluginInstance struct {
|
||||
server *httpserver.Server
|
||||
authenticator authentication.Authenticator
|
||||
|
@ -25,7 +36,9 @@ func (instance *pluginInstance) SetChannelManager(channelManager *channels.Chann
|
|||
|
||||
func (instance *pluginInstance) SetServer(server *httpserver.Server) {
|
||||
instance.server = server
|
||||
}
|
||||
|
||||
func (instance *pluginInstance) Init() {
|
||||
router := instance.server.Router
|
||||
|
||||
router.PUT("/:channel", func(ctx *gin.Context) {
|
||||
|
@ -43,6 +56,36 @@ func (instance *pluginInstance) SetServer(server *httpserver.Server) {
|
|||
ctx.Status(401)
|
||||
return
|
||||
}
|
||||
io.Copy(channel.InputStream, ctx.Request.Body)
|
||||
|
||||
var sr io.Reader = ctx.Request.Body
|
||||
defer ctx.Request.Body.Close()
|
||||
if ctx.GetHeader("icy-metadata") == "1" {
|
||||
metaInt64, err := strconv.ParseInt(ctx.GetHeader("icy-metaint"), 10, 32)
|
||||
if err != nil {
|
||||
ctx.Status(400)
|
||||
return
|
||||
}
|
||||
metaInt := int(metaInt64)
|
||||
|
||||
// Client is sending metadata!
|
||||
mr := streams.NewMetadataExtractor(sr, metaInt)
|
||||
sr = mr
|
||||
|
||||
metadataChan := channel.Metadata()
|
||||
defer func() { metadataChan <- nil }()
|
||||
go func() {
|
||||
for metadata := range metadataChan {
|
||||
metadataToWrite := streams.Metadata{}
|
||||
if value, ok := metadata["StreamTitle"]; ok {
|
||||
metadataToWrite["StreamTitle"] = value
|
||||
}
|
||||
channel.SetMetadata(metadataToWrite)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
input := channel.AddInputStream("icecast")
|
||||
|
||||
io.Copy(input, sr)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
package icecast_output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"git.icedream.tech/icedream/uplink/app/authentication"
|
||||
"git.icedream.tech/icedream/uplink/app/channels"
|
||||
"git.icedream.tech/icedream/uplink/app/media"
|
||||
"git.icedream.tech/icedream/uplink/app/servers/http"
|
||||
"git.icedream.tech/icedream/uplink/app/streams"
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glycerine/rbuf"
|
||||
)
|
||||
|
||||
type pluginInstance struct {
|
||||
server *httpserver.Server
|
||||
authenticator authentication.Authenticator
|
||||
channelManager *channels.ChannelManager
|
||||
ringBuffers map[string]map[string]*rbuf.FixedSizeRingBuf
|
||||
}
|
||||
|
||||
func (instance *pluginInstance) SetAuthenticator(authenticator authentication.Authenticator) {
|
||||
|
@ -31,33 +25,6 @@ func (instance *pluginInstance) SetAuthenticator(authenticator authentication.Au
|
|||
func (instance *pluginInstance) SetChannelManager(channelManager *channels.ChannelManager) {
|
||||
instance.channelManager = channelManager
|
||||
|
||||
go func() {
|
||||
channelC := channelManager.Events().Sub("open")
|
||||
log.Println("Burst cache: Now watching")
|
||||
for c := range channelC {
|
||||
channelId := c.(string)
|
||||
go func(channel *channels.Channel) {
|
||||
streamRbufMap := map[string]*rbuf.FixedSizeRingBuf{}
|
||||
instance.ringBuffers[channelId] = streamRbufMap
|
||||
outputContainerC := channel.Events.Sub("output_container")
|
||||
log.Println("Burst cache: Now watching channel", channelId)
|
||||
for c := range outputContainerC {
|
||||
containerId := c.(string)
|
||||
burstCache := rbuf.NewFixedSizeRingBuf(64 * 1024)
|
||||
streamRbufMap[containerId] = burstCache
|
||||
go func(container *media.MediaStreamContainer) {
|
||||
r := container.Sub()
|
||||
log.Println("Burst cache: Now watching container", containerId, "in channel", channelId)
|
||||
io.Copy(burstCache, r)
|
||||
}(channel.OutputContainers[containerId])
|
||||
runtime.Gosched()
|
||||
}
|
||||
}(channelManager.Channel(channelId))
|
||||
runtime.Gosched()
|
||||
}
|
||||
}()
|
||||
runtime.Gosched()
|
||||
|
||||
// TODO - handle channel and container closure
|
||||
}
|
||||
|
||||
|
@ -66,8 +33,6 @@ func (instance *pluginInstance) SetServer(server *httpserver.Server) {
|
|||
}
|
||||
|
||||
func (instance *pluginInstance) Init() {
|
||||
instance.ringBuffers = map[string]map[string]*rbuf.FixedSizeRingBuf{}
|
||||
|
||||
router := instance.server.Router
|
||||
|
||||
router.GET("/:channel/:container", func(ctx *gin.Context) {
|
||||
|
@ -95,6 +60,13 @@ func (instance *pluginInstance) Init() {
|
|||
ctx.Writer.Header().Set("icy-metadata", "1")
|
||||
ctx.Writer.Header().Set("icy-metaint", fmt.Sprintf("%d", metaInt))
|
||||
}
|
||||
ctx.Writer.Header().Set("icy-name", "Channel name") // TODO
|
||||
ctx.Writer.Header().Set("icy-pub", "0") // TODO
|
||||
ctx.Writer.Header().Set("Server", "Uplink/0.0.0; Icecast 2.4.0 compatible")
|
||||
ctx.Writer.Header().Set("Cache-Control", "no-cache, no-store")
|
||||
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type")
|
||||
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS, HEAD")
|
||||
ctx.Writer.WriteHeader(200)
|
||||
|
||||
w := ctx.Writer
|
||||
|
@ -103,32 +75,26 @@ func (instance *pluginInstance) Init() {
|
|||
sr := container.Sub()
|
||||
defer sr.Close()
|
||||
|
||||
log.Println("Someone tuned in to", channelId, channel)
|
||||
|
||||
if sendMetadata {
|
||||
mw = streams.NewMetadataInjector(w, metaInt)
|
||||
nw = mw
|
||||
}
|
||||
|
||||
if channelRbuf, ok := instance.ringBuffers[channelId]; ok {
|
||||
if containerRbuf, ok := channelRbuf[containerId]; ok {
|
||||
burst := containerRbuf.Bytes()
|
||||
log.Println("Sending", humanize.Bytes(uint64(len(burst))), "burst")
|
||||
_, err := io.Copy(nw, bytes.NewReader(burst))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
metadataChan := channel.Metadata()
|
||||
defer func() { metadataChan <- nil }()
|
||||
go func() {
|
||||
for metadata := range metadataChan {
|
||||
metadataToWrite := streams.Metadata{}
|
||||
if value, ok := metadata["StreamTitle"]; ok {
|
||||
metadataToWrite["StreamTitle"] = value
|
||||
}
|
||||
mw.SetMetadata(metadataToWrite)
|
||||
}
|
||||
} else {
|
||||
log.Println("No burst cache for", channelId, "/", containerId)
|
||||
}
|
||||
} else {
|
||||
log.Println("No burst cache for", channelId)
|
||||
}()
|
||||
}
|
||||
|
||||
_, err := io.Copy(nw, sr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Println("copying stream to output failed:", err)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -22,8 +22,6 @@ func (instance *pluginInstance) Init() {
|
|||
channelManager := instance.channelManager
|
||||
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second) // give burst cache a chance to realize
|
||||
|
||||
c, err := channelManager.Open("sine")
|
||||
if err != nil {
|
||||
log.Println("ERROR: sine channel could not be opened:", err)
|
||||
|
|
|
@ -43,16 +43,11 @@ func (stream *SineStream) Read(data []byte) (n int, err error) {
|
|||
targetTime := stream.Timestamp.
|
||||
Add(time.Duration(float64(time.Second) * float64(stream.State) / float64(stream.Samplerate)))
|
||||
delay := targetTime.Sub(time.Now())
|
||||
/*log.Println("state", stream.State, "value", sampleValue, "time", targetTime, "delay", delay)
|
||||
time.Sleep(time.Second)*/
|
||||
|
||||
if delay > 0 {
|
||||
<-time.After(delay)
|
||||
}
|
||||
|
||||
/*if stream.State%uint64(stream.Samplerate) == 0 {
|
||||
log.Println("state", stream.State, "value", sampleValue, "time", targetTime, "delay", delay)
|
||||
}*/
|
||||
|
||||
stream.State++
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit f158770a793e28c093dc22c2a9ef0af20bdd804f
|
||||
Subproject commit d481e19a7c9f8d849bb3f6a69854ec711dc894e8
|
Loading…
Reference in New Issue