diff --git a/README.md b/README.md index 5078e1d4..a6d08f5f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ The Video Transcoding API provides an agnostic API to transcode media assets across different cloud services. Currently, it supports the following providers: -- [Amazon Elastic Transcoder](https://aws.amazon.com/elastictranscoder/) - [Bitmovin](http://bitmovin.com) - [Elemental Conductor](http://www.elementaltechnologies.com/products/elemental-conductor) - [Encoding.com](http://encoding.com) @@ -26,19 +25,6 @@ environment variables: ### Providers configuration -#### For [Amazon Elastic Transcoder](https://aws.amazon.com/elastictranscoder/) - -``` -export AWS_ACCESS_KEY_ID=your.access.key.id -export AWS_SECRET_ACCESS_KEY=your.secret.access.key -export AWS_REGION="us-east-1" -export ELASTICTRANSCODER_PIPELINE_ID="yourpipeline-id" -``` - -Please notice that for Elastic Transcoder you don't specify the destination -bucket, as it is [defined in the Elastic Transcoder -Pipeline](https://docs.aws.amazon.com/elastictranscoder/latest/developerguide/pipeline-settings.html#pipeline-settings-configure-transcoded-bucket). - #### For [Bitmovin](http://bitmovin.com) ``` diff --git a/config/config.go b/config/config.go index 84bfcf29..8d448e57 100644 --- a/config/config.go +++ b/config/config.go @@ -15,7 +15,6 @@ type Config struct { DefaultSegmentDuration uint `envconfig:"DEFAULT_SEGMENT_DURATION" default:"5"` Redis *storage.Config EncodingCom *EncodingCom - ElasticTranscoder *ElasticTranscoder ElementalConductor *ElementalConductor Hybrik *Hybrik Zencoder *Zencoder @@ -41,15 +40,6 @@ type Zencoder struct { Destination string `envconfig:"ZENCODER_DESTINATION"` } -// ElasticTranscoder represents the set of configurations for the Elastic -// Transcoder provider. -type ElasticTranscoder struct { - AccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID"` - SecretAccessKey string `envconfig:"AWS_SECRET_ACCESS_KEY"` - Region string `envconfig:"AWS_REGION"` - PipelineID string `envconfig:"ELASTICTRANSCODER_PIPELINE_ID"` -} - // ElementalConductor represents the set of configurations for the Elemental // Conductor provider. type ElementalConductor struct { diff --git a/config/config_test.go b/config/config_test.go index 675cbd96..dbc08475 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -29,7 +29,6 @@ func TestLoadConfigFromEnv(t *testing.T) { "AWS_ACCESS_KEY_ID": "AKIANOTREALLY", "AWS_SECRET_ACCESS_KEY": "secret-key", "AWS_REGION": "us-east-1", - "ELASTICTRANSCODER_PIPELINE_ID": "mypipeline", "ELEMENTALCONDUCTOR_HOST": "elemental-server", "ELEMENTALCONDUCTOR_USER_LOGIN": "myuser", "ELEMENTALCONDUCTOR_API_KEY": "secret-key", @@ -83,12 +82,6 @@ func TestLoadConfigFromEnv(t *testing.T) { PresetPath: "transcoding-api-presets", }, Zencoder: &Zencoder{}, - ElasticTranscoder: &ElasticTranscoder{ - AccessKeyID: "AKIANOTREALLY", - SecretAccessKey: "secret-key", - Region: "us-east-1", - PipelineID: "mypipeline", - }, ElementalConductor: &ElementalConductor{ Host: "elemental-server", UserLogin: "myuser", @@ -151,7 +144,6 @@ func TestLoadConfigFromEnvWithDefaults(t *testing.T) { "AWS_ACCESS_KEY_ID": "AKIANOTREALLY", "AWS_SECRET_ACCESS_KEY": "secret-key", "AWS_REGION": "us-east-1", - "ELASTICTRANSCODER_PIPELINE_ID": "mypipeline", "ELEMENTALCONDUCTOR_HOST": "elemental-server", "ELEMENTALCONDUCTOR_USER_LOGIN": "myuser", "ELEMENTALCONDUCTOR_API_KEY": "secret-key", @@ -187,12 +179,6 @@ func TestLoadConfigFromEnvWithDefaults(t *testing.T) { Destination: "https://safe-stuff", StatusEndpoint: "http://status.encoding.com", }, - ElasticTranscoder: &ElasticTranscoder{ - AccessKeyID: "AKIANOTREALLY", - SecretAccessKey: "secret-key", - Region: "us-east-1", - PipelineID: "mypipeline", - }, ElementalConductor: &ElementalConductor{ Host: "elemental-server", UserLogin: "myuser", diff --git a/go.mod b/go.mod index 1cd61d26..beee5639 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/video-dev/video-transcoding-api/v2 require ( github.com/NYTimes/gizmo v1.2.12 github.com/NYTimes/gziphandler v1.1.1 - github.com/aws/aws-sdk-go v1.21.8 github.com/aws/aws-sdk-go-v2 v0.10.0 github.com/bitmovin/bitmovin-go v1.29.0 github.com/fsouza/ctxlogger v1.5.6 diff --git a/go.sum b/go.sum index f119f79e..8fa1647b 100644 --- a/go.sum +++ b/go.sum @@ -19,10 +19,6 @@ github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4 h1:vdT7QwBh github.com/Gurpartap/logrus-stack v0.0.0-20170710170904-89c00d8a28f4/go.mod h1:SvXOG8ElV28oAiG9zv91SDe5+9PfIr7PPccpr8YyXNs= github.com/NYTimes/gizmo v1.2.9 h1:DzAWlQBS7ZU4BlrJr6/U+A3Od1M1LH5sVuCpbwDsqgI= github.com/NYTimes/gizmo v1.2.9/go.mod h1:+Ic0KePzIxrkcfIXnII7WMmrciukqbXuQcTOWAzJQNg= -github.com/NYTimes/gizmo v1.2.10 h1:0M2wSXQIlolVTyChFnBnXwqxhnheVKj6rk8YnXBEk9U= -github.com/NYTimes/gizmo v1.2.10/go.mod h1:+Ic0KePzIxrkcfIXnII7WMmrciukqbXuQcTOWAzJQNg= -github.com/NYTimes/gizmo v1.2.11 h1:PadcuT9T51hklWtjlS+U6Hkf4uZ3V7osAupDItC3NP8= -github.com/NYTimes/gizmo v1.2.11/go.mod h1:+Ic0KePzIxrkcfIXnII7WMmrciukqbXuQcTOWAzJQNg= github.com/NYTimes/gizmo v1.2.12 h1:dz88yhOQNeOQ2BqCdEyHjl37BtHk0jfCXUVINFXagiw= github.com/NYTimes/gizmo v1.2.12/go.mod h1:+Ic0KePzIxrkcfIXnII7WMmrciukqbXuQcTOWAzJQNg= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= @@ -34,12 +30,6 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/aws/aws-sdk-go v1.15.31/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.21.6 h1:3GuIm55Uls52aQIDGBnSEZbk073jpasfQyeM5eZU61Q= -github.com/aws/aws-sdk-go v1.21.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.21.7 h1:ml+k7szyVaq4YD+3LhqOGl9tgMTqgMbpnuUSkB6UJvQ= -github.com/aws/aws-sdk-go v1.21.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.21.8 h1:Lv6hW2twBhC6mGZAuWtqplEpIIqtVctJg02sE7Qn0Zw= -github.com/aws/aws-sdk-go v1.21.8/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.10.0 h1:qxZ7TyWFEIucMPQR2qymRx7JZ+hWF0N8HyCWh0XKh6Q= github.com/aws/aws-sdk-go-v2 v0.10.0/go.mod h1:cpXCmy3BB+lqwGweJjdawczHW3a+g8QgcFHcoOVoHao= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= diff --git a/main.go b/main.go index b4d18545..19107d56 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,6 @@ import ( "github.com/google/gops/agent" "github.com/video-dev/video-transcoding-api/v2/config" _ "github.com/video-dev/video-transcoding-api/v2/provider/bitmovin" - _ "github.com/video-dev/video-transcoding-api/v2/provider/elastictranscoder" _ "github.com/video-dev/video-transcoding-api/v2/provider/elementalconductor" _ "github.com/video-dev/video-transcoding-api/v2/provider/encodingcom" _ "github.com/video-dev/video-transcoding-api/v2/provider/hybrik" diff --git a/provider/elastictranscoder/aws.go b/provider/elastictranscoder/aws.go deleted file mode 100644 index aa37ba34..00000000 --- a/provider/elastictranscoder/aws.go +++ /dev/null @@ -1,419 +0,0 @@ -// Package elastictranscoder provides a implementation of the provider that -// uses AWS Elastic Transcoder for transcoding media files. -// -// It doesn't expose any public type. In order to use the provider, one must - -// -// import ( -// "github.com/video-dev/video-transcoding-api/v2/provider" -// "github.com/video-dev/video-transcoding-api/v2/provider/elastictranscoder" -// ) -// -// func UseProvider() { -// factory, err := provider.GetProviderFactory(elastictranscoder.Name) -// // handle err and use factory to get an instance of the provider. -// } -package elastictranscoder - -import ( - "errors" - "fmt" - "path/filepath" - "regexp" - "strconv" - "strings" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/elastictranscoder" - "github.com/aws/aws-sdk-go/service/elastictranscoder/elastictranscoderiface" - "github.com/video-dev/video-transcoding-api/v2/config" - "github.com/video-dev/video-transcoding-api/v2/db" - "github.com/video-dev/video-transcoding-api/v2/provider" -) - -const ( - // Name is the name used for registering the Elastic Transcoder - // provider in the registry of providers. - Name = "elastictranscoder" - - defaultAWSRegion = "us-east-1" - hlsPlayList = "HLSv3" -) - -var ( - errAWSInvalidConfig = errors.New("invalid Elastic Transcoder config. Please define the configuration entries in the config file or environment variables") - s3Pattern = regexp.MustCompile(`^s3://`) -) - -func init() { - provider.Register(Name, elasticTranscoderFactory) -} - -type awsProvider struct { - c elastictranscoderiface.ElasticTranscoderAPI - config *config.ElasticTranscoder -} - -func (p *awsProvider) Transcode(job *db.Job) (*provider.JobStatus, error) { - var adaptiveStreamingOutputs []db.TranscodeOutput - source := p.normalizeSource(job.SourceMedia) - params := elastictranscoder.CreateJobInput{ - PipelineId: aws.String(p.config.PipelineID), - Input: &elastictranscoder.JobInput{Key: aws.String(source)}, - } - params.Outputs = make([]*elastictranscoder.CreateJobOutput, len(job.Outputs)) - for i, output := range job.Outputs { - presetID, ok := output.Preset.ProviderMapping[Name] - if !ok { - return nil, provider.ErrPresetMapNotFound - } - presetQuery := &elastictranscoder.ReadPresetInput{ - Id: aws.String(presetID), - } - presetOutput, err := p.c.ReadPreset(presetQuery) - if err != nil { - return nil, err - } - if presetOutput.Preset == nil || presetOutput.Preset.Container == nil { - return nil, fmt.Errorf("misconfigured preset: %s", presetID) - } - var isAdaptiveStreamingPreset bool - if *presetOutput.Preset.Container == "ts" { - isAdaptiveStreamingPreset = true - adaptiveStreamingOutputs = append(adaptiveStreamingOutputs, output) - } - params.Outputs[i] = &elastictranscoder.CreateJobOutput{ - PresetId: aws.String(presetID), - Key: p.outputKey(job, output.FileName, isAdaptiveStreamingPreset), - } - if isAdaptiveStreamingPreset { - params.Outputs[i].SegmentDuration = aws.String(strconv.Itoa(int(job.StreamingParams.SegmentDuration))) - } - } - - if len(adaptiveStreamingOutputs) > 0 { - playlistFileName := job.StreamingParams.PlaylistFileName - playlistFileName = strings.TrimRight(playlistFileName, filepath.Ext(playlistFileName)) - jobPlaylist := elastictranscoder.CreateJobPlaylist{ - Format: aws.String(hlsPlayList), - Name: aws.String(job.ID + "/" + playlistFileName), - } - - jobPlaylist.OutputKeys = make([]*string, len(adaptiveStreamingOutputs)) - for i, output := range adaptiveStreamingOutputs { - jobPlaylist.OutputKeys[i] = p.outputKey(job, output.FileName, true) - } - - params.Playlists = []*elastictranscoder.CreateJobPlaylist{&jobPlaylist} - } - resp, err := p.c.CreateJob(¶ms) - if err != nil { - return nil, err - } - return &provider.JobStatus{ - ProviderName: Name, - ProviderJobID: aws.StringValue(resp.Job.Id), - Status: provider.StatusQueued, - }, nil -} - -func (p *awsProvider) normalizeSource(source string) string { - if s3Pattern.MatchString(source) { - source = strings.Replace(source, "s3://", "", 1) - parts := strings.SplitN(source, "/", 2) - return parts[len(parts)-1] - } - return source -} - -func (p *awsProvider) outputKey(job *db.Job, fileName string, adaptive bool) *string { - if adaptive { - fileName = strings.TrimRight(fileName, filepath.Ext(fileName)) - } - return aws.String(job.ID + "/" + fileName) -} - -func (p *awsProvider) createVideoPreset(preset db.Preset) *elastictranscoder.VideoParameters { - videoPreset := elastictranscoder.VideoParameters{ - DisplayAspectRatio: aws.String("auto"), - FrameRate: aws.String("auto"), - SizingPolicy: aws.String("Fill"), - PaddingPolicy: aws.String("Pad"), - Codec: &preset.Video.Codec, - KeyframesMaxDist: &preset.Video.GopSize, - CodecOptions: map[string]*string{ - "Profile": aws.String(strings.ToLower(preset.Video.Profile)), - "Level": &preset.Video.ProfileLevel, - "MaxReferenceFrames": aws.String("2"), - }, - } - if preset.Video.Width != "" { - videoPreset.MaxWidth = &preset.Video.Width - } else { - videoPreset.MaxWidth = aws.String("auto") - } - if preset.Video.Height != "" { - videoPreset.MaxHeight = &preset.Video.Height - } else { - videoPreset.MaxHeight = aws.String("auto") - } - normalizedVideoBitRate, _ := strconv.Atoi(preset.Video.Bitrate) - videoBitrate := strconv.Itoa(normalizedVideoBitRate / 1000) - videoPreset.BitRate = &videoBitrate - switch preset.Video.Codec { - case "h264": - videoPreset.Codec = aws.String("H.264") - case "vp8", "vp9": - videoPreset.Codec = aws.String(preset.Video.Codec) - delete(videoPreset.CodecOptions, "MaxReferenceFrames") - delete(videoPreset.CodecOptions, "Level") - // Recommended profile value is zero, based on: - // http://www.webmproject.org/docs/encoder-parameters/ - videoPreset.CodecOptions["Profile"] = aws.String("0") - } - if preset.Video.GopMode == "fixed" { - videoPreset.FixedGOP = aws.String("true") - } - return &videoPreset -} - -func (p *awsProvider) createThumbsPreset() *elastictranscoder.Thumbnails { - thumbsPreset := &elastictranscoder.Thumbnails{ - PaddingPolicy: aws.String("Pad"), - Format: aws.String("png"), - Interval: aws.String("1"), - SizingPolicy: aws.String("Fill"), - MaxWidth: aws.String("auto"), - MaxHeight: aws.String("auto"), - } - return thumbsPreset -} - -func (p *awsProvider) createAudioPreset(preset db.Preset) *elastictranscoder.AudioParameters { - audioPreset := &elastictranscoder.AudioParameters{ - Codec: &preset.Audio.Codec, - Channels: aws.String("auto"), - SampleRate: aws.String("auto"), - } - - normalizedAudioBitRate, _ := strconv.Atoi(preset.Audio.Bitrate) - audioBitrate := strconv.Itoa(normalizedAudioBitRate / 1000) - audioPreset.BitRate = &audioBitrate - - switch preset.Audio.Codec { - case "aac": - audioPreset.Codec = aws.String("AAC") - case "libvorbis": - audioPreset.Codec = aws.String("vorbis") - } - - return audioPreset -} - -func (p *awsProvider) CreatePreset(preset db.Preset) (string, error) { - presetInput := elastictranscoder.CreatePresetInput{ - Name: &preset.Name, - Description: &preset.Description, - } - if preset.Container == "m3u8" { - presetInput.Container = aws.String("ts") - } else { - presetInput.Container = &preset.Container - } - presetInput.Video = p.createVideoPreset(preset) - presetInput.Audio = p.createAudioPreset(preset) - presetInput.Thumbnails = p.createThumbsPreset() - presetOutput, err := p.c.CreatePreset(&presetInput) - if err != nil { - return "", err - } - return *presetOutput.Preset.Id, nil -} - -func (p *awsProvider) GetPreset(presetID string) (interface{}, error) { - readPresetInput := &elastictranscoder.ReadPresetInput{ - Id: aws.String(presetID), - } - readPresetOutput, err := p.c.ReadPreset(readPresetInput) - if err != nil { - return nil, err - } - return readPresetOutput, err -} - -func (p *awsProvider) DeletePreset(presetID string) error { - presetInput := elastictranscoder.DeletePresetInput{ - Id: &presetID, - } - _, err := p.c.DeletePreset(&presetInput) - return err -} - -func (p *awsProvider) JobStatus(job *db.Job) (*provider.JobStatus, error) { - id := job.ProviderJobID - resp, err := p.c.ReadJob(&elastictranscoder.ReadJobInput{Id: aws.String(id)}) - if err != nil { - return nil, err - } - totalJobs := len(resp.Job.Outputs) - completedJobs := float64(0) - outputs := make(map[string]interface{}, totalJobs) - for _, output := range resp.Job.Outputs { - outputStatus := p.statusMap(aws.StringValue(output.Status)) - switch outputStatus { - case provider.StatusFinished, provider.StatusCanceled, provider.StatusFailed: - completedJobs++ - } - outputs[aws.StringValue(output.Key)] = aws.StringValue(output.StatusDetail) - } - outputDestination, err := p.getOutputDestination(job, resp.Job) - if err != nil { - outputDestination = err.Error() - } - outputFiles, err := p.getOutputFiles(resp.Job) - if err != nil { - return nil, err - } - var sourceInfo provider.SourceInfo - if resp.Job.Input.DetectedProperties != nil { - sourceInfo = provider.SourceInfo{ - Duration: time.Duration(aws.Int64Value(resp.Job.Input.DetectedProperties.DurationMillis)) * time.Millisecond, - Height: aws.Int64Value(resp.Job.Input.DetectedProperties.Height), - Width: aws.Int64Value(resp.Job.Input.DetectedProperties.Width), - } - } - statusMessage := "" - if len(resp.Job.Outputs) > 0 { - statusMessage = aws.StringValue(resp.Job.Outputs[0].StatusDetail) - if strings.Contains(statusMessage, ":") { - errorMessage := strings.SplitN(statusMessage, ":", 2)[1] - statusMessage = strings.TrimSpace(errorMessage) - } - } - return &provider.JobStatus{ - ProviderJobID: aws.StringValue(resp.Job.Id), - Status: p.statusMap(aws.StringValue(resp.Job.Status)), - StatusMessage: statusMessage, - Progress: completedJobs / float64(totalJobs) * 100, - ProviderStatus: map[string]interface{}{"outputs": outputs}, - SourceInfo: sourceInfo, - Output: provider.JobOutput{ - Destination: outputDestination, - Files: outputFiles, - }, - }, nil -} - -func (p *awsProvider) getOutputDestination(job *db.Job, awsJob *elastictranscoder.Job) (string, error) { - readPipelineOutput, err := p.c.ReadPipeline(&elastictranscoder.ReadPipelineInput{ - Id: awsJob.PipelineId, - }) - if err != nil { - return "", err - } - return fmt.Sprintf("s3://%s/%s", - aws.StringValue(readPipelineOutput.Pipeline.OutputBucket), - job.ID, - ), nil -} - -func (p *awsProvider) getOutputFiles(job *elastictranscoder.Job) ([]provider.OutputFile, error) { - pipeline, err := p.c.ReadPipeline(&elastictranscoder.ReadPipelineInput{ - Id: job.PipelineId, - }) - if err != nil { - return nil, err - } - files := make([]provider.OutputFile, 0, len(job.Outputs)+len(job.Playlists)) - for _, output := range job.Outputs { - preset, err := p.c.ReadPreset(&elastictranscoder.ReadPresetInput{ - Id: output.PresetId, - }) - if err != nil { - return nil, err - } - filePath := fmt.Sprintf("s3://%s/%s%s", - aws.StringValue(pipeline.Pipeline.OutputBucket), - aws.StringValue(job.OutputKeyPrefix), - aws.StringValue(output.Key), - ) - container := aws.StringValue(preset.Preset.Container) - if container == "ts" { - continue - } - file := provider.OutputFile{ - Path: filePath, - Container: container, - VideoCodec: aws.StringValue(preset.Preset.Video.Codec), - Width: aws.Int64Value(output.Width), - Height: aws.Int64Value(output.Height), - } - files = append(files, file) - } - for _, playlist := range job.Playlists { - filePath := fmt.Sprintf("s3://%s/%s%s", - aws.StringValue(pipeline.Pipeline.OutputBucket), - aws.StringValue(job.OutputKeyPrefix), - aws.StringValue(playlist.Name)+".m3u8", - ) - files = append(files, provider.OutputFile{Path: filePath, Container: "m3u8"}) - } - return files, nil -} - -func (p *awsProvider) statusMap(awsStatus string) provider.Status { - switch awsStatus { - case "Submitted": - return provider.StatusQueued - case "Progressing": - return provider.StatusStarted - case "Complete": - return provider.StatusFinished - case "Canceled": - return provider.StatusCanceled - default: - return provider.StatusFailed - } -} - -func (p *awsProvider) CancelJob(id string) error { - _, err := p.c.CancelJob(&elastictranscoder.CancelJobInput{Id: aws.String(id)}) - return err -} - -func (p *awsProvider) Healthcheck() error { - _, err := p.c.ReadPipeline(&elastictranscoder.ReadPipelineInput{ - Id: aws.String(p.config.PipelineID), - }) - return err -} - -func (p *awsProvider) Capabilities() provider.Capabilities { - return provider.Capabilities{ - InputFormats: []string{"h264"}, - OutputFormats: []string{"mp4", "hls", "webm"}, - Destinations: []string{"s3"}, - } -} - -func elasticTranscoderFactory(cfg *config.Config) (provider.TranscodingProvider, error) { - if cfg.ElasticTranscoder.AccessKeyID == "" || cfg.ElasticTranscoder.SecretAccessKey == "" || cfg.ElasticTranscoder.PipelineID == "" { - return nil, errAWSInvalidConfig - } - creds := credentials.NewStaticCredentials(cfg.ElasticTranscoder.AccessKeyID, cfg.ElasticTranscoder.SecretAccessKey, "") - region := cfg.ElasticTranscoder.Region - if region == "" { - region = defaultAWSRegion - } - awsSession, err := session.NewSession(aws.NewConfig().WithCredentials(creds).WithRegion(region)) - if err != nil { - return nil, err - } - return &awsProvider{ - c: elastictranscoder.New(awsSession), - config: cfg.ElasticTranscoder, - }, nil -} diff --git a/provider/elastictranscoder/aws_fake_transcode_test.go b/provider/elastictranscoder/aws_fake_transcode_test.go deleted file mode 100644 index 54f57f47..00000000 --- a/provider/elastictranscoder/aws_fake_transcode_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package elastictranscoder - -import ( - "crypto/rand" - "errors" - "fmt" - "strings" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/elastictranscoder" -) - -type failure struct { - op string - err error -} - -type fakeElasticTranscoder struct { - *elastictranscoder.ElasticTranscoder - jobs map[string]*elastictranscoder.CreateJobInput - canceledJobs []elastictranscoder.CancelJobInput - failures chan failure -} - -func newFakeElasticTranscoder() *fakeElasticTranscoder { - return &fakeElasticTranscoder{ - ElasticTranscoder: &elastictranscoder.ElasticTranscoder{}, - failures: make(chan failure, 1), - jobs: make(map[string]*elastictranscoder.CreateJobInput), - } -} - -func (c *fakeElasticTranscoder) CreateJob(input *elastictranscoder.CreateJobInput) (*elastictranscoder.CreateJobResponse, error) { - if err := c.getError("CreateJob"); err != nil { - return nil, err - } - input.Input.DetectedProperties = &elastictranscoder.DetectedProperties{ - DurationMillis: aws.Int64(120e3), - FileSize: aws.Int64(60356779), - Width: aws.Int64(1920), - Height: aws.Int64(1080), - } - id := fmt.Sprintf("job-%x", generateID()) - c.jobs[id] = input - return &elastictranscoder.CreateJobResponse{ - Job: &elastictranscoder.Job{ - Id: aws.String(id), - Input: input.Input, - PipelineId: input.PipelineId, - Status: aws.String("Submitted"), - }, - }, nil -} - -func (c *fakeElasticTranscoder) CreatePreset(input *elastictranscoder.CreatePresetInput) (*elastictranscoder.CreatePresetOutput, error) { - presetID := *input.Name + "-abc123" - return &elastictranscoder.CreatePresetOutput{ - Preset: &elastictranscoder.Preset{ - Audio: input.Audio, - Container: input.Container, - Description: input.Description, - Name: input.Name, - Id: &presetID, - Thumbnails: input.Thumbnails, - Video: input.Video, - }, - }, nil -} - -func (c *fakeElasticTranscoder) ReadPreset(input *elastictranscoder.ReadPresetInput) (*elastictranscoder.ReadPresetOutput, error) { - container := "mp4" - codec := "H.264" - if strings.Contains(*input.Id, "hls") { - container = "ts" - } - if strings.Contains(*input.Id, "webm") { - container = "webm" - codec = "VP8" - } - return &elastictranscoder.ReadPresetOutput{ - Preset: &elastictranscoder.Preset{ - Id: input.Id, - Name: input.Id, - Container: aws.String(container), - Video: &elastictranscoder.VideoParameters{Codec: aws.String(codec)}, - }, - }, nil -} - -func (c *fakeElasticTranscoder) ReadJob(input *elastictranscoder.ReadJobInput) (*elastictranscoder.ReadJobOutput, error) { - if err := c.getError("ReadJob"); err != nil { - return nil, err - } - createJobInput, ok := c.jobs[*input.Id] - if !ok { - return nil, errors.New("job not found") - } - outputs := make([]*elastictranscoder.JobOutput, len(createJobInput.Outputs)) - for i, createJobOutput := range createJobInput.Outputs { - outputs[i] = &elastictranscoder.JobOutput{ - Key: createJobOutput.Key, - Status: aws.String("Complete"), - StatusDetail: aws.String("it's finished!"), - PresetId: aws.String(fmt.Sprintf("preset-%s", aws.StringValue(createJobOutput.Key))), - Width: aws.Int64(0), - Height: aws.Int64(720), - } - } - playlists := make([]*elastictranscoder.Playlist, len(createJobInput.Playlists)) - for i, createJobPlaylist := range createJobInput.Playlists { - playlists[i] = &elastictranscoder.Playlist{ - Name: createJobPlaylist.Name, - Format: createJobPlaylist.Format, - OutputKeys: createJobPlaylist.OutputKeys, - } - } - return &elastictranscoder.ReadJobOutput{ - Job: &elastictranscoder.Job{ - Id: input.Id, - Input: createJobInput.Input, - PipelineId: createJobInput.PipelineId, - Status: aws.String("Complete"), - Outputs: outputs, - Playlists: playlists, - }, - }, nil -} - -func (c *fakeElasticTranscoder) ReadPipeline(input *elastictranscoder.ReadPipelineInput) (*elastictranscoder.ReadPipelineOutput, error) { - if err := c.getError("ReadPipeline"); err != nil { - return nil, err - } - return &elastictranscoder.ReadPipelineOutput{ - Pipeline: &elastictranscoder.Pipeline{ - Id: input.Id, - Name: aws.String("nice pipeline"), - OutputBucket: aws.String("some bucket"), - }, - }, nil -} - -func (c *fakeElasticTranscoder) CancelJob(input *elastictranscoder.CancelJobInput) (*elastictranscoder.CancelJobOutput, error) { - if err := c.getError("CancelJob"); err != nil { - return nil, err - } - c.canceledJobs = append(c.canceledJobs, *input) - return &elastictranscoder.CancelJobOutput{}, nil -} - -func (c *fakeElasticTranscoder) prepareFailure(op string, err error) { - c.failures <- failure{op: op, err: err} -} - -func (c *fakeElasticTranscoder) getError(op string) error { - select { - case prepFailure := <-c.failures: - if prepFailure.op == op { - return prepFailure.err - } - c.failures <- prepFailure - default: - } - return nil -} - -func generateID() []byte { - var b [4]byte - rand.Read(b[:]) - return b[:] -} diff --git a/provider/elastictranscoder/aws_test.go b/provider/elastictranscoder/aws_test.go deleted file mode 100644 index a54eac9a..00000000 --- a/provider/elastictranscoder/aws_test.go +++ /dev/null @@ -1,1165 +0,0 @@ -package elastictranscoder - -import ( - "errors" - "os" - "reflect" - "regexp" - "testing" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/service/elastictranscoder" - "github.com/kr/pretty" - "github.com/video-dev/video-transcoding-api/v2/config" - "github.com/video-dev/video-transcoding-api/v2/db" - "github.com/video-dev/video-transcoding-api/v2/provider" -) - -func TestFactoryIsRegistered(t *testing.T) { - _, err := provider.GetProviderFactory(Name) - if err != nil { - t.Fatal(err) - } -} - -func TestElasticTranscoderProvider(t *testing.T) { - cfg := config.Config{ - ElasticTranscoder: &config.ElasticTranscoder{ - AccessKeyID: "AKIANOTREALLY", - SecretAccessKey: "really-secret", - PipelineID: "mypipeline", - Region: "sa-east-1", - }, - } - provider, err := elasticTranscoderFactory(&cfg) - if err != nil { - t.Fatal(err) - } - elasticProvider := provider.(*awsProvider) - if !reflect.DeepEqual(*elasticProvider.config, *cfg.ElasticTranscoder) { - t.Errorf("ElasticTranscoderProvider: did not store the proper config. Want %#v. Got %#v.", cfg.ElasticTranscoder, elasticProvider.config) - } - expectedCreds := credentials.Value{AccessKeyID: "AKIANOTREALLY", SecretAccessKey: "really-secret"} - creds, err := elasticProvider.c.(*elastictranscoder.ElasticTranscoder).Config.Credentials.Get() - if err != nil { - t.Fatal(err) - } - - // provider is not relevant - creds.ProviderName = expectedCreds.ProviderName - if !reflect.DeepEqual(creds, expectedCreds) { - t.Errorf("ElasticTranscoderProvider: wrogn credentials. Want %#v. Got %#v.", expectedCreds, creds) - } - - region := *elasticProvider.c.(*elastictranscoder.ElasticTranscoder).Config.Region - if region != cfg.ElasticTranscoder.Region { - t.Errorf("ElasticTranscoderProvider: wrong region. Want %q. Got %q.", cfg.ElasticTranscoder.Region, region) - } -} - -func TestElasticTranscoderProviderDefaultRegion(t *testing.T) { - cfg := config.Config{ - ElasticTranscoder: &config.ElasticTranscoder{ - AccessKeyID: "AKIANOTREALLY", - SecretAccessKey: "really-secret", - PipelineID: "mypipeline", - }, - } - provider, err := elasticTranscoderFactory(&cfg) - if err != nil { - t.Fatal(err) - } - elasticProvider := provider.(*awsProvider) - if !reflect.DeepEqual(*elasticProvider.config, *cfg.ElasticTranscoder) { - t.Errorf("ElasticTranscoderProvider: did not store the proper config. Want %#v. Got %#v.", cfg.ElasticTranscoder, elasticProvider.config) - } - expectedCreds := credentials.Value{AccessKeyID: "AKIANOTREALLY", SecretAccessKey: "really-secret"} - creds, err := elasticProvider.c.(*elastictranscoder.ElasticTranscoder).Config.Credentials.Get() - if err != nil { - t.Fatal(err) - } - - // provider is not relevant - creds.ProviderName = expectedCreds.ProviderName - if !reflect.DeepEqual(creds, expectedCreds) { - t.Errorf("ElasticTranscoderProvider: wrogn credentials. Want %#v. Got %#v.", expectedCreds, creds) - } - - region := *elasticProvider.c.(*elastictranscoder.ElasticTranscoder).Config.Region - if region != "us-east-1" { - t.Errorf("ElasticTranscoderProvider: wrong region. Want %q. Got %q.", "us-east-1", region) - } -} - -func TestElasticTranscoderProviderValidation(t *testing.T) { - tests := []struct { - accessKeyID string - secretAccessKey string - pipelineID string - }{ - {"", "", ""}, - {"AKIANOTREALLY", "", ""}, - {"", "very-secret", ""}, - {"", "", "superpipeline"}, - {"AKIANOTREALLY", "very-secret", ""}, - } - for _, test := range tests { - cfg := config.Config{ - ElasticTranscoder: &config.ElasticTranscoder{ - AccessKeyID: test.accessKeyID, - SecretAccessKey: test.secretAccessKey, - PipelineID: test.pipelineID, - }, - } - provider, err := elasticTranscoderFactory(&cfg) - if provider != nil { - t.Errorf("Got unexpected non-nil provider: %#v", provider) - } - if err != errAWSInvalidConfig { - t.Errorf("Wrong error returned. Want errAWSInvalidConfig. Got %#v", err) - } - } -} - -func TestAWSTranscode(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "dir/file.mov" - outputs := []db.TranscodeOutput{ - { - FileName: "output-720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0001", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - { - FileName: "output-720p.webm", - Preset: db.PresetMap{ - Name: "webm_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0002", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "webm"}, - }, - }, - { - FileName: "output-1080p.mov", - Preset: db.PresetMap{ - Name: "mov_1080p", - ProviderMapping: map[string]string{ - Name: "93239832-0003", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mov"}, - }, - }, - } - - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{}, - }) - if err != nil { - t.Fatal(err) - } - if m, _ := regexp.MatchString(`^job-[a-f0-9]{8}$`, jobStatus.ProviderJobID); !m { - t.Errorf("Elastic Transcoder: invalid id returned - %q", jobStatus.ProviderJobID) - } - if jobStatus.Status != provider.StatusQueued { - t.Errorf("Elastic Transcoder: wrong status returned. Want queued. Got %v", jobStatus.Status) - } - if jobStatus.ProviderName != Name { - t.Errorf("Elastic Transcoder: wrong provider name returned. Want %q. Got %q", Name, jobStatus.ProviderName) - } - - if len(fakeTranscoder.jobs) != 1 { - t.Fatal("Did not send any job request to the server.") - } - jobInput := fakeTranscoder.jobs[jobStatus.ProviderJobID] - - expectedJobInput := elastictranscoder.CreateJobInput{ - PipelineId: aws.String("mypipeline"), - Input: &elastictranscoder.JobInput{ - Key: aws.String(source), - DetectedProperties: &elastictranscoder.DetectedProperties{ - DurationMillis: aws.Int64(120e3), - FileSize: aws.Int64(60356779), - Height: aws.Int64(1080), - Width: aws.Int64(1920), - }, - }, - Outputs: []*elastictranscoder.CreateJobOutput{ - {PresetId: aws.String("93239832-0001"), Key: aws.String("job-123/output-720p.mp4")}, - {PresetId: aws.String("93239832-0002"), Key: aws.String("job-123/output-720p.webm")}, - {PresetId: aws.String("93239832-0003"), Key: aws.String("job-123/output-1080p.mov")}, - }, - } - if !reflect.DeepEqual(*jobInput, expectedJobInput) { - t.Errorf("Elastic Transcoder: wrong input\nWant %#v\nGot %#v", expectedJobInput, *jobInput) - } -} - -func TestAWSTranscodeAdaptiveStreaming(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "dir/file.mov" - outputs := []db.TranscodeOutput{ - { - FileName: "output_360p_hls/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_360p", - ProviderMapping: map[string]string{ - Name: "93239832-0001-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - { - FileName: "output_480p_hls/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_480p", - ProviderMapping: map[string]string{ - Name: "93239832-0002-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - { - FileName: "output_720p_hls/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0003-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - } - - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{ - PlaylistFileName: "video.m3u8", - Protocol: "asdf", - SegmentDuration: 3, - }, - }) - if err != nil { - t.Fatal(err) - } - if m, _ := regexp.MatchString(`^job-[a-f0-9]{8}$`, jobStatus.ProviderJobID); !m { - t.Errorf("Elastic Transcoder: invalid id returned - %q", jobStatus.ProviderJobID) - } - if jobStatus.Status != provider.StatusQueued { - t.Errorf("Elastic Transcoder: wrong status returned. Want queued. Got %v", jobStatus.Status) - } - if jobStatus.ProviderName != Name { - t.Errorf("Elastic Transcoder: wrong provider name returned. Want %q. Got %q", Name, jobStatus.ProviderName) - } - - if len(fakeTranscoder.jobs) != 1 { - t.Fatal("Did not send any job request to the server.") - } - jobInput := fakeTranscoder.jobs[jobStatus.ProviderJobID] - - expectedJobInput := elastictranscoder.CreateJobInput{ - PipelineId: aws.String("mypipeline"), - Input: &elastictranscoder.JobInput{ - Key: aws.String(source), - DetectedProperties: &elastictranscoder.DetectedProperties{ - DurationMillis: aws.Int64(120e3), - FileSize: aws.Int64(60356779), - Height: aws.Int64(1080), - Width: aws.Int64(1920), - }, - }, - Outputs: []*elastictranscoder.CreateJobOutput{ - {PresetId: aws.String("93239832-0001-hls"), Key: aws.String("job-123/output_360p_hls/video"), SegmentDuration: aws.String("3")}, - {PresetId: aws.String("93239832-0002-hls"), Key: aws.String("job-123/output_480p_hls/video"), SegmentDuration: aws.String("3")}, - {PresetId: aws.String("93239832-0003-hls"), Key: aws.String("job-123/output_720p_hls/video"), SegmentDuration: aws.String("3")}, - }, - Playlists: []*elastictranscoder.CreateJobPlaylist{ - { - Format: aws.String("HLSv3"), - Name: aws.String("job-123/video"), - OutputKeys: []*string{ - aws.String("job-123/output_360p_hls/video"), - aws.String("job-123/output_480p_hls/video"), - aws.String("job-123/output_720p_hls/video"), - }, - }, - }, - } - if !reflect.DeepEqual(*jobInput, expectedJobInput) { - t.Errorf("Elastic Transcoder: wrong input\nWant %#v\nGot %#v", expectedJobInput, *jobInput) - } -} - -func TestAWSTranscodeAdaptiveAndNonAdaptiveStreaming(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "dir/file.mov" - outputs := []db.TranscodeOutput{ - { - FileName: "hls/output_hls_360p/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_360p", - ProviderMapping: map[string]string{ - Name: "93239832-0001-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - { - FileName: "hls/output_hls_480p/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_480p", - ProviderMapping: map[string]string{ - Name: "93239832-0002-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - { - FileName: "hls/output_hls_720p/video.m3u8", - Preset: db.PresetMap{ - Name: "hls_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0003-hls", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "hls"}, - }, - }, - { - FileName: "output_720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0004", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - } - - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{ - PlaylistFileName: "hls/video.m3u8", - Protocol: "asdf", - SegmentDuration: 3, - }, - }) - if err != nil { - t.Fatal(err) - } - if m, _ := regexp.MatchString(`^job-[a-f0-9]{8}$`, jobStatus.ProviderJobID); !m { - t.Errorf("Elastic Transcoder: invalid id returned - %q", jobStatus.ProviderJobID) - } - if jobStatus.Status != provider.StatusQueued { - t.Errorf("Elastic Transcoder: wrong status returned. Want queued. Got %v", jobStatus.Status) - } - if jobStatus.ProviderName != Name { - t.Errorf("Elastic Transcoder: wrong provider name returned. Want %q. Got %q", Name, jobStatus.ProviderName) - } - - if len(fakeTranscoder.jobs) != 1 { - t.Fatal("Did not send any job request to the server.") - } - jobInput := fakeTranscoder.jobs[jobStatus.ProviderJobID] - - expectedJobInput := elastictranscoder.CreateJobInput{ - PipelineId: aws.String("mypipeline"), - Input: &elastictranscoder.JobInput{ - Key: aws.String(source), - DetectedProperties: &elastictranscoder.DetectedProperties{ - DurationMillis: aws.Int64(120e3), - FileSize: aws.Int64(60356779), - Height: aws.Int64(1080), - Width: aws.Int64(1920), - }, - }, - Outputs: []*elastictranscoder.CreateJobOutput{ - {PresetId: aws.String("93239832-0001-hls"), Key: aws.String("job-123/hls/output_hls_360p/video"), SegmentDuration: aws.String("3")}, - {PresetId: aws.String("93239832-0002-hls"), Key: aws.String("job-123/hls/output_hls_480p/video"), SegmentDuration: aws.String("3")}, - {PresetId: aws.String("93239832-0003-hls"), Key: aws.String("job-123/hls/output_hls_720p/video"), SegmentDuration: aws.String("3")}, - {PresetId: aws.String("93239832-0004"), Key: aws.String("job-123/output_720p.mp4")}, - }, - Playlists: []*elastictranscoder.CreateJobPlaylist{ - { - Format: aws.String("HLSv3"), - Name: aws.String("job-123/hls/video"), - OutputKeys: []*string{ - aws.String("job-123/hls/output_hls_360p/video"), - aws.String("job-123/hls/output_hls_480p/video"), - aws.String("job-123/hls/output_hls_720p/video"), - }, - }, - }, - } - if !reflect.DeepEqual(*jobInput, expectedJobInput) { - t.Errorf("Elastic Transcoder: wrong input\nWant %#v\nGot %#v", expectedJobInput, *jobInput) - } -} - -func TestAWSTranscodeNormalizedSource(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "s3://bucketname/some/dir/with/subdir/file.mov" - outputs := []db.TranscodeOutput{ - { - FileName: "output_720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0001", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - { - FileName: "output_1080p.webm", - Preset: db.PresetMap{ - Name: "webm_1080p", - ProviderMapping: map[string]string{ - Name: "93239832-0002", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "webm"}, - }, - }, - } - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-1", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{}, - }) - if err != nil { - t.Fatal(err) - } - if m, _ := regexp.MatchString(`^job-[a-f0-9]{8}$`, jobStatus.ProviderJobID); !m { - t.Errorf("Elastic Transcoder: invalid id returned - %q", jobStatus.ProviderJobID) - } - if jobStatus.Status != provider.StatusQueued { - t.Errorf("Elastic Transcoder: wrong status returned. Want queued. Got %v", jobStatus.Status) - } - if jobStatus.ProviderName != Name { - t.Errorf("Elastic Transcoder: wrong provider name returned. Want %q. Got %q", Name, jobStatus.ProviderName) - } - - if len(fakeTranscoder.jobs) != 1 { - t.Fatal("Did not send any job request to the server.") - } - jobInput := fakeTranscoder.jobs[jobStatus.ProviderJobID] - - expectedJobInput := elastictranscoder.CreateJobInput{ - PipelineId: aws.String("mypipeline"), - Input: &elastictranscoder.JobInput{ - Key: aws.String("some/dir/with/subdir/file.mov"), - DetectedProperties: &elastictranscoder.DetectedProperties{ - DurationMillis: aws.Int64(120e3), - FileSize: aws.Int64(60356779), - Height: aws.Int64(1080), - Width: aws.Int64(1920), - }, - }, - Outputs: []*elastictranscoder.CreateJobOutput{ - {PresetId: aws.String("93239832-0001"), Key: aws.String("job-1/output_720p.mp4")}, - {PresetId: aws.String("93239832-0002"), Key: aws.String("job-1/output_1080p.webm")}, - }, - } - if !reflect.DeepEqual(*jobInput, expectedJobInput) { - t.Errorf("Elastic Transcoder: wrong input\nWant %#v\nGot %#v", expectedJobInput, *jobInput) - } -} - -func TestAWSTranscodePresetNotFound(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "s3://bucketname/some/dir/with/subdir/file.mov" - outputs := []db.TranscodeOutput{ - { - FileName: "output_720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{"other": "irrelevant"}, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - } - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{}, - }) - if err != provider.ErrPresetMapNotFound { - t.Errorf("Wrong error returned. Want %#v. Got %#v", provider.ErrPresetMapNotFound, err) - } - if jobStatus != nil { - t.Errorf("Got unexpected non-nil JobStatus: %#v", jobStatus) - } -} - -func TestAWSTranscodeAWSFailureInAmazon(t *testing.T) { - prepErr := errors.New("something went wrong") - fakeTranscoder := newFakeElasticTranscoder() - fakeTranscoder.prepareFailure("CreateJob", prepErr) - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - source := "dir/file.mp4" - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - StreamingParams: db.StreamingParams{}, - }) - if jobStatus != nil { - t.Errorf("Got unexpected non-nil status: %#v", jobStatus) - } - if err != prepErr { - t.Errorf("Got wrong error. Want %q. Got %q", prepErr.Error(), err.Error()) - } -} - -func TestAWSJobStatus(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - outputs := []db.TranscodeOutput{ - { - FileName: "output_720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0001", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - { - FileName: "output_720p.webm", - Preset: db.PresetMap{ - Name: "webm_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0002", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "webm"}, - }, - }, - { - FileName: "hls/output_720p.m3u8", - Preset: db.PresetMap{ - Name: "hls_720p", - ProviderMapping: map[string]string{ - Name: "hls-93239832-0003", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "m3u8"}, - }, - }, - } - source := "dir/file.mov" - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: source, - Outputs: outputs, - StreamingParams: db.StreamingParams{ - PlaylistFileName: "hls/index.m3u8", - SegmentDuration: 3, - Protocol: "hls", - }, - }) - if err != nil { - t.Fatal(err) - } - jobStatus, err = prov.JobStatus(&db.Job{ID: "job-123", ProviderJobID: jobStatus.ProviderJobID}) - if err != nil { - t.Fatal(err) - } - expectedJobStatus := provider.JobStatus{ - ProviderJobID: jobStatus.ProviderJobID, - Status: provider.StatusFinished, - StatusMessage: "it's finished!", - Progress: 100, - ProviderStatus: map[string]interface{}{ - "outputs": map[string]interface{}{ - "job-123/output_720p.mp4": "it's finished!", - "job-123/output_720p.webm": "it's finished!", - "job-123/hls/output_720p": "it's finished!", - }, - }, - SourceInfo: provider.SourceInfo{ - Duration: 120 * time.Second, - Width: 1920, - Height: 1080, - }, - Output: provider.JobOutput{ - Destination: "s3://some bucket/job-123", - Files: []provider.OutputFile{ - { - Path: "s3://some bucket/job-123/output_720p.mp4", - Container: "mp4", - VideoCodec: "H.264", - Width: 0, - Height: 720, - }, - { - Path: "s3://some bucket/job-123/output_720p.webm", - Container: "webm", - VideoCodec: "VP8", - Width: 0, - Height: 720, - }, - { - Path: "s3://some bucket/job-123/hls/index.m3u8", - Container: "m3u8", - }, - }, - }, - } - if !reflect.DeepEqual(*jobStatus, expectedJobStatus) { - t.Errorf("Wrong JobStatus\nWant %#v\nGot %#v", expectedJobStatus, *jobStatus) - } -} - -func TestAWSJobStatusNoDetectedProperties(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - outputs := []db.TranscodeOutput{ - { - FileName: "output_720p.mp4", - Preset: db.PresetMap{ - Name: "mp4_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0001", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "mp4"}, - }, - }, - { - FileName: "output_720p.webm", - Preset: db.PresetMap{ - Name: "webm_720p", - ProviderMapping: map[string]string{ - Name: "93239832-0002", - "other": "irrelevant", - }, - OutputOpts: db.OutputOptions{Extension: "webm"}, - }, - }, - } - jobStatus, err := prov.Transcode(&db.Job{ - ID: "job-123", - SourceMedia: "dir/file.mov", - Outputs: outputs, - StreamingParams: db.StreamingParams{}, - }) - if err != nil { - t.Fatal(err) - } - fakeTranscoder.jobs[jobStatus.ProviderJobID].Input.DetectedProperties = nil - jobStatus, err = prov.JobStatus(&db.Job{ID: "job-123", ProviderJobID: jobStatus.ProviderJobID}) - if err != nil { - t.Fatal(err) - } - expectedJobStatus := provider.JobStatus{ - ProviderJobID: jobStatus.ProviderJobID, - Status: provider.StatusFinished, - StatusMessage: "it's finished!", - Progress: 100, - ProviderStatus: map[string]interface{}{ - "outputs": map[string]interface{}{ - "job-123/output_720p.mp4": "it's finished!", - "job-123/output_720p.webm": "it's finished!", - }, - }, - Output: provider.JobOutput{ - Destination: "s3://some bucket/job-123", - Files: []provider.OutputFile{ - { - Path: "s3://some bucket/job-123/output_720p.mp4", - Container: "mp4", - VideoCodec: "H.264", - Width: 0, - Height: 720, - }, - { - Path: "s3://some bucket/job-123/output_720p.webm", - Container: "webm", - VideoCodec: "VP8", - Width: 0, - Height: 720, - }, - }, - }, - } - if !reflect.DeepEqual(*jobStatus, expectedJobStatus) { - t.Errorf("Wrong JobStatus\nWant %#v\nGot %#v", expectedJobStatus, *jobStatus) - } -} - -func TestAWSCreatePreset(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - - inputPreset := db.Preset{ - Name: "preset_name", - Description: "description here", - Container: "mp4", - RateControl: "VBR", - Video: db.VideoPreset{ - Profile: "Main", - ProfileLevel: "3.1", - Height: "720", - Codec: "h264", - Bitrate: "2500000", - GopSize: "90", - GopMode: "fixed", - InterlaceMode: "progressive", - }, - Audio: db.AudioPreset{ - Codec: "aac", - Bitrate: "64000", - }, - } - - presetID, _ := prov.CreatePreset(inputPreset) - - if !reflect.DeepEqual(presetID, "preset_name-abc123") { - t.Errorf("CreatePreset: want %s. Got %s", presetID, "preset_name-abc123") - } -} - -func TestCreateVideoPreset(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - tests := []struct { - givenTestCase string - givenPreset db.Preset - expectedVideoParams *elastictranscoder.VideoParameters - }{ - { - "H.264 preset", - db.Preset{ - Container: "m3u8", - Video: db.VideoPreset{ - Profile: "Main", - ProfileLevel: "3.1", - Codec: "h264", - }, - }, - &elastictranscoder.VideoParameters{ - BitRate: aws.String("0"), - Codec: aws.String("H.264"), - CodecOptions: map[string]*string{ - "MaxReferenceFrames": aws.String("2"), - "Profile": aws.String("main"), - "Level": aws.String("3.1"), - }, - DisplayAspectRatio: aws.String("auto"), - FrameRate: aws.String("auto"), - KeyframesMaxDist: aws.String(""), - MaxHeight: aws.String("auto"), - MaxWidth: aws.String("auto"), - PaddingPolicy: aws.String("Pad"), - SizingPolicy: aws.String("Fill"), - }, - }, - { - "WEBM vp8 preset", - db.Preset{ - Container: "webm", - Video: db.VideoPreset{ - Codec: "vp8", - GopSize: "90", - }, - }, - &elastictranscoder.VideoParameters{ - BitRate: aws.String("0"), - Codec: aws.String("vp8"), - CodecOptions: map[string]*string{ - "Profile": aws.String("0"), - }, - DisplayAspectRatio: aws.String("auto"), - FrameRate: aws.String("auto"), - KeyframesMaxDist: aws.String("90"), - MaxHeight: aws.String("auto"), - MaxWidth: aws.String("auto"), - PaddingPolicy: aws.String("Pad"), - SizingPolicy: aws.String("Fill"), - }, - }, - { - "WEBM vp9 preset", - db.Preset{ - Container: "webm", - Video: db.VideoPreset{ - Codec: "vp9", - GopSize: "90", - }, - }, - &elastictranscoder.VideoParameters{ - BitRate: aws.String("0"), - Codec: aws.String("vp9"), - CodecOptions: map[string]*string{ - "Profile": aws.String("0"), - }, - DisplayAspectRatio: aws.String("auto"), - FrameRate: aws.String("auto"), - KeyframesMaxDist: aws.String("90"), - MaxHeight: aws.String("auto"), - MaxWidth: aws.String("auto"), - PaddingPolicy: aws.String("Pad"), - SizingPolicy: aws.String("Fill"), - }, - }, - { - "MP4 preset", - db.Preset{ - Container: "mp4", - Video: db.VideoPreset{ - Profile: "Main", - ProfileLevel: "3.1", - Codec: "h264", - GopSize: "90", - }, - }, - &elastictranscoder.VideoParameters{ - BitRate: aws.String("0"), - Codec: aws.String("H.264"), - CodecOptions: map[string]*string{ - "MaxReferenceFrames": aws.String("2"), - "Profile": aws.String("main"), - "Level": aws.String("3.1"), - }, - DisplayAspectRatio: aws.String("auto"), - FrameRate: aws.String("auto"), - KeyframesMaxDist: aws.String("90"), - MaxHeight: aws.String("auto"), - MaxWidth: aws.String("auto"), - PaddingPolicy: aws.String("Pad"), - SizingPolicy: aws.String("Fill"), - }, - }, - } - for _, test := range tests { - videoParams := prov.createVideoPreset(test.givenPreset) - if !reflect.DeepEqual(test.expectedVideoParams, videoParams) { - t.Errorf("%s: CreateVideoPreset: want %s. Got %s", test.givenTestCase, test.expectedVideoParams, videoParams) - pretty.Fdiff(os.Stderr, videoParams, test.expectedVideoParams) - } - } -} - -func TestCreateAudioPreset(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - tests := []struct { - givenTestCase string - givenPreset db.Preset - expectedAudioParams *elastictranscoder.AudioParameters - }{ - { - "AAC preset", - db.Preset{ - Audio: db.AudioPreset{ - Codec: "aac", - }, - }, - &elastictranscoder.AudioParameters{ - BitRate: aws.String("0"), - Channels: aws.String("auto"), - Codec: aws.String("AAC"), - SampleRate: aws.String("auto"), - }, - }, - { - "libvorbis preset", - db.Preset{ - Audio: db.AudioPreset{ - Codec: "libvorbis", - }, - }, - &elastictranscoder.AudioParameters{ - BitRate: aws.String("0"), - Channels: aws.String("auto"), - Codec: aws.String("vorbis"), - SampleRate: aws.String("auto"), - }, - }, - } - for _, test := range tests { - audioParams := prov.createAudioPreset(test.givenPreset) - if !reflect.DeepEqual(test.expectedAudioParams, audioParams) { - t.Errorf("%s: CreateAudioPreset: want %s. Got %s", test.givenTestCase, test.expectedAudioParams, audioParams) - pretty.Fdiff(os.Stderr, audioParams, test.expectedAudioParams) - } - } -} - -func TestAWSJobStatusNotFound(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - provider := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - jobStatus, err := provider.JobStatus(&db.Job{ProviderJobID: "idk"}) - if err == nil { - t.Fatal("Got unexpected error") - } - expectedErrMsg := "job not found" - if err.Error() != expectedErrMsg { - t.Errorf("Got wrong error message. Want %q. Got %q", expectedErrMsg, err.Error()) - } - if jobStatus != nil { - t.Errorf("Got unexpected non-nil JobStatus: %#v", jobStatus) - } -} - -func TestAWSJobStatusInternalError(t *testing.T) { - prepErr := errors.New("failed to get job status") - fakeTranscoder := newFakeElasticTranscoder() - fakeTranscoder.prepareFailure("ReadJob", prepErr) - provider := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - jobStatus, err := provider.JobStatus(&db.Job{ProviderJobID: "idk"}) - if jobStatus != nil { - t.Errorf("Got unexpected non-nil JobStatus: %#v", jobStatus) - } - if err != prepErr { - t.Errorf("Got wrong error. Want %q. Got %q", prepErr.Error(), err.Error()) - } -} - -func TestAWSStatusMap(t *testing.T) { - tests := []struct { - input string - output provider.Status - }{ - {"Submitted", provider.StatusQueued}, - {"Progressing", provider.StatusStarted}, - {"Canceled", provider.StatusCanceled}, - {"Error", provider.StatusFailed}, - {"Complete", provider.StatusFinished}, - {"unknown", provider.StatusFailed}, - } - var prov awsProvider - for _, test := range tests { - result := prov.statusMap(test.input) - if result != test.output { - t.Errorf("statusMap(%q): wrong result. Want %q. Got %q", test.input, test.output, result) - } - } -} - -func TestCancelJob(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - prov := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - err := prov.CancelJob("idk") - if err != nil { - t.Fatal(err) - } - if id := aws.StringValue(fakeTranscoder.canceledJobs[0].Id); id != "idk" { - t.Errorf("wrong job canceled. Want %q. Got %q", "idk", id) - } -} - -func TestCancelJobInternalError(t *testing.T) { - prepErr := errors.New("failed to cancel job") - fakeTranscoder := newFakeElasticTranscoder() - fakeTranscoder.prepareFailure("CancelJob", prepErr) - provider := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - err := provider.CancelJob("idk") - if err != prepErr { - t.Errorf("wrong error returned.\nWant %#v\nGot %#v", prepErr, err) - } -} - -func TestHealthcheck(t *testing.T) { - fakeTranscoder := newFakeElasticTranscoder() - provider := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - err := provider.Healthcheck() - if err != nil { - t.Fatal(err) - } -} - -func TestHealthcheckFailure(t *testing.T) { - prepErr := errors.New("something went wrong") - fakeTranscoder := newFakeElasticTranscoder() - fakeTranscoder.prepareFailure("ReadPipeline", prepErr) - provider := &awsProvider{ - c: fakeTranscoder, - config: &config.ElasticTranscoder{ - AccessKeyID: "AKIA", - SecretAccessKey: "secret", - Region: "sa-east-1", - PipelineID: "mypipeline", - }, - } - err := provider.Healthcheck() - if err != prepErr { - t.Errorf("Wrong error returned. Want %#v.Got %#v", prepErr, err) - } -} - -func TestCapabilities(t *testing.T) { - var prov awsProvider - expected := provider.Capabilities{ - InputFormats: []string{"h264"}, - OutputFormats: []string{"mp4", "hls", "webm"}, - Destinations: []string{"s3"}, - } - cap := prov.Capabilities() - if !reflect.DeepEqual(cap, expected) { - t.Errorf("Capabilities: want %#v. Got %#v", expected, cap) - } -}