Skip to content

Commit

Permalink
add struct for transcoded media info. use it for calculating bitrate …
Browse files Browse the repository at this point in the history
…of transcoded results (#135)
  • Loading branch information
AlexKordic committed Nov 2, 2022
1 parent fe520dd commit d38bab6
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 16 deletions.
7 changes: 4 additions & 3 deletions transcode/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,21 @@ 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, transcodeProfiles []clients.EncodedProfile) (string, error) {
func GenerateAndUploadManifests(sourceManifest m3u8.MediaPlaylist, targetOSURL string, transcodedStats []RenditionStats) (string, error) {
// Generate the master + rendition output manifests
masterPlaylist := m3u8.NewMasterPlaylist()

for i, profile := range transcodeProfiles {
for i, profile := range transcodedStats {
// For each profile, add a new entry to the master manifest
bitsPerSecond := uint32(float64(profile.Bytes) * 8000.0 / float64(profile.DurationMs))
masterPlaylist.Append(
fmt.Sprintf("rendition-%d/rendition.m3u8", i),
&m3u8.MediaPlaylist{
TargetDuration: sourceManifest.TargetDuration,
},
m3u8.VariantParams{
Name: fmt.Sprintf("%d-%s", i, profile.Name),
Bandwidth: uint32(1), // TODO: Don't hardcode - this should come from the transcoder output
Bandwidth: bitsPerSecond,
FrameRate: float64(profile.FPS),
Resolution: fmt.Sprintf("%dx%d", profile.Width, profile.Height),
},
Expand Down
23 changes: 13 additions & 10 deletions transcode/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"testing"

"github.com/grafov/m3u8"
"github.com/livepeer/catalyst-api/clients"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -106,18 +105,22 @@ func TestItCanGenerateAndWriteManifests(t *testing.T) {
masterManifestURL, err := GenerateAndUploadManifests(
*sourceMediaPlaylist,
outputDir,
[]clients.EncodedProfile{
[]RenditionStats{
{
Name: "lowlowlow",
FPS: 60,
Width: 800,
Height: 600,
Name: "lowlowlow",
FPS: 60,
Width: 800,
Height: 600,
Bytes: 1,
DurationMs: 8000,
},
{
Name: "super-high-def",
FPS: 30,
Width: 1080,
Height: 720,
Name: "super-high-def",
FPS: 30,
Width: 1080,
Height: 720,
Bytes: 1,
DurationMs: 8000,
},
},
)
Expand Down
36 changes: 33 additions & 3 deletions transcode/transcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st
targetOSURL := segmentedUploadURL.ResolveReference(relativeTranscodeURL)
// Grab some useful parameters to be used later from the TranscodeSegmentRequest
sourceManifestOSURL := transcodeRequest.UploadURL
// transcodeProfiles are desired constraints for transcoding process
transcodeProfiles := transcodeRequest.Profiles

// If Profiles haven't been overridden, use the default set
Expand Down Expand Up @@ -115,6 +116,10 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st

// Generate a unique ID to use when talking to the Broadcaster
manifestID := "manifest-" + config.RandomTrailer(8)
// transcodedStats hold actual info from transcoded results within requested constraints (this usually differs from requested profiles)
transcodedStats := statsFromProfiles(transcodeProfiles)

// Iterate through the segment URLs and transcode them
// Use channel to queue segments
queue := make(chan segmentInfo, len(sourceSegmentURLs))
for segmentIndex, u := range sourceSegmentURLs {
Expand All @@ -130,7 +135,7 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st
go func() {
defer completed.Done()
for segment := range queue {
err := transcodeSegment(segment, streamName, manifestID, transcodeRequest, transcodeProfiles, targetOSURL)
err := transcodeSegment(segment, streamName, manifestID, transcodeRequest, transcodeProfiles, targetOSURL, transcodedStats)
if err != nil {
errors <- err
return
Expand All @@ -152,7 +157,7 @@ func RunTranscodeProcess(transcodeRequest TranscodeSegmentRequest, streamName st
}

// Build the manifests and push them to storage
manifestManifestURL, err := GenerateAndUploadManifests(sourceManifest, targetOSURL.String(), transcodeProfiles)
manifestManifestURL, err := GenerateAndUploadManifests(sourceManifest, targetOSURL.String(), transcodedStats)
if err != nil {
return outputs, err
}
Expand All @@ -172,7 +177,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) 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)
Expand Down Expand Up @@ -214,6 +219,9 @@ func transcodeSegment(segment segmentInfo, streamName, manifestID string, transc
if err != nil {
return fmt.Errorf("failed to upload master playlist: %s", err)
}
// bitrate calculation
transcodedStats[renditionIndex].Bytes += int64(len(transcodedSegment.MediaData))
transcodedStats[renditionIndex].DurationMs += float64(segment.Input.DurationMillis)
}
return nil
}
Expand Down Expand Up @@ -258,3 +266,25 @@ type segmentInfo struct {
Input SourceSegment
Index int
}

func statsFromProfiles(profiles []clients.EncodedProfile) []RenditionStats {
stats := []RenditionStats{}
for _, profile := range profiles {
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
FPS: profile.FPS, // TODO: extract this from actual media retrieved from B
})
}
return stats
}

type RenditionStats struct {
Name string
Width int64
Height int64
FPS int64
Bytes int64
DurationMs float64
}

0 comments on commit d38bab6

Please sign in to comment.