Skip to content
Snippets Groups Projects
Commit 77031ac6 authored by ale's avatar ale
Browse files

more controls on encoding params; fix radioctl edit-mount

parent d9e1bf1f
Branches
No related tags found
No related merge requests found
......@@ -50,22 +50,51 @@ type EncodingParams struct {
BitRate int
SampleRate int
Channels int
StereoMode string
Quality float64
}
// NewEncodingParams sets some default values.
func NewEncodingParams() *EncodingParams {
return &EncodingParams{
SampleRate: 44100,
Channels: 2,
Quality: -1,
}
}
func (p *EncodingParams) Valid() error {
if p.Format == "" {
switch p.Format {
case "mp3", "mp3.cbr", "mp3.abr", "vorbis.cbr", "vorbis.abr":
if p.BitRate == 0 {
return errors.New("bitrate not specified")
}
case "mp3.vbr":
if p.Quality < 0 || p.Quality > 9 {
return errors.New("quality must be in range [0, 9]")
}
case "vorbis":
if p.Quality < -0.2 || p.Quality > 1 {
return errors.New("quality must be in range [-0.2, 1]")
}
case "":
return errors.New("format not specified")
default:
return fmt.Errorf("unknown format \"%s\"", p.Format)
}
if p.SampleRate == 0 {
return errors.New("sample rate not specified")
}
if p.BitRate == 0 && p.Quality == 0 {
return errors.New("either bitrate or quality must be specified")
}
if p.Channels < 1 {
if p.Channels < 1 || p.Channels > 2 {
return errors.New("bad number of channels")
}
if p.Channels > 1 {
switch p.StereoMode {
case "", "stereo", "joint_stereo", "default":
default:
return fmt.Errorf("unknown stereo mode \"%s\"", p.StereoMode)
}
}
return nil
}
......
......@@ -8,6 +8,7 @@ import (
"log"
"net/url"
"os"
"strconv"
"strings"
"git.autistici.org/ale/autoradio"
......@@ -18,6 +19,97 @@ import (
// Format for output of structured data.
var outputFormat = flag.String("format", "txt", "Output format for structured data (json, txt)")
type optionalValue struct {
isSet bool
}
func (v *optionalValue) IsSet() bool {
return v.isSet
}
type stringOptionalValue struct {
*optionalValue
value string
}
func newStringOptionalValue() *stringOptionalValue {
return &stringOptionalValue{}
}
func (v *stringOptionalValue) Set(val string) error {
v.isSet = true
v.value = val
return nil
}
func (v *stringOptionalValue) Value() string {
return v.value
}
func (v *stringOptionalValue) String() string {
return v.value
}
func (v *stringOptionalValue) Get() interface{} {
return v.value
}
type intOptionalValue struct {
*optionalValue
value int
}
func newIntOptionalValue() *intOptionalValue {
return &intOptionalValue{}
}
func (v *intOptionalValue) Set(val string) error {
ival, err := strconv.Atoi(val)
v.value = ival
v.isSet = true
return err
}
func (v *intOptionalValue) Value() int {
return v.value
}
func (v *intOptionalValue) String() string {
return fmt.Sprintf("%v", v.value)
}
func (v *intOptionalValue) Get() interface{} {
return v.value
}
type floatOptionalValue struct {
*optionalValue
value float64
}
func newFloatOptionalValue() *floatOptionalValue {
return &floatOptionalValue{}
}
func (v *floatOptionalValue) Set(val string) error {
fval, err := strconv.ParseFloat(val, 64)
v.value = fval
v.isSet = true
return err
}
func (v *floatOptionalValue) Value() float64 {
return v.value
}
func (v *floatOptionalValue) String() string {
return fmt.Sprintf("%v", v.value)
}
func (v *floatOptionalValue) Get() interface{} {
return v.value
}
type HasAddFlags interface {
AddFlags(*gonutsflag.FlagSet)
}
......@@ -99,15 +191,6 @@ func printMount(m *autoradio.Mount) {
}
}
func addEncodingFlags(f *gonutsflag.FlagSet, p *autoradio.EncodingParams) {
f.StringVar(&p.SourceName, "source", "", "Source mountpoint")
f.StringVar(&p.Format, "codec", "", "Encoding format")
f.IntVar(&p.BitRate, "bitrate", 0, "Bitrate (kbps)")
f.IntVar(&p.SampleRate, "samplerate", 0, "Sample rate (Hz)")
f.IntVar(&p.Channels, "channels", 2, "Number of channels")
f.Float64Var(&p.Quality, "quality", 0, "Quality (alternatively to bitrate for some encoders)")
}
func mountExists(name string, client *autoradio.Client) bool {
m, _ := client.GetMount(name)
return m != nil
......@@ -184,16 +267,26 @@ func newCreateTranscodingMountCommand() *createTranscodingMountCommand {
return &createTranscodingMountCommand{
BaseCommand: BaseCommand{
UsageLine: "create-transcoding-mount <path>",
Short: "Create a transcoded mount",
Short: "Create a transcoding mount",
Long: `
Create a new stream that will transcode the parent stream with
different encoding parameters.
`,
},
params: &autoradio.EncodingParams{},
params: autoradio.NewEncodingParams(),
}
}
func addEncodingFlags(f *gonutsflag.FlagSet, p *autoradio.EncodingParams) {
f.StringVar(&p.SourceName, "source", "", "Source mountpoint")
f.StringVar(&p.Format, "codec", p.Format, "Encoding format")
f.Float64Var(&p.Quality, "quality", p.Quality, "Quality (for VBR encoders)")
f.IntVar(&p.BitRate, "bitrate", p.BitRate, "Bitrate (kbps)")
f.IntVar(&p.SampleRate, "samplerate", p.SampleRate, "Sample rate (Hz)")
f.IntVar(&p.Channels, "channels", p.Channels, "Number of channels")
f.StringVar(&p.StereoMode, "stereo-mode", p.StereoMode, "Stereo mode for mp3 codec (stereo, joint_stereo)")
}
func (cmd *createTranscodingMountCommand) AddFlags(f *gonutsflag.FlagSet) {
addEncodingFlags(f, cmd.params)
f.StringVar(&cmd.fallback, "fallback", "", "Fallback stream URL")
......@@ -242,9 +335,15 @@ func (cmd *createTranscodingMountCommand) Run(args []string) {
// Edit a mountpoint.
type editMountCommand struct {
BaseCommand
params *autoradio.EncodingParams
relay string
fallback string
relay *stringOptionalValue
fallback *stringOptionalValue
transFormat *stringOptionalValue
transSource *stringOptionalValue
transBitRate *intOptionalValue
transSampleRate *intOptionalValue
transChannels *intOptionalValue
transStereoMode *stringOptionalValue
transQuality *floatOptionalValue
}
var UNSET = "UNSET"
......@@ -262,18 +361,45 @@ default, non-relay behavior, set the relay to the empty string
(with --relay="").
`,
},
params: &autoradio.EncodingParams{},
relay: newStringOptionalValue(),
fallback: newStringOptionalValue(),
transFormat: newStringOptionalValue(),
transSource: newStringOptionalValue(),
transBitRate: newIntOptionalValue(),
transSampleRate: newIntOptionalValue(),
transChannels: newIntOptionalValue(),
transStereoMode: newStringOptionalValue(),
transQuality: newFloatOptionalValue(),
}
}
func (cmd *editMountCommand) AddFlags(f *gonutsflag.FlagSet) {
// Note that we use a magic value to figure out whether a flag
// has been specified or not, to make it possible to clear a
// field (by setting it to the empty string). There might be
// better way to do this.
f.StringVar(&cmd.relay, "relay", UNSET, "Upstream URL to relay")
f.StringVar(&cmd.fallback, "fallback", UNSET, "Fallback stream URL")
addEncodingFlags(f, cmd.params)
f.Var(cmd.relay, "relay", "Upstream URL to relay")
f.Var(cmd.fallback, "fallback", "Fallback stream URL")
f.Var(cmd.transSource, "source", "[transcoding] Source mountpoint")
f.Var(cmd.transFormat, "codec", "[transcoding] Encoding format")
f.Var(cmd.transQuality, "quality", "[transcoding] Quality (for VBR encoders)")
f.Var(cmd.transBitRate, "bitrate", "[transcoding] Bitrate (kbps)")
f.Var(cmd.transSampleRate, "samplerate", "[transcoding] Sample rate (Hz)")
f.Var(cmd.transChannels, "channels", "[transcoding] Number of channels")
f.Var(cmd.transStereoMode, "stereo-mode", "[transcoding] Stereo mode for mp3 encoding (stereo, joint_stereo)")
}
func (cmd *editMountCommand) transcodingOptionsSet() bool {
opts := []interface {
IsSet() bool
}{
cmd.transSource, cmd.transFormat, cmd.transQuality,
cmd.transBitRate, cmd.transSampleRate, cmd.transChannels,
cmd.transStereoMode,
}
for _, o := range opts {
if o.IsSet() {
return true
}
}
return false
}
func (cmd *editMountCommand) Run(args []string) {
......@@ -290,11 +416,38 @@ func (cmd *editMountCommand) Run(args []string) {
log.Fatal("ERROR: mount not found")
}
if cmd.fallback != UNSET {
m.Fallback = cmd.fallback
// Only set those fields that were passed on the command line.
if cmd.fallback.IsSet() {
m.Fallback = cmd.fallback.Value()
}
if cmd.relay.IsSet() {
setRelay(m, cmd.relay.Value())
}
if cmd.transcodingOptionsSet() && m.Transcoding == nil {
log.Fatal("ERROR: can't set transcoding options on a non-transcoding mount (delete and re-create)")
}
if cmd.transFormat.IsSet() {
m.Transcoding.Format = cmd.transFormat.Value()
}
if cmd.transSource.IsSet() {
m.Transcoding.SourceName = cmd.transSource.Value()
}
if cmd.transBitRate.IsSet() {
m.Transcoding.BitRate = cmd.transBitRate.Value()
}
if cmd.transSampleRate.IsSet() {
m.Transcoding.SampleRate = cmd.transSampleRate.Value()
}
if cmd.transQuality.IsSet() {
m.Transcoding.Quality = cmd.transQuality.Value()
}
if cmd.transChannels.IsSet() {
m.Transcoding.Channels = cmd.transChannels.Value()
}
if cmd.relay != UNSET {
setRelay(m, cmd.relay)
if cmd.transStereoMode.IsSet() {
m.Transcoding.StereoMode = cmd.transStereoMode.Value()
}
if err := m.Valid(); err != nil {
......
......@@ -24,7 +24,7 @@ set("log.file", false)
set("log.stdout", true)
upstream = mksafe(input.http("{{.SourceURL}}", buffer=5.0))
output.icecast(%{{.Format}}(samplerate={{.SampleRate}}, {{if gt .BitRate 0}}bitrate={{.BitRate}}, {{end}}{{if gt .Quality 0.0}}quality={{.Quality}}, {{end}}{{if eq .Channels 2}}stereo{{else}}mono{{end}}),
output.icecast(%{{.Format}}(samplerate={{.SampleRate}}, {{if gt .BitRate 0}}bitrate={{.BitRate}}, {{end}}{{if gt .Quality -1.0}}quality={{.Quality}}, {{end}}{{if .StereoMode}}stereo_mode={{.StereoMode}}, {{end}}{{if eq .Channels 2}}stereo{{else}}mono{{end}}),
mount="{{.TargetMount}}", host="{{.TargetIP}}", port={{.TargetPort}}, user="{{.TargetUsername}}", password="{{.TargetPassword}}",
upstream)
`
......@@ -51,6 +51,7 @@ type liquidsoapParams struct {
BitRate int
SampleRate int
Channels int
StereoMode string
Quality float32
}
......@@ -69,6 +70,7 @@ func newLiquidsoapParams(mount *autoradio.Mount) *liquidsoapParams {
BitRate: mount.Transcoding.BitRate,
SampleRate: mount.Transcoding.SampleRate,
Channels: mount.Transcoding.Channels,
StereoMode: mount.Transcoding.StereoMode,
Quality: float32(mount.Transcoding.Quality),
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment