HLS video handling/storage/state refactor (#151)

* WIP with new transcoder progress monitor

* A whole different WIP in progress monitoring via local PUTs

* Use an actual hls playlist parser to rewrite master playlist

* Cleanup

* Private vs public path for thumbnail generation

* Allow each storage provider to make decisions of how to store different types of files

* Simplify inbound file writes

* Revert

* Split out set stream as connected/disconnected state methods

* Update videojs

* Add comment about the hls handler

* Rework of the offline stream state.  For #85

* Delete old unreferenced video segment files from disk

* Cleanup all segments and revert to a completely offline state after 5min

* Stop thumbnail generation on stream stop. Copy logo to thumbnail on cleanup.

* Update transcoder test

* Add comment

* Return http 200 on success to transcoder. Tweak how files are written to disk

* Force pixel color format in transcoder

* Add debugging info for S3 transfers. Add default ACL.

* Fix cleanup timer

* Reset session stats when we cleanup the session.

* Put log file back

* Update test

* File should not be a part of this commit

* Add centralized shared performance timer for use anywhere

* Post-rebase cleanup

* Support returning nil from storage provider save

* Updates to reflect package changes + other updates in master

* Fix storage providers being overwritten

* Do not return pointer in save. Support cache headers with S3 providers

* Split out videojs + vhs and point to specific working versions of them

* Bump vjs and vhs versions

* Fix test

* Remove unused

* Update upload warning message

* No longer valid comment

* Pin videojs and vhs versions
This commit is contained in:
Gabe Kangas
2020-10-14 14:07:38 -07:00
committed by GitHub
parent 57f2e4b567
commit 6ea9affce0
43 changed files with 30296 additions and 56701 deletions

View File

@ -3,7 +3,6 @@ package ffmpeg
import (
"fmt"
"os/exec"
"path"
"strconv"
"strings"
@ -27,6 +26,8 @@ type Transcoder struct {
appendToStream bool
ffmpegPath string
segmentIdentifier string
internalListenerPort int
TranscoderCompleted func(error)
}
// HLSVariant is a combination of settings that results in a single HLS stream
@ -91,16 +92,27 @@ func (t *Transcoder) Start() {
log.Panicln(err, command)
}
err = _commandExec.Wait()
if t.TranscoderCompleted != nil {
t.TranscoderCompleted(err)
}
return
}
func (t *Transcoder) getString() string {
hlsOptionFlags := []string{
"delete_segments",
"program_date_time",
"temp_file",
var port int
if config.Config != nil {
port = config.Config.GetPublicWebServerPort() + 1
} else if t.internalListenerPort != 0 {
port = t.internalListenerPort
} else {
log.Panicln("A internal port must be set for transcoder callback")
}
localListenerAddress := "http://127.0.0.1:" + strconv.Itoa(port)
hlsOptionFlags := []string{}
if t.appendToStream {
hlsOptionFlags = append(hlsOptionFlags, "append_list")
}
@ -109,32 +121,43 @@ func (t *Transcoder) getString() string {
t.segmentIdentifier = shortid.MustGenerate()
}
hlsOptionsString := ""
if len(hlsOptionFlags) > 0 {
hlsOptionsString = "-hls_flags " + strings.Join(hlsOptionFlags, "+")
}
ffmpegFlags := []string{
"cat", t.input, "|",
t.ffmpegPath,
"-hide_banner",
"-i pipe:",
"-loglevel warning",
"-i ", t.input,
t.getVariantsString(),
// HLS Output
"-f", "hls",
"-hls_time", strconv.Itoa(t.segmentLengthSeconds), // Length of each segment
"-hls_list_size", strconv.Itoa(t.hlsPlaylistLength), // Max # in variant playlist
"-hls_delete_threshold", "10", // Start deleting files after hls_list_size + 10
"-hls_flags", strings.Join(hlsOptionFlags, "+"), // Specific options in HLS generation
hlsOptionsString,
// Video settings
"-tune", "zerolatency", // Option used for good for fast encoding and low-latency streaming (always includes iframes in each segment)
// "-profile:v", "high", // Main for standard definition (SD) to 640×480, High for high definition (HD) to 1920×1080
"-pix_fmt", "yuv420p", // Force yuv420p color format
"-profile:v", "high", // Main for standard definition (SD) to 640×480, High for high definition (HD) to 1920×1080
"-sc_threshold", "0", // Disable scene change detection for creating segments
// Filenames
"-master_pl_name", "stream.m3u8",
"-strftime 1", // Support the use of strftime in filenames
"-hls_segment_filename", path.Join(t.segmentOutputPath, "/%v/stream-%s-"+t.segmentIdentifier+".ts"), // Each segment's filename
"-strftime 1", // Support the use of strftime in filenames
"-hls_segment_filename", localListenerAddress + "/%v/stream-" + t.segmentIdentifier + "%s.ts", // Send HLS segments back to us over HTTP
"-max_muxing_queue_size", "400", // Workaround for Too many packets error: https://trac.ffmpeg.org/ticket/6375?cversion=0
path.Join(t.segmentOutputPath, "/%v/stream.m3u8"), // Each variant's playlist
"2> transcoder.log",
"-method PUT -http_persistent 1", // HLS results sent back to us will be over PUTs
"-fflags +genpts", // Generate presentation time stamp if missing
localListenerAddress + "/%v/stream.m3u8", // Send HLS playlists back to us over HTTP
"2> transcoder.log", // Log to a file for debugging
}
return strings.Join(ffmpegFlags, " ")
@ -180,7 +203,7 @@ func getVariantFromConfigQuality(quality config.StreamQuality, index int) HLSVar
}
// NewTranscoder will return a new Transcoder, populated by the config
func NewTranscoder() Transcoder {
func NewTranscoder() *Transcoder {
transcoder := new(Transcoder)
transcoder.ffmpegPath = config.Config.GetFFMpegPath()
transcoder.hlsPlaylistLength = config.Config.GetMaxNumberOfReferencedSegmentsInPlaylist()
@ -207,7 +230,7 @@ func NewTranscoder() Transcoder {
transcoder.AddVariant(variant)
}
return *transcoder
return transcoder
}
// Uses `map` https://www.ffmpeg.org/ffmpeg-all.html#Stream-specifiers-1 https://www.ffmpeg.org/ffmpeg-all.html#Advanced-options
@ -364,3 +387,7 @@ func (t *Transcoder) SetAppendToStream(append bool) {
func (t *Transcoder) SetIdentifier(output string) {
t.segmentIdentifier = output
}
func (t *Transcoder) SetInternalHTTPPort(port int) {
t.internalListenerPort = port
}