Skip to content

Instantly share code, notes, and snippets.

@j0sh

j0sh/Makefile Secret

Last active October 18, 2022 09:18
Show Gist options
  • Save j0sh/ffe816e4bca5dd8be92803c597efd8bd to your computer and use it in GitHub Desktop.
Save j0sh/ffe816e4bca5dd8be92803c597efd8bd to your computer and use it in GitHub Desktop.
Livepeer Sample Code

ffgopeg sample: stream copy transmuxer

Incomplete; requires further work on packet handling bindings.

To Build:

git clone https://gist.github.com/j0sh/ffe816e4bca5dd8be92803c597efd8bd
cd ffe816e4bca5dd8be92803c597efd8bd
go get -u gopkg.in/targodan/ffgopeg.v1
pushd $GOPATH/src/gopkg.in/targodan/ffgopeg.v1/
git remote add j0sh https://github.com/j0sh/ffgopeg
git checkout -b livepeer j0sh/livepeer
popd
make
./transmux rtmp://server/app/stream out.m3u8

Using RTMP as an input is actually untested but should work. Famous last words.

package main
import "log"
import "os"
import "gopkg.in/targodan/ffgopeg.v1/avformat"
func usage() {
log.Fatalf("Usage: %s <infile>\n", os.Args[0])
return
}
func main() {
if len(os.Args) < 2 {
usage()
return
}
filename := os.Args[1]
avformat.RegisterAll()
formatCtx, code := avformat.OpenInput(filename, nil, nil)
if ! code.Ok() {
log.Fatal("Error: Couldn't open file.")
return
}
defer formatCtx.Close();
code = formatCtx.FindStreamInfo(nil)
if ! code.Ok() {
log.Fatal("Error: Couldn't find stream information.")
return
}
}
PROGS=hellow transmux
all: $(PROGS)
hellow: hellow.go
go build $^
transmux: transmux.go
go build $^
clean:
go clean
// Transmuxer (incomplete) -- needs proper packet handling (timestamps, etc)
package main
import "log"
import "os"
import "gopkg.in/targodan/ffgopeg.v1/avformat"
import "gopkg.in/targodan/ffgopeg.v1/avcodec"
import "gopkg.in/targodan/ffgopeg.v1/avutil"
import "unsafe"
func usage() {
log.Fatalf("Usage: %s <infile> <outfile>\n", os.Args[0])
return
}
func main() {
if len(os.Args) < 3 {
usage()
return
}
ifile := os.Args[1]
ofile := os.Args[2]
//avutil.LogSetLevel(999)
avformat.RegisterAll()
inCtx, code := avformat.OpenInput(ifile, nil, nil)
if ! code.Ok() {
log.Fatal("Error: Couldn't open file.")
return
}
defer inCtx.Close()
code = inCtx.FindStreamInfo(nil)
log.Printf("Opened %s", ifile)
if ! code.Ok() {
log.Fatal("Error: Couldn't find stream information.")
return
}
// set up output format
outCtx, code := avformat.NewOutputFormatContext(nil, nil, &ofile)
if !code.Ok() {
log.Fatal("Error: Couldn't set up output format");
return
}
defer outCtx.Close()
smap := make([]int, inCtx.NbStreams())
for i := uint(0); i < inCtx.NbStreams(); i++ {
istream := inCtx.Streams()[i]
iparams := istream.CodecPar();
if iparams.CodecType() != avutil.AVMEDIA_TYPE_VIDEO &&
iparams.CodecType() != avutil.AVMEDIA_TYPE_AUDIO {
continue
}
ostream := outCtx.NewStream(nil)
iparams.CopyParams(ostream.CodecPar())
smap[i] = ostream.Index();
log.Println("Added new stream")
}
// XXX implement aviobuf handling ?
log.Printf("out fname %s\n", outCtx.Filename())
int_code := outCtx.WriteHeader(nil) // XXX: convert to go-friendly ReturnCode
if int_code < 0 {
log.Fatal("Error: Couldn't write header");
return
}
// read input and write output
var packet avcodec.Packet
packet.Init()
for {
code = inCtx.ReadFrame(&packet)
if code.IsOneOf(avutil.AVERROR_EOF()) {
log.Println("Finished decoding.")
break
}
// XXX set packet stream index appropriately, rescale timestamps, etc
int_code = outCtx.InterleavedWriteFrame((*avformat.Packet)(unsafe.Pointer(&packet)))
if int_code < 0 { // XXX distinguish between EOF and other errors?
log.Println("Finished writing; error?")
}
}
outCtx.WriteTrailer()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment