Skip to content

Commit

Permalink
Add new-plan helper sub-command
Browse files Browse the repository at this point in the history
new-plan subcommand will create a sample plan template, once generated
it should be customized further by the user. It serves as a starting
point to kick-off plan creation.
  • Loading branch information
jurisevo committed Feb 29, 2024
1 parent fbea042 commit b032537
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 4 deletions.
1 change: 1 addition & 0 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ implemented:
- `ease bitrate`
- `ease vqmplot`
- `ease dump-conf`
- `ease new-plan`
- `ease version`

## Intended usage workflow
Expand Down
21 changes: 17 additions & 4 deletions internal/encoding/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,18 @@ type Scheme struct {
CommandTpl string
}

// schemeJson maps directly to JSON representation.
type schemeJson struct {
Name string
CommandTpl []string
}

// UnmarshalJSON implement Unmarshaler interface for Scheme type.
func (s *Scheme) UnmarshalJSON(data []byte) error {
// Since JSON Scheme.CommandTpl is a string array we create a "temporary"
// struct that will be used to decode JSON, we will use this struct to
// construct Scheme fields.
scheme := struct {
Name string
CommandTpl []string
}{}
scheme := schemeJson{}
if err := json.Unmarshal(data, &scheme); err != nil {
return err
}
Expand All @@ -139,6 +142,16 @@ func (s *Scheme) UnmarshalJSON(data []byte) error {
return nil
}

// MarshalJSON implement Marshaler interface for Scheme type.
func (s *Scheme) MarshalJSON() ([]byte, error) {
// Need to convert to implementation that supports multiline string.
scheme := schemeJson{
Name: s.Name,
CommandTpl: []string{s.CommandTpl},
}
return json.Marshal(scheme)
}

// Expand will generate complete encoding commands based on provided "context".
//
// "Context" being input/source files and output directory.
Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The commands are:
vqmplot create plot for given metric from libvmaf JSON report
bitrate create bitrate plot of given video file
dump-conf output actual application configuration
new-plan helper to create anew plan configuration file
version print ease version and exit
Use "ease <command> -h|-help" for more information about command.`
Expand All @@ -45,6 +46,8 @@ Use "ease <command> -h|-help" for more information about command.`
return CreateBitrateCommand().Run(args[1:])
case "dump-conf", "dump":
return CreateDumpConfCommand().Run(args[1:])
case "new-plan", "newp":
return CreateNewPlanCommand().Run(args[1:])
case "version":
printVersion()
return nil
Expand Down
114 changes: 114 additions & 0 deletions new_plan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright ©2022 Evolution. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// ease tool's new-plan subcommand implementation.
package main

import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strings"

"github.com/evolution-gaming/ease/internal/encoding"
)

// inputFiles implements flag.Value interface.
type inputFiles []string

func (i *inputFiles) String() string {
return strings.Join(*i, ", ")
}

func (i *inputFiles) Set(value string) error {
*i = append(*i, value)
return nil
}

func CreateNewPlanCommand() *NewPlanApp {
longHelp := `Subcommand "new-plan" helps create a new plan configuration file template.
Examples:
ease new-plan -i path/to/input/video.mp4 -o plan.json
ease new-plan -i video1.mp4 -i video2.mp4 -o plan.json`

app := &NewPlanApp{
fs: flag.NewFlagSet("new-plan", flag.ContinueOnError),
}
app.fs.StringVar(&app.flOutFile, "o", "", "Output file (stdout by default).")
app.fs.Var(&app.flInputFiles, "i", "Source video files. Use multiple times for multiple files.")

app.fs.Usage = func() {
printSubCommandUsage(longHelp, app.fs)
}

return app
}

type NewPlanApp struct {
// FlagSet instance
fs *flag.FlagSet
// Output file to save plot to
flOutFile string
// Video input files
flInputFiles inputFiles
}

func (a *NewPlanApp) Run(args []string) error {
if err := a.fs.Parse(args); err != nil {
return &AppError{
msg: "usage error",
exitCode: 2,
}
}

// In case no input video provided we will use some placeholder string.
if len(a.flInputFiles) == 0 {
a.flInputFiles = []string{"path/to/source/video.mp4"}
}

// Create a PlanConfig instance and populate it with some data. From this we shall
// crate a JSON plan.
pc := encoding.PlanConfig{}
pc.Inputs = a.flInputFiles
pc.Schemes = []encoding.Scheme{
{
Name: "encode1",
CommandTpl: "ffmpeg -i %INPUT% -c:v libx264 -preset fast -crf 23 %OUTPUT%.mp4",
},
{
Name: "encode2",
CommandTpl: "ffmpeg -i %INPUT% -c:v libx264 -preset faster -crf 25 %OUTPUT%.mp4",
},
}

var out io.Writer
switch a.flOutFile {
case "":
out = os.Stdout
default:
fd, err := os.Create(a.flOutFile)
if err != nil {
return &AppError{
msg: fmt.Sprintf("output file error: %s", err),
exitCode: 1,
}
}
out = fd
}

e := json.NewEncoder(out)
e.SetIndent("", " ")
if err := e.Encode(pc); err != nil {
return &AppError{
msg: "JSON marshal error",
exitCode: 1,
}
}

return nil
}

0 comments on commit b032537

Please sign in to comment.