diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b6f92bf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +kill.sh +build.sh +launch.sh +go/ +www/ +logs/ +low-latency-preview diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6e2e020 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.11.10 +WORKDIR $GOPATH/src/app +COPY . . +RUN mkdir -p www logs +RUN go get -d -v ./... +RUN go install -v ./... +EXPOSE 8080 +CMD ["app", "'./www'"] + diff --git a/LICENSE b/LICENSE deleted file mode 100644 index beb5f01..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 codingtmd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/buildEncoderAndServerUbuntu.sh b/build.sh old mode 100755 new mode 100644 similarity index 58% rename from buildEncoderAndServerUbuntu.sh rename to build.sh index db72d6f..4b9832c --- a/buildEncoderAndServerUbuntu.sh +++ b/build.sh @@ -1,15 +1,7 @@ -#!/bin/bash - -# install go repo - sudo add-apt-repository -y ppa:longsleep/golang-backports - # update the OS - sudo apt-get -y update - sudo apt-get -y upgrade - sudo apt-get -y install git rm -r -f *zip* @@ -19,15 +11,6 @@ rm -r -f *tar* wget -O- https://dl.google.com/go/go1.11.1.linux-amd64.tar.gz | tar xz go/bin/go get -d -v . - -go/bin/go build - -go/bin/go get -d -v . - go/bin/go build mkdir -p www logs - -mkdir -p ffmpeg -wget -O- "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" | tar xJ -C ffmpeg --strip-components 1 - diff --git a/buildEncoderAndServerMacOS.sh b/buildEncoderAndServerMacOS.sh deleted file mode 100755 index 2577117..0000000 --- a/buildEncoderAndServerMacOS.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -cd utils - -curl -O https://cdn.theunarchiver.com/downloads/unarMac.zip - -unzip unarMac.zip - -cd .. - -rm -r -f ffmpeg/ - -mkdir ffmpeg - -cd ffmpeg - -# NOTE: This link will need to be updated whenever ffmpeg creates a new binary release -wget https://evermeet.cx/ffmpeg/ffmpeg-4.3.1.7z - -../utils/unar ffmpeg-4.3.1.7z - -rm ffmpeg-4.3.1.7z - -cd .. - -rm -r -f *zip* - -rm -r -f *tar* - -wget https://golang.org/dl/go1.15.7.darwin-amd64.tar.gz - -tar xvzf go1.15.7.darwin-amd64.tar.gz - -rm go1.15.7.darwin-amd64.tar.gz - -go/bin/go mod init github.com/streamlinevideo/low-latency-preview - -go/bin/go get -d -v . - -go/bin/go build - -rm -r -f www/ - -mkdir -p www logs - -rm -r -f low-latency-preview-master diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3ec4ff8 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module streamline + +go 1.15 + +require ( + github.com/gorilla/mux v1.8.0 + github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect + github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e + github.com/lunixbochs/vtclean v1.0.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/rs/cors v1.7.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2ebb6f2 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e h1:FdDd7bdI6cjq5vaoYlK1mfQYfF9sF2VZw8VEZMsl5t8= +github.com/juju/loggo v0.0.0-20200526014432-9ce3a2e09b5e/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/handlers/dashplayhandle.go b/handlers/dashplayhandle.go index c64791d..2b7ec93 100644 --- a/handlers/dashplayhandle.go +++ b/handlers/dashplayhandle.go @@ -5,7 +5,7 @@ import ( "net/url" "path" - "github.com/streamlinevideo/low-latency-preview/utils" + "streamline/utils" ) type DashPlayHandler struct { @@ -25,10 +25,10 @@ func (l *DashPlayHandler) servePlayer(w http.ResponseWriter, req *http.Request) if req.URL.EscapedPath() == "/" { curFileURL = "/localhost/manifest.mpd" } else { - curFileURL = req.URL.EscapedPath()[len("/ldashplay"):] + curFileURL = req.URL.EscapedPath()[len("/dashplay"):] } - curFilePath := path.Join("ldash", curFileURL) + curFilePath := path.Join("dash", curFileURL) base, _ := url.Parse("http://" + req.Host) relativeUrl, _ := url.Parse(curFilePath) manifestUrl := base.ResolveReference(relativeUrl).String() @@ -613,7 +613,7 @@ func (l *DashPlayHandler) servePlayer(w http.ResponseWriter, req *http.Request)
- +
diff --git a/handlers/filedeletehandler.go b/handlers/filedeletehandlerdash.go similarity index 72% rename from handlers/filedeletehandler.go rename to handlers/filedeletehandlerdash.go index 6957b06..955734e 100644 --- a/handlers/filedeletehandler.go +++ b/handlers/filedeletehandlerdash.go @@ -6,15 +6,15 @@ import ( "path" "time" - "github.com/streamlinevideo/low-latency-preview/utils" + "streamline/utils" ) // UploadHandler handles for http delete -type FileDeleteHandler struct { +type FileDashDeleteHandler struct { BaseDir string } -func (d *FileDeleteHandler) isFileUploadingDone(file string) bool { +func (d *FileDashDeleteHandler) isFileUploadingDone(file string) bool { symlink := file + ".symlink" if _, err := os.Stat(symlink); err == nil { // exist, then segment uploading is not finished yet @@ -24,14 +24,14 @@ func (d *FileDeleteHandler) isFileUploadingDone(file string) bool { return true } -func (d *FileDeleteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (d *FileDashDeleteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { utils.GetUploadLogger().Infof("Received upload request\n") - curFileURL := req.URL.EscapedPath()[len("/ldash"):] + curFileURL := req.URL.EscapedPath()[len("/dash"):] curFilePath := path.Join(d.BaseDir, curFileURL) d.serveHTTPImpl(curFilePath, w, req) } -func (d *FileDeleteHandler) serveHTTPImpl(curFilePath string, w http.ResponseWriter, req *http.Request) { +func (d *FileDashDeleteHandler) serveHTTPImpl(curFilePath string, w http.ResponseWriter, req *http.Request) { // check file existing if _, err := os.Stat(curFilePath); err != nil { utils.GetDeleteLogger().Debugf("file %s not exists \n", curFilePath) diff --git a/handlers/filedeletehandlerhls.go b/handlers/filedeletehandlerhls.go new file mode 100644 index 0000000..cd26d04 --- /dev/null +++ b/handlers/filedeletehandlerhls.go @@ -0,0 +1,52 @@ +package handlers + +import ( + "net/http" + "os" + "path" + "time" + + "streamline/utils" +) + +// UploadHandler handles for http delete +type FileHLSDeleteHandler struct { + BaseDir string +} + +func (d *FileHLSDeleteHandler) isFileUploadingDone(file string) bool { + symlink := file + ".symlink" + if _, err := os.Stat(symlink); err == nil { + // exist, then segment uploading is not finished yet + return false + } + // not exist + return true +} + +func (d *FileHLSDeleteHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + utils.GetUploadLogger().Infof("Received upload request\n") + curFileURL := req.URL.EscapedPath()[len("/hls"):] + curFilePath := path.Join(d.BaseDir, curFileURL) + d.serveHTTPImpl(curFilePath, w, req) +} + +func (d *FileHLSDeleteHandler) serveHTTPImpl(curFilePath string, w http.ResponseWriter, req *http.Request) { + // check file existing + if _, err := os.Stat(curFilePath); err != nil { + utils.GetDeleteLogger().Debugf("file %s not exists \n", curFilePath) + return + } + + is_chunk_uploading_done := d.isFileUploadingDone(curFilePath) + if !is_chunk_uploading_done { // chunk uploading is not done yet + return + } + + if err := os.Remove(curFilePath); err != nil { + utils.GetDeleteLogger().Errorf("Failed to delete file %s with %v \n", curFilePath, err) + return + } + + utils.GetDeleteLogger().Debugf("file %s was deleted exists @ %v \n", curFilePath, time.Now().Format(time.RFC3339)) +} diff --git a/handlers/filedownloadhandler.go b/handlers/filedownloadhandlerdash.go similarity index 84% rename from handlers/filedownloadhandler.go rename to handlers/filedownloadhandlerdash.go index a877919..14a4a6d 100644 --- a/handlers/filedownloadhandler.go +++ b/handlers/filedownloadhandlerdash.go @@ -8,24 +8,24 @@ import ( "strings" "time" - "github.com/streamlinevideo/low-latency-preview/utils" + "streamline/utils" ) -type FileDownloadHandler struct { +type FileDashDownloadHandler struct { StartTime time.Time BaseDir string } -func (l *FileDownloadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (l *FileDashDownloadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { utils.GetDownloadLogger().Infof("Received download request\n") l.serveDownload(w, req) } -func (l *FileDownloadHandler) getSourcePath(req *http.Request) string { +func (l *FileDashDownloadHandler) getSourcePath(req *http.Request) string { return l.BaseDir } -func (l *FileDownloadHandler) isFileUploadingDone(file string) bool { +func (l *FileDashDownloadHandler) isFileUploadingDone(file string) bool { symlink := file + ".symlink" if _, err := os.Stat(symlink); err == nil { // exist, then segment uploading is not finished yet @@ -35,8 +35,8 @@ func (l *FileDownloadHandler) isFileUploadingDone(file string) bool { return true } -func (l *FileDownloadHandler) serveDownload(w http.ResponseWriter, req *http.Request) { - curFileURL := req.URL.EscapedPath()[len("/ldash"):] +func (l *FileDashDownloadHandler) serveDownload(w http.ResponseWriter, req *http.Request) { + curFileURL := req.URL.EscapedPath()[len("/dash"):] curFilePath := path.Join(l.getSourcePath(req), curFileURL) file, err := os.Open(curFilePath) // For read access. if err != nil { diff --git a/handlers/filedownloadhandlerhls.go b/handlers/filedownloadhandlerhls.go new file mode 100644 index 0000000..fb85306 --- /dev/null +++ b/handlers/filedownloadhandlerhls.go @@ -0,0 +1,105 @@ +package handlers + +import ( + "io" + "net/http" + "os" + "path" + "strings" + "time" + + "streamline/utils" +) + +type FileHLSDownloadHandler struct { + StartTime time.Time + BaseDir string +} + +func (l *FileHLSDownloadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + utils.GetDownloadLogger().Infof("Received download request\n") + l.serveDownload(w, req) +} + +func (l *FileHLSDownloadHandler) getSourcePath(req *http.Request) string { + return l.BaseDir +} + +func (l *FileHLSDownloadHandler) isFileUploadingDone(file string) bool { + symlink := file + ".symlink" + if _, err := os.Stat(symlink); err == nil { + // exist, then segment uploading is not finished yet + return false + } + // not exist + return true +} + +func (l *FileHLSDownloadHandler) serveDownload(w http.ResponseWriter, req *http.Request) { + curFileURL := req.URL.EscapedPath()[len("/lhls"):] + curFilePath := path.Join(l.getSourcePath(req), curFileURL) + file, err := os.Open(curFilePath) // For read access. + if err != nil { + utils.GetDownloadLogger().Errorf("Failed to open file: %v \n", err) + http.NotFound(w, req) + return + } + defer file.Close() + + utils.GetDownloadLogger().Debugf("file %s was requested @ %v \n", curFileURL, time.Now().Format(time.RFC3339)) + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Transfer-Encoding", "chunked") + w.Header().Set("Connection", "Keep-Alive") + + if strings.HasSuffix(curFilePath, ".m3u8") { + w.Header().Set("Content-Type", "application/x-mpegURL") + } else if strings.HasSuffix(curFilePath, ".mp4") || strings.HasSuffix(curFilePath, ".fmp4") { + w.Header().Set("Content-Type", "video/MP4") + } else { + w.Header().Set("Content-Type", "video/MP2T") + } + + w.WriteHeader(http.StatusOK) + + bufferSize := 20480 + buffer := make([]byte, bufferSize) + var read_err error + bytesread := 0 + for { + // start chunk transfer + for { + bytesread, read_err = file.Read(buffer) + if read_err != nil { + if read_err != io.EOF { // print out if read error + utils.GetDownloadLogger().Errorf("Failed to read file: %v \n", err) + panic(read_err) + } + } + + if bytesread > 0 { + utils.GetDownloadLogger().Debugf("%s read %d bytes \n", curFileURL, bytesread) + _, errpr := w.Write(buffer[:bytesread]) + if errpr != nil { + panic(errpr) + } + } + + if bytesread != bufferSize { + break + } + } + + if read_err != nil { + is_chunk_uploading_done := l.isFileUploadingDone(curFilePath) + if read_err == io.EOF && is_chunk_uploading_done { + // if read to end and uploading is done, time to close the downloading too + break + } + utils.GetDownloadLogger().Debugf("Read to end, but uploading is not finished yet: %v \n", err) + } + time.Sleep(50 * time.Millisecond) + } + utils.GetDownloadLogger().Debugf("file %s was downloaded @ %v \n", curFileURL, time.Now().Format(time.RFC3339)) + +} diff --git a/handlers/fileuploadhandler.go b/handlers/fileuploadhandlerdash.go similarity index 85% rename from handlers/fileuploadhandler.go rename to handlers/fileuploadhandlerdash.go index b29829f..fc1d847 100644 --- a/handlers/fileuploadhandler.go +++ b/handlers/fileuploadhandlerdash.go @@ -8,18 +8,18 @@ import ( "path" "time" - "github.com/streamlinevideo/low-latency-preview/utils" + "streamline/utils" "github.com/gorilla/mux" ) // UploadHandler handles for http upload -type FileUploadHandler struct { +type FileDashUploadHandler struct { BaseDir string } -func (u *FileUploadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { +func (u *FileDashUploadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { utils.GetUploadLogger().Infof("Received upload request\n") - curFileURL := req.URL.EscapedPath()[len("/ldash"):] + curFileURL := req.URL.EscapedPath()[len("/dash"):] vars := mux.Vars(req) folder := vars["folder"] curFolderPath := path.Join(u.BaseDir, folder) @@ -27,7 +27,7 @@ func (u *FileUploadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) u.serveHTTPImpl(curFolderPath, curFilePath, w, req) } -func (u *FileUploadHandler) serveHTTPImpl(curFolderPath string, curFilePath string, w http.ResponseWriter, req *http.Request) { +func (u *FileDashUploadHandler) serveHTTPImpl(curFolderPath string, curFilePath string, w http.ResponseWriter, req *http.Request) { if _, err := os.Stat(curFolderPath); os.IsNotExist(err) { err := os.MkdirAll(curFolderPath, os.ModePerm) if err != nil { diff --git a/handlers/fileuploadhandlerhls.go b/handlers/fileuploadhandlerhls.go new file mode 100644 index 0000000..8c097b2 --- /dev/null +++ b/handlers/fileuploadhandlerhls.go @@ -0,0 +1,75 @@ +package handlers + +import ( + "io" + "io/ioutil" + "net/http" + "os" + "path" + "time" + + "streamline/utils" + "github.com/gorilla/mux" +) + +// UploadHandler handles for http upload +type FileHLSUploadHandler struct { + BaseDir string +} + +func (u *FileHLSUploadHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + utils.GetUploadLogger().Infof("Received upload request\n") + curFileURL := req.URL.EscapedPath()[len("/hls"):] + vars := mux.Vars(req) + folder := vars["folder"] + curFolderPath := path.Join(u.BaseDir, folder) + curFilePath := path.Join(u.BaseDir, curFileURL) + u.serveHTTPImpl(curFolderPath, curFilePath, w, req) +} + +func (u *FileHLSUploadHandler) serveHTTPImpl(curFolderPath string, curFilePath string, w http.ResponseWriter, req *http.Request) { + if _, err := os.Stat(curFolderPath); os.IsNotExist(err) { + err := os.MkdirAll(curFolderPath, os.ModePerm) + if err != nil { + utils.GetUploadLogger().Infof("fail to create file %v", err) + } + } + + // rewrite, mostly for manifest file + if _, err := os.Stat(curFilePath); err == nil { + utils.GetUploadLogger().Debugf("rewrite file %s @ %v \n", curFilePath, time.Now().Format(time.RFC3339)) + data, _ := ioutil.ReadAll(req.Body) + err = ioutil.WriteFile(curFilePath, data, 0644) + if err != nil { + utils.GetUploadLogger().Errorf("fail to create file %v \n", err) + } + return + } + + // create, mostly for segment + // for segment, we will allow partial downloading during the uploading to save the time for player(this is what low latency meaning) + // So here uses Symlink as a signal to tell download handler whether the uploading is finished or not. + symlink := curFilePath + ".symlink" + os.Symlink(curFilePath, symlink) + utils.GetUploadLogger().Debugf("create symlink %s @ %v \n", symlink, time.Now().Format(time.RFC3339)) + + + f, rerr := os.Create(curFilePath) + if rerr != nil { + utils.GetUploadLogger().Errorf("fail to create file %s : %v\n", curFilePath, rerr) + return + } + + utils.GetUploadLogger().Debugf("create file %s @ %v \n", curFilePath, time.Now().Format(time.RFC3339)) + defer f.Close() + + _, rerr = io.Copy(f, req.Body) + if rerr != nil { + utils.GetUploadLogger().Errorf("fail to create file %v \n", rerr) + } + + // remove symlink once the uploading is done + os.Remove(symlink) + utils.GetUploadLogger().Debugf("remove symlink %s @ %v \n", symlink, time.Now().Format(time.RFC3339)) + +} diff --git a/killAll.sh b/killAll.sh deleted file mode 100755 index 91e73d9..0000000 --- a/killAll.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -pkill main -pkill ffmpeg - -echo All processes have been killed ☠ diff --git a/launchEncoderTestPattern.sh b/launchEncoderTestPattern.sh deleted file mode 100755 index 259d29b..0000000 --- a/launchEncoderTestPattern.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - TARGETSERVER="127.0.0.1" - echo "Target Server not specified, assuming ${TARGETSERVER}..." -else - TARGETSERVER="$1" -fi - -if [ -z "$2" ]; then - STREAMID="1234" - echo "Target Path not specified, assuming ${STREAMID}..." -else - STREAMID="$2" -fi - -echo Oh 💩 here we go! -echo View your stream at http://${TARGETSERVER}:8080/ldashplay/${STREAMID}/manifest.mpd - -# Encoding settings for x264 (CPU based encoder) - -x264enc='libx264 -tune zerolatency -profile:v high -preset veryfast -bf 0 -refs 3 -sc_threshold 0' - -ffmpeg/ffmpeg \ - -hide_banner \ - -re \ - -f lavfi \ - -i "testsrc2=size=1920x1080:rate=30" \ - -pix_fmt yuv420p \ - -map 0:v \ - -c:v ${x264enc} \ - -g 150 \ - -keyint_min 150 \ - -b:v 4000k \ - -vf "fps=30,drawtext=fontfile=utils/OpenSans-Bold.ttf:box=1:fontcolor=black:boxcolor=white:fontsize=100':x=40:y=400:textfile=utils/text.txt" \ - -method PUT \ - -seg_duration 5 \ - -streaming 1 \ - -http_persistent 1 \ - -utc_timing_url "https://time.akamai.com/?iso" \ - -index_correction 1 \ - -use_timeline 0 \ - -media_seg_name 'chunk-stream-$RepresentationID$-$Number%05d$.m4s' \ - -init_seg_name 'init-stream1-$RepresentationID$.m4s' \ - -window_size 5 \ - -extra_window_size 10 \ - -remove_at_exit 1 \ - -adaptation_sets "id=0,streams=v id=1,streams=a" \ - -f dash \ - http://${TARGETSERVER}:8080/ldash/${STREAMID}/manifest.mpd >/dev/null 2>logs/encode.log & diff --git a/launchEncoderWebCam.sh b/launchEncoderWebCam.sh deleted file mode 100755 index 5119a88..0000000 --- a/launchEncoderWebCam.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ]; then - TARGETSERVER="127.0.0.1" - echo "Target Server not specified, assuming ${TARGETSERVER}..." -else - TARGETSERVER="$1" -fi - -if [ -z "$2" ]; then - STREAMID="1234" - echo "Target Path not specified, assuming ${STREAMID}..." -else - STREAMID="$2" -fi - -echo Oh 💩 here we go! -echo View your stream at http://${TARGETSERVER}:8080/ldashplay/${STREAMID}/manifest.mpd - -input='-f lavfi -i testsrc2=size=1920x1080:rate=30 -pix_fmt yuv420p' - -if [ "$(uname)" == "Darwin" ]; then - # Use Apple Mac hardware encoder - echo Using hardware encoder - x264enc='h264_videotoolbox -profile:v main' - - #using Apple hardware webcam - input='-f avfoundation -video_size 2048x1536 -framerate 20 -i 1' -else - # Encoding settings for x264 (CPU based encoder) - echo Using software encoder - x264enc='libx264 -tune zerolatency -profile:v high -preset veryfast -bf 0 -refs 3 -sc_threshold 0' -fi - -ffmpeg/ffmpeg \ - -hide_banner \ - -re \ - ${input} \ - -map 0:v \ - -c:v ${x264enc} \ - -g 150 \ - -keyint_min 150 \ - -b:v 4000k \ - -vf "fps=30,drawtext=fontfile=utils/OpenSans-Bold.ttf:box=1:fontcolor=black:boxcolor=white:fontsize=100':x=40:y=500:textfile=utils/text.txt" \ - -method PUT \ - -seg_duration 5 \ - -streaming 1 \ - -http_persistent 1 \ - -utc_timing_url "https://time.akamai.com/?iso" \ - -index_correction 1 \ - -use_timeline 0 \ - -media_seg_name 'chunk-stream-$RepresentationID$-$Number%05d$.m4s' \ - -init_seg_name 'init-stream1-$RepresentationID$.m4s' \ - -window_size 5 \ - -extra_window_size 10 \ - -remove_at_exit 1 \ - -adaptation_sets "id=0,streams=v id=1,streams=a" \ - -f dash \ - http://${TARGETSERVER}:8080/ldash/${STREAMID}/manifest.mpd - diff --git a/launchServer.sh b/launchServer.sh deleted file mode 100755 index 76b39fa..0000000 --- a/launchServer.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -go/bin/go run main.go "./www" 2>logs/server.log & diff --git a/main.go b/main.go index 4d06d41..058d0a6 100644 --- a/main.go +++ b/main.go @@ -7,8 +7,8 @@ import ( "path/filepath" "time" - "github.com/streamlinevideo/low-latency-preview/handlers" - "github.com/streamlinevideo/low-latency-preview/utils" + "streamline/handlers" + "streamline/utils" "github.com/gorilla/mux" "github.com/rs/cors" ) @@ -31,12 +31,12 @@ func main() { // clean the segment folder utils.RemoveContents(args[0]) - file_downloadHandler := &handlers.FileDownloadHandler{ + file_dashdownloadHandler := &handlers.FileDashDownloadHandler{ StartTime: time.Now(), BaseDir: filePath, } - file_uploadHandler := &handlers.FileUploadHandler{ + file_dashuploadHandler := &handlers.FileDashUploadHandler{ BaseDir: filePath, } @@ -44,16 +44,32 @@ func main() { BaseDir: filePath, } - file_deleteHandler := &handlers.FileDeleteHandler{ + file_dashdeleteHandler := &handlers.FileDashDeleteHandler{ + BaseDir: filePath, + } + + file_hlsdownloadHandler := &handlers.FileHLSDownloadHandler{ + StartTime: time.Now(), + BaseDir: filePath, + } + + file_hlsuploadHandler := &handlers.FileHLSUploadHandler{ + BaseDir: filePath, + } + + file_hlsdeleteHandler := &handlers.FileHLSDeleteHandler{ BaseDir: filePath, } r := mux.NewRouter() - r.Handle("/ldash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_uploadHandler).Methods("PUT", "POST") - r.Handle("/ldash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_downloadHandler).Methods("GET") - r.Handle("/ldash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_deleteHandler).Methods("DELETE") - r.Handle("/ldashplay/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", dash_playHandler) + r.Handle("/dash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_dashuploadHandler).Methods("PUT", "POST") + r.Handle("/dash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_dashdownloadHandler).Methods("GET") + r.Handle("/dash/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_dashdeleteHandler).Methods("DELETE") + r.Handle("/dashplay/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", dash_playHandler) + r.Handle("/hls/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_hlsuploadHandler).Methods("PUT", "POST") + r.Handle("/hls/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_hlsdownloadHandler).Methods("GET") + r.Handle("/hls/{folder}/{name:[a-zA-Z0-9/_-]+}.{name:[a-zA-Z0-9/_-]+}", file_hlsdeleteHandler).Methods("DELETE") r.Handle("/", dash_playHandler) // KJSL: Adding CORS diff --git a/streamline.exe b/streamline.exe new file mode 100644 index 0000000..1233187 Binary files /dev/null and b/streamline.exe differ diff --git a/utils/OpenSans-Bold.ttf b/utils/OpenSans-Bold.ttf deleted file mode 100644 index fd79d43..0000000 Binary files a/utils/OpenSans-Bold.ttf and /dev/null differ diff --git a/utils/text.txt b/utils/text.txt deleted file mode 100644 index 25d34da..0000000 --- a/utils/text.txt +++ /dev/null @@ -1,2 +0,0 @@ -Encoder time -%{localtime:%M}:%{localtime:%S }:%{eif:1M*t-1K*trunc(t*1K):d}