From 10b1e78d643007f7933a124ed8bc3339d907c214 Mon Sep 17 00:00:00 2001 From: Dror Levy Date: Fri, 21 Jul 2023 13:14:10 +0900 Subject: [PATCH 1/3] transcode based --- application/application.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/application/application.go b/application/application.go index e87ee3e..af1a613 100644 --- a/application/application.go +++ b/application/application.go @@ -986,13 +986,13 @@ func (a *Application) loadAndServeFiles(filenames []string, contentType string, // attempt to use that contentTypeToUse := contentType if contentType != "" { - } else if knownFileType { - // If this is a media file we know the chromecast can play, - // then we don't need to transcode it. - contentTypeToUse, _ = a.possibleContentType(filename) - if a.castPlayableContentType(contentTypeToUse) { - transcodeFile = false - } + // } else if knownFileType { + // // If this is a media file we know the chromecast can play, + // // then we don't need to transcode it. + // contentTypeToUse, _ = a.possibleContentType(filename) + // if a.castPlayableContentType(contentTypeToUse) { + // transcodeFile = false + // } } else if transcodeFile { contentTypeToUse = "video/mp4" } @@ -1126,14 +1126,18 @@ func (a *Application) startStreamingServer() error { } func (a *Application) serveLiveStreaming(w http.ResponseWriter, r *http.Request, filename string) { + + filterpath := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(filename, "\\", "/"), "[", "\\["), "]", "\\]"), ":", "\\\\:") cmd := exec.Command( "ffmpeg", - "-re", // encode at 1x playback speed, to not burn the CPU + "-hwaccel", "cuda", + "-c:v", "h264_cuvid", "-i", filename, - "-vcodec", "h264", + "-vcodec", "h264_nvenc", "-acodec", "aac", "-ac", "2", // chromecasts don't support more than two audio channels "-f", "mp4", + "-vf", "subtitles="+filterpath+"", "-movflags", "frag_keyframe+faststart", "-strict", "-experimental", "pipe:1", From 524e92fa0a3171fe8a448bd8b4964e5ccf12738c Mon Sep 17 00:00:00 2001 From: Dror Levy Date: Sat, 22 Jul 2023 14:53:48 +0900 Subject: [PATCH 2/3] track based --- application/application.go | 30 +++++++++++++++++++++++------- cast/payload.go | 28 +++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/application/application.go b/application/application.go index af1a613..d4f44b2 100644 --- a/application/application.go +++ b/application/application.go @@ -780,7 +780,20 @@ func (a *Application) Load(filenameOrUrl string, startTime int, contentType stri ContentId: mi.contentURL, StreamType: "BUFFERED", ContentType: mi.contentType, + Tracks: []cast.MediaTrack{ + { + TrackId: 1, + TrackContentId: strings.ReplaceAll(mi.contentURL, ".mp4", ".vtt"), + Language: "en-US", + Subtype: "SUBTITLES", + Type: "TEXT", + TrackContentType: "text/vtt", + Name: "kundalini Subtitle", + }, + }, + TextTrackStyle: cast.TextTrackStyle{BackgroundColor: "#FFFFFF00", EdgeType: "OUTLINE", EdgeColor: "#000000FF"}, }, + ActiveTrackIds: []int{1}, }) // If we should detach from waiting for media to finish playing @@ -986,13 +999,13 @@ func (a *Application) loadAndServeFiles(filenames []string, contentType string, // attempt to use that contentTypeToUse := contentType if contentType != "" { - // } else if knownFileType { - // // If this is a media file we know the chromecast can play, - // // then we don't need to transcode it. - // contentTypeToUse, _ = a.possibleContentType(filename) - // if a.castPlayableContentType(contentTypeToUse) { - // transcodeFile = false - // } + } else if knownFileType { + // If this is a media file we know the chromecast can play, + // then we don't need to transcode it. + contentTypeToUse, _ = a.possibleContentType(filename) + if a.castPlayableContentType(contentTypeToUse) { + transcodeFile = false + } } else if transcodeFile { contentTypeToUse = "video/mp4" } @@ -1084,6 +1097,9 @@ func (a *Application) startStreamingServer() error { canServe = true } } + if strings.Contains(filename, ".vtt") { + canServe = true + } a.playedItems[filename] = PlayedItem{ContentID: filename, Started: time.Now().Unix()} a.writePlayedItems() diff --git a/cast/payload.go b/cast/payload.go index ca02446..4cf4e4f 100644 --- a/cast/payload.go +++ b/cast/payload.go @@ -101,6 +101,8 @@ type LoadMediaCommand struct { Autoplay bool `json:"autoplay"` QueueData QueueData `json:"queueData"` CustomData interface{} `json:"customData"` + + ActiveTrackIds []int `json:"activeTrackIds"` } type QueueData struct { @@ -108,11 +110,27 @@ type QueueData struct { } type MediaItem struct { - ContentId string `json:"contentId"` - ContentType string `json:"contentType"` - StreamType string `json:"streamType"` - Duration float32 `json:"duration"` - Metadata MediaMetadata `json:"metadata"` + ContentId string `json:"contentId"` + ContentType string `json:"contentType"` + StreamType string `json:"streamType"` + Duration float32 `json:"duration"` + Metadata MediaMetadata `json:"metadata"` + Tracks []MediaTrack `json:"tracks"` + TextTrackStyle TextTrackStyle `json:"textTrackStyle"` +} +type TextTrackStyle struct { + BackgroundColor string `json:"backgroundColor"` + EdgeType string `json:"edgeType"` + EdgeColor string `json:"edgeColor"` +} +type MediaTrack struct { + TrackId int `json:"trackId"` + TrackContentId string `json:"trackContentId"` + Language string `json:"language"` + Subtype string `json:"subtype"` + Type string `json:"type"` + TrackContentType string `json:"trackContentType"` + Name string `json:"name"` } type MediaMetadata struct { From 1ea196336289b3d64d3c9c691f31499ea8761726 Mon Sep 17 00:00:00 2001 From: Dror Levy Date: Sat, 29 Jul 2023 00:34:18 +0900 Subject: [PATCH 3/3] track based subtitles --- application/application.go | 71 ++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/application/application.go b/application/application.go index d4f44b2..0750641 100644 --- a/application/application.go +++ b/application/application.go @@ -783,7 +783,7 @@ func (a *Application) Load(filenameOrUrl string, startTime int, contentType stri Tracks: []cast.MediaTrack{ { TrackId: 1, - TrackContentId: strings.ReplaceAll(mi.contentURL, ".mp4", ".vtt"), + TrackContentId: mi.contentURL + "&subtitles=1", Language: "en-US", Subtype: "SUBTITLES", Type: "TEXT", @@ -1092,14 +1092,12 @@ func (a *Application) startStreamingServer() error { // already been validated and is useable. filename := r.URL.Query().Get("media_file") canServe := false + isSubtitles := r.URL.Query().Get("subtitles") for _, fn := range a.mediaFilenames { if fn == filename { canServe = true } } - if strings.Contains(filename, ".vtt") { - canServe = true - } a.playedItems[filename] = PlayedItem{ContentID: filename, Started: time.Now().Unix()} a.writePlayedItems() @@ -1112,10 +1110,12 @@ func (a *Application) startStreamingServer() error { liveStreaming = true } - a.log("canServe=%t, liveStreaming=%t, filename=%s", canServe, liveStreaming, filename) + a.log("canServe=%t, liveStreaming=%t, filename=%s, isSubtitles=%s", canServe, liveStreaming, filename, isSubtitles) if canServe { if !liveStreaming { http.ServeFile(w, r, filename) + } else if isSubtitles != "" { + a.serveSubtitles(w, r, filename) } else { a.serveLiveStreaming(w, r, filename) } @@ -1141,9 +1141,41 @@ func (a *Application) startStreamingServer() error { return nil } +// func (a *Application) serveLiveStreamingHardsub(w http.ResponseWriter, r *http.Request, filename string) { + +// filterpath := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(filename, "\\", "/"), "[", "\\["), "]", "\\]"), ":", "\\\\:") +// cmd := exec.Command( +// "ffmpeg", +// "-hwaccel", "cuda", +// "-c:v", "h264_cuvid", +// "-i", filename, +// "-vcodec", "h264_nvenc", +// "-acodec", "aac", +// "-ac", "2", // chromecasts don't support more than two audio channels +// "-f", "mp4", +// "-vf", "subtitles="+filterpath+"", +// "-movflags", "frag_keyframe+faststart", +// "-strict", "-experimental", +// "pipe:1", +// ) + +// cmd.Stdout = w +// if a.debug { +// cmd.Stderr = os.Stderr +// } + +// w.Header().Set("Access-Control-Allow-Origin", "*") +// w.Header().Set("Transfer-Encoding", "chunked") + +// if err := cmd.Run(); err != nil { +// log.WithField("package", "application").WithFields(log.Fields{ +// "filename": filename, +// }).WithError(err).Error("error transcoding") +// } +// } + func (a *Application) serveLiveStreaming(w http.ResponseWriter, r *http.Request, filename string) { - filterpath := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(filename, "\\", "/"), "[", "\\["), "]", "\\]"), ":", "\\\\:") cmd := exec.Command( "ffmpeg", "-hwaccel", "cuda", @@ -1153,7 +1185,6 @@ func (a *Application) serveLiveStreaming(w http.ResponseWriter, r *http.Request, "-acodec", "aac", "-ac", "2", // chromecasts don't support more than two audio channels "-f", "mp4", - "-vf", "subtitles="+filterpath+"", "-movflags", "frag_keyframe+faststart", "-strict", "-experimental", "pipe:1", @@ -1174,6 +1205,32 @@ func (a *Application) serveLiveStreaming(w http.ResponseWriter, r *http.Request, } } +func (a *Application) serveSubtitles(w http.ResponseWriter, r *http.Request, filename string) { + + cmd := exec.Command( + "ffmpeg", + "-i", filename, + "-c:s", "webvtt", + "-f", "webvtt", + "-strict", "-experimental", + "pipe:1", + ) + + cmd.Stdout = w + if a.debug { + cmd.Stderr = os.Stderr + } + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Transfer-Encoding", "chunked") + + if err := cmd.Run(); err != nil { + log.WithField("package", "application").WithFields(log.Fields{ + "filename": filename, + }).WithError(err).Error("error transcoding") + } +} + func (a *Application) log(message string, args ...interface{}) { if a.debug { log.WithField("package", "application").Infof(message, args...)