Working on getting rtmp to youtube. Have a testing file to start with

This commit is contained in:
Unknown 2018-02-08 17:51:43 +10:30
parent c8c9c3f0d2
commit ed14ebb9b9
8 changed files with 129 additions and 58 deletions

View File

@ -29,8 +29,8 @@ LICENSE
package mpegts package mpegts
import ( import (
"bitbucket.org/ausocean/av/tools" //"bitbucket.org/ausocean/av/tools"
//"../tools" "../tools"
"errors" "errors"
//"fmt" //"fmt"

View File

@ -32,14 +32,14 @@ import (
_"fmt" _"fmt"
"os" "os"
"bitbucket.org/ausocean/av/mpegts" //"bitbucket.org/ausocean/av/mpegts"
"bitbucket.org/ausocean/av/rtp" //"bitbucket.org/ausocean/av/rtp"
"bitbucket.org/ausocean/av/tools" //"bitbucket.org/ausocean/av/tools"
"bitbucket.org/ausocean/av/itut" //"bitbucket.org/ausocean/av/itut"
//"../mpegts" "../mpegts"
//"../rtp" "../rtp"
//"../tools" "../tools"
//"../itut" "../itut"
) )
type RtpToH264Converter interface { type RtpToH264Converter interface {

View File

@ -28,8 +28,8 @@ LICENSE
package parser package parser
import ( import (
"bitbucket.org/ausocean/av/itut" //"bitbucket.org/ausocean/av/itut"
//"../itut" "../itut"
"log" "log"
"sync" "sync"
_"time" _"time"

View File

@ -27,8 +27,8 @@ LICENSE
package pes package pes
import ( import (
"bitbucket.org/ausocean/av/tools" //"bitbucket.org/ausocean/av/tools"
//"../tools" "../tools"
) )
const ( const (

View File

@ -42,15 +42,15 @@ import (
"strconv" "strconv"
"time" "time"
"bitbucket.org/ausocean/av/parser" //"bitbucket.org/ausocean/av/parser"
"bitbucket.org/ausocean/av/tsgenerator" //"bitbucket.org/ausocean/av/tsgenerator"
//"../parser" "../parser"
//"../tsgenerator" "../tsgenerator"
"bitbucket.org/ausocean/av/ringbuffer" //"bitbucket.org/ausocean/av/ringbuffer"
"bitbucket.org/ausocean/utils/smartLogger" //"bitbucket.org/ausocean/utils/smartLogger"
//"../../utils/smartLogger" "../../utils/smartLogger"
//"../ringbuffer" "../ringbuffer"
) )
// Misc constants // Misc constants
@ -65,6 +65,7 @@ const (
h264BufferSize = 1000000 h264BufferSize = 1000000
bitrateTime = 60 bitrateTime = 60
mjpegParserInChanLen = 100000 mjpegParserInChanLen = 100000
ffmpegPath = "/home/$USER/bin/ffmpeg"
) )
// Log Types // Log Types
@ -82,7 +83,7 @@ type Config struct {
InputCodec uint8 InputCodec uint8
Output uint8 Output uint8
RtmpEncodingMethod uint8 RtmpEncodingMethod uint8
RtmpURL string RtmpUrl string
Bitrate string Bitrate string
OutputFileName string OutputFileName string
InputFileName string InputFileName string
@ -148,7 +149,9 @@ type revidInst struct {
generator tsgenerator.TsGenerator generator tsgenerator.TsGenerator
parser parser.Parser parser parser.Parser
cmd *exec.Cmd cmd *exec.Cmd
ffmpegCmd *exec.Cmd
inputReader *bufio.Reader inputReader *bufio.Reader
ffmpegStdin io.WriteCloser
mjpegOutputChan chan []byte mjpegOutputChan chan []byte
} }
@ -253,10 +256,13 @@ func (r *revidInst) ChangeState(config Config) error {
switch config.RtmpEncodingMethod { switch config.RtmpEncodingMethod {
case Revid: case Revid:
case Ffmpeg: case Ffmpeg:
case NothingDefine: case NothingDefined:
r.Log(Warning, "No RTMP encoding method defined, defautling to ffmpeg!") r.Log(Warning, "No RTMP encoding method defined, defautling to ffmpeg!")
config.RtmpEncodingMethod = Ffmpeg config.RtmpEncodingMethod = Ffmpeg
} }
if config.RtmpUrl == "" {
return errors.New("Bad RTMP URL")
}
case NothingDefined: case NothingDefined:
r.Log(Warning, "No output defined, defaulting to httpOut!") r.Log(Warning, "No output defined, defaulting to httpOut!")
config.Output = HttpOut config.Output = HttpOut
@ -327,7 +333,6 @@ func (r *revidInst) ChangeState(config Config) error {
return errors.New("Bad quantization defined in config!") return errors.New("Bad quantization defined in config!")
} }
} }
r.config = config r.config = config
return nil return nil
} }
@ -356,7 +361,7 @@ func (r *revidInst) Start() {
return return
} }
r.Log(Info, "Starting Revid!") r.Log(Info, "Starting Revid!")
// Configure input
switch r.config.Input { switch r.config.Input {
case Raspivid: case Raspivid:
r.Log(Info, "Starting raspivid!") r.Log(Info, "Starting raspivid!")
@ -384,7 +389,6 @@ func (r *revidInst) Start() {
"-fps", r.config.FrameRate, "-fps", r.config.FrameRate,
) )
} }
stdout, _ := r.cmd.StdoutPipe() stdout, _ := r.cmd.StdoutPipe()
err := r.cmd.Start() err := r.cmd.Start()
r.inputReader = bufio.NewReader(stdout) r.inputReader = bufio.NewReader(stdout)
@ -407,9 +411,40 @@ func (r *revidInst) Start() {
r.Stop() r.Stop()
return return
} }
go func() = {
for i := range data { for i := range data {
r.parser.GetInputChan() <- data[i] r.parser.GetInputChan() <- data[i]
} }
}{}
}
// Configure output
switch r.config.Output {
case Rtmp:
r.ffmpegCmd = exec.Command(ffmpegPath,
"-f", "h264",
"-r", r.config.FrameRate,
"-i", "-",
"-itsoffset", "5.5",
"-fflags", "nobuffer",
"-vcodec", "copy",
"-strict", "experimental",
"-f", "flv",
r.config.RtmpUrl,
)
err := r.ffmpegCmd.Start()
if err != nil {
r.Log(Error, err.Error())
r.Stop()
return
}
r.ffmpegStdin, err = r.ffmpegCmd.StdinPipe()
if err != nil {
r.Log(Error, err.Error())
r.Stop()
return
}
} }
go r.readCamera() go r.readCamera()
go r.outputClips() go r.outputClips()
@ -474,8 +509,10 @@ func (r *revidInst) packClips() {
for { for {
switch r.config.Packetization { switch r.config.Packetization {
case None: case None:
frame := <-r.mjpegOutputChan frame := <-r.mjpegOutputChan
upperBound := clipSize + len(frame) upperBound := clipSize + len(frame)
fmt.Printf("clipSize: %v\n len(frame): %v\n", clipSize, len(frame))
copy(clip[clipSize:upperBound], frame) copy(clip[clipSize:upperBound], frame)
packetCount++ packetCount++
clipSize += len(frame) clipSize += len(frame)
@ -515,6 +552,7 @@ func (r *revidInst) outputClips() {
bytes := 0 bytes := 0
delay := 0 delay := 0
for r.isRunning { for r.isRunning {
// Here we slow things down as much as we can to decrease cpu usage
switch { switch {
case r.ringBuffer.GetNoOfElements() < 2: case r.ringBuffer.GetNoOfElements() < 2:
delay++ delay++
@ -522,12 +560,15 @@ func (r *revidInst) outputClips() {
case delay > 10: case delay > 10:
delay -= 10 delay -= 10
} }
// If the ringbuffer has something we can read and send off
if clip, err := r.ringBuffer.Read(); err == nil { if clip, err := r.ringBuffer.Read(); err == nil {
r.Log(Debug, fmt.Sprintf("Delay is: %v\n", delay)) r.Log(Debug, fmt.Sprintf("Delay is: %v\n", delay))
r.Log(Debug, fmt.Sprintf("Ring buffer size: %v\n", r.ringBuffer.GetNoOfElements())) r.Log(Debug, fmt.Sprintf("Ring buffer size: %v\n", r.ringBuffer.GetNoOfElements()))
// Output clip to the output specified in the configuration struct
switch r.config.Output { switch r.config.Output {
case File: case File:
fmt.Println("")
r.outputFile.Write(clip) r.outputFile.Write(clip)
case HttpOut: case HttpOut:
bytes += len(clip) bytes += len(clip)
@ -536,12 +577,19 @@ func (r *revidInst) outputClips() {
r.Log(Warning, "Post failed trying again!") r.Log(Warning, "Post failed trying again!")
err = r.sendClipToHTTP(clip, r.config.HttpAddress) err = r.sendClipToHTTP(clip, r.config.HttpAddress)
} }
case Rtmp:
_, err := r.ffmpegStdin.Write(clip)
if err != nil {
r.Log(Error, err.Error())
}
default: default:
r.Log(Error, "No output defined!") r.Log(Error, "No output defined!")
} }
if err := r.ringBuffer.DoneReading(); err != nil { if err := r.ringBuffer.DoneReading(); err != nil {
r.Log(Error, err.Error()) r.Log(Error, err.Error())
} }
// Log some information regarding bitrate and ring buffer size if it's time
now = time.Now() now = time.Now()
deltaTime := now.Sub(prevTime) deltaTime := now.Sub(prevTime)
if deltaTime > time.Duration(bitrateTime)*time.Second { if deltaTime > time.Duration(bitrateTime)*time.Second {

View File

@ -71,7 +71,7 @@ func TestRaspividH264Input(t *testing.T){
time.Sleep(100*time.Second) time.Sleep(100*time.Second)
revidInst.Stop() revidInst.Stop()
} }
*/
// Test revidInst with a raspivid mjpeg input // Test revidInst with a raspivid mjpeg input
func TestRaspividMJPEGInput(t *testing.T){ func TestRaspividMJPEGInput(t *testing.T){
@ -94,3 +94,26 @@ func TestRaspividMJPEGInput(t *testing.T){
time.Sleep(20*time.Second) time.Sleep(20*time.Second)
revidInst.Stop() revidInst.Stop()
} }
*/
// Test revidInst with rtmp output
func TestRtmpOutput(t *testing.T){
config := Config{
Input: File,
InputFileName: "testInput.h264",
InputCodec: H264,
Output: Rtmp,
RtmpUrl: "-",
Width: "1280",
Height: "720",
FrameRate: "25",
}
revidInst, err := NewRevidInstance(config)
if err != nil {
t.Errorf("Should not of have got an error!: %v\n", err.Error())
return
}
revidInst.Start()
time.Sleep(60*time.Second)
revidInst.Stop()
}

View File

@ -31,8 +31,8 @@ package tools
import ( import (
_"os" _"os"
_"fmt" _"fmt"
"bitbucket.org/ausocean/av/rtp" //"bitbucket.org/ausocean/av/rtp"
//"../rtp" "../rtp"
) )
func BoolToByte(in bool) (out byte) { func BoolToByte(in bool) (out byte) {

View File

@ -31,14 +31,14 @@ package tsgenerator
import ( import (
_"fmt" _"fmt"
_"os" _"os"
"bitbucket.org/ausocean/av/mpegts" //"bitbucket.org/ausocean/av/mpegts"
"bitbucket.org/ausocean/av/pes" //"bitbucket.org/ausocean/av/pes"
"bitbucket.org/ausocean/av/tools" //"bitbucket.org/ausocean/av/tools"
"bitbucket.org/ausocean/av/rtp" //"bitbucket.org/ausocean/av/rtp"
//"../mpegts" "../mpegts"
//"../pes" "../pes"
//"../tools" "../tools"
//"../rtp" "../rtp"
) )
var ( var (