From a33140ed45962676914b1162f66cd540ff7085ed Mon Sep 17 00:00:00 2001 From: Redouane Lakrache Date: Tue, 8 Nov 2022 12:56:58 +0100 Subject: [PATCH 1/3] Fix 0 bandwidth on master playlist (#155) * Fix 0 bandwidth on master playlist * Fix expected manifest Co-authored-by: Thom Shutt --- transcode/manifest.go | 2 +- transcode/manifest_test.go | 4 ++-- transcode/transcode.go | 8 ++++---- transcode/transcode_test.go | 16 ++++++++++------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/transcode/manifest.go b/transcode/manifest.go index 5f0bbd13a..e0318ee90 100644 --- a/transcode/manifest.go +++ b/transcode/manifest.go @@ -74,7 +74,7 @@ func GetSourceSegmentURLs(sourceManifestURL string, manifest m3u8.MediaPlaylist) // Generate a Master manifest, plus one Rendition manifest for each Profile we're transcoding, then write them to storage // Returns the master manifest URL on success -func GenerateAndUploadManifests(sourceManifest m3u8.MediaPlaylist, targetOSURL string, transcodedStats []RenditionStats) (string, error) { +func GenerateAndUploadManifests(sourceManifest m3u8.MediaPlaylist, targetOSURL string, transcodedStats []*RenditionStats) (string, error) { // Generate the master + rendition output manifests masterPlaylist := m3u8.NewMasterPlaylist() diff --git a/transcode/manifest_test.go b/transcode/manifest_test.go index 1f06f5c49..5dde45405 100644 --- a/transcode/manifest_test.go +++ b/transcode/manifest_test.go @@ -105,7 +105,7 @@ func TestItCanGenerateAndWriteManifests(t *testing.T) { masterManifestURL, err := GenerateAndUploadManifests( *sourceMediaPlaylist, outputDir, - []RenditionStats{ + []*RenditionStats{ { Name: "lowlowlow", FPS: 60, @@ -160,7 +160,7 @@ func TestCompliantMasterManifestOrdering(t *testing.T) { _, err = GenerateAndUploadManifests( *sourceMediaPlaylist, outputDir, - []RenditionStats{ + []*RenditionStats{ { Name: "lowlowlow", FPS: 60, diff --git a/transcode/transcode.go b/transcode/transcode.go index f167856be..dc79e6865 100644 --- a/transcode/transcode.go +++ b/transcode/transcode.go @@ -190,7 +190,7 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st return outputs, nil } -func transcodeSegment(segment segmentInfo, streamName, manifestID string, transcodeRequest TranscodeSegmentRequest, transcodeProfiles []clients.EncodedProfile, targetOSURL *url.URL, transcodedStats []RenditionStats) error { +func transcodeSegment(segment segmentInfo, streamName, manifestID string, transcodeRequest TranscodeSegmentRequest, transcodeProfiles []clients.EncodedProfile, targetOSURL *url.URL, transcodedStats []*RenditionStats) error { rc, err := clients.DownloadOSURL(segment.Input.URL) if err != nil { return fmt.Errorf("failed to download source segment %q: %s", segment.Input, err) @@ -312,10 +312,10 @@ type segmentInfo struct { Index int } -func statsFromProfiles(profiles []clients.EncodedProfile) []RenditionStats { - stats := []RenditionStats{} +func statsFromProfiles(profiles []clients.EncodedProfile) []*RenditionStats { + stats := []*RenditionStats{} for _, profile := range profiles { - stats = append(stats, RenditionStats{ + stats = append(stats, &RenditionStats{ Name: profile.Name, Width: profile.Width, // TODO: extract this from actual media retrieved from B Height: profile.Height, // TODO: extract this from actual media retrieved from B diff --git a/transcode/transcode_test.go b/transcode/transcode_test.go index 45ff39183..40be3089e 100644 --- a/transcode/transcode_test.go +++ b/transcode/transcode_test.go @@ -124,15 +124,19 @@ func TestItCanTranscode(t *testing.T) { require.NoError(t, err) // Confirm the master manifest was created and that it looks like a manifest + var expectedMasterManifest = `#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=28800,RESOLUTION=2020x2020,NAME="0-2020p0" +2020p0/index.m3u8 +#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=19200,RESOLUTION=2020x2020,NAME="1-low-bitrate" +low-bitrate/index.m3u8 +` + masterManifestBytes, err := os.ReadFile(filepath.Join(topLevelDir, "index.m3u8")) + require.NoError(t, err) require.Greater(t, len(masterManifestBytes), 0) - require.Contains(t, string(masterManifestBytes), "#EXTM3U") - require.Contains(t, string(masterManifestBytes), "#EXT-X-STREAM-INF") - - // Confirm that the master manifest contains links to 2 renditions - require.Contains(t, string(masterManifestBytes), "low-bitrate/index.m3u8") - require.Contains(t, string(masterManifestBytes), "2020p0/index.m3u8") + require.Equal(t, expectedMasterManifest, string(masterManifestBytes)) // Check we received a progress callback for each segment require.Equal(t, 3, len(callbacks)) From bf68a01614fa97c80426bfa39b7d936fd0599c51 Mon Sep 17 00:00:00 2001 From: AlexKordic Date: Wed, 9 Nov 2022 15:48:05 +0100 Subject: [PATCH 2/3] remove rendition .dtsh files. Not used in playback (#162) --- handlers/misttriggers/recording_end.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/handlers/misttriggers/recording_end.go b/handlers/misttriggers/recording_end.go index 60be42c3c..7d37672b3 100644 --- a/handlers/misttriggers/recording_end.go +++ b/handlers/misttriggers/recording_end.go @@ -152,14 +152,6 @@ func (d *MistCallbackHandlersCollection) triggerRecordingEndSegmenting(w http.Re // should not block the ingestion flow or make it fail on error. log.LogError(requestID, "master createDtsh() failed", err, "destination", output.Manifest) } - for _, rendition := range output.Videos { - // we create dtsh for all rendition playlists - err := createDtsh(requestID, rendition.Location) - if err != nil { - // should not block the ingestion flow or make it fail on error. - log.LogError(requestID, "createDtsh() failed", err, "destination", rendition.Location) - } - } } }() From 7fc95eb7a68a93c0ce9915f8090d2710d2191ecf Mon Sep 17 00:00:00 2001 From: AlexKordic Date: Wed, 9 Nov 2022 18:55:07 +0100 Subject: [PATCH 3/3] Send the success callback after dtsh upload (#163) --- handlers/misttriggers/recording_end.go | 9 +++++++++ transcode/transcode.go | 5 ----- transcode/transcode_test.go | 10 ++++++---- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/handlers/misttriggers/recording_end.go b/handlers/misttriggers/recording_end.go index 7d37672b3..1879cd055 100644 --- a/handlers/misttriggers/recording_end.go +++ b/handlers/misttriggers/recording_end.go @@ -144,6 +144,15 @@ func (d *MistCallbackHandlersCollection) triggerRecordingEndSegmenting(w http.Re return } + // When createDtsh() completes issue another callback signaling to studio playback is ready + defer func() { + // Send the success callback + err = clients.DefaultCallbackClient.SendTranscodeStatusCompleted(transcodeRequest.CallbackURL, inputInfo, outputs) + if err != nil { + log.LogError(transcodeRequest.RequestID, "Failed to send TranscodeStatusCompleted callback", err, "url", transcodeRequest.CallbackURL) + } + }() + // prepare .dtsh headers for all rendition playlists for _, output := range outputs { // output is multivariant playlist diff --git a/transcode/transcode.go b/transcode/transcode.go index dc79e6865..3c89b9af6 100644 --- a/transcode/transcode.go +++ b/transcode/transcode.go @@ -181,11 +181,6 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st output.Videos = append(output.Videos, clients.OutputVideoFile{Location: rendition.ManifestLocation}) } outputs = []clients.OutputVideo{output} - // Send the success callback - err = clients.DefaultCallbackClient.SendTranscodeStatusCompleted(transcodeRequest.CallbackURL, inputInfo, outputs) - if err != nil { - log.LogError(transcodeRequest.RequestID, "Failed to send TranscodeStatusCompleted callback", err, "url", transcodeRequest.CallbackURL) - } // Return outputs for .dtsh file creation return outputs, nil } diff --git a/transcode/transcode_test.go b/transcode/transcode_test.go index 40be3089e..27f21f1d1 100644 --- a/transcode/transcode_test.go +++ b/transcode/transcode_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "os" + "path" "path/filepath" "strconv" "testing" @@ -103,7 +104,7 @@ func TestItCanTranscode(t *testing.T) { } // Check we don't get an error downloading or parsing it - _, err = RunTranscodeProcess( + outputs, err := RunTranscodeProcess( TranscodeSegmentRequest{ CallbackURL: callbackServer.URL, UploadURL: manifestFile.Name(), @@ -139,13 +140,14 @@ low-bitrate/index.m3u8 require.Equal(t, expectedMasterManifest, string(masterManifestBytes)) // Check we received a progress callback for each segment - require.Equal(t, 3, len(callbacks)) + require.Equal(t, 2, len(callbacks)) require.Equal(t, 0.7, callbacks[0]["completion_ratio"]) require.Equal(t, 1.0, callbacks[1]["completion_ratio"]) // Check we received a final Transcode Completed callback - require.Equal(t, 1.0, callbacks[2]["completion_ratio"]) - require.Equal(t, "success", callbacks[2]["status"]) + require.Equal(t, 1, len(outputs)) + require.Equal(t, path.Join(topLevelDir, "index.m3u8"), outputs[0].Manifest) + require.Equal(t, 2, len(outputs[0].Videos)) } func TestItCalculatesTheTranscodeCompletionPercentageCorrectly(t *testing.T) {