diff --git a/codec/codecutil/list.go b/codec/codecutil/list.go index 071935d1..773f0c3e 100644 --- a/codec/codecutil/list.go +++ b/codec/codecutil/list.go @@ -24,20 +24,23 @@ LICENSE package codecutil -// A global list containing all available codecs for reference in any application. -// When adding or removing a codec from this list, the numCodecs const must be updated. +// All available codecs for reference in any application. +// When adding or removing a codec from this list, the IsValid function below must be updated. const ( - Undef = iota - PCM - ADPCM - H264 - H265 - MJPEG - JPEG - maxCodec + PCM = "pcm" + ADPCM = "adpcm" + H264 = "h264" + H265 = "h265" + MJPEG = "mjpeg" + JPEG = "jpeg" ) -// IsValid recieves an int representing a codec and checks if it is valid. -func IsValid(codec uint8) bool { - return 0 < codec && codec < maxCodec +// IsValid checks if a string is a known and valid codec in the right format. +func IsValid(s string) bool { + switch s { + case PCM, ADPCM, H264, H265, MJPEG, JPEG: + return true + default: + return false + } } diff --git a/container/mts/encoder.go b/container/mts/encoder.go index fff47dac..d9af626f 100644 --- a/container/mts/encoder.go +++ b/container/mts/encoder.go @@ -101,13 +101,20 @@ const ( defaultMediaPID = PIDVideo ) +// Used to consistently read and write MTS metadata entries. +const ( + WriteRateKey = "writeRate" + TimestampKey = "ts" + LocationKey = "loc" +) + // Meta allows addition of metadata to encoded mts from outside of this pkg. // See meta pkg for usage. // // TODO: make this not global. var Meta *meta.Data -// This will help us obtain a realtime for timestamp meta encoding. +// RealTime will help us obtain a realtime for timestamp meta encoding. var RealTime = realtime.NewRealTime() // Encoder encapsulates properties of an MPEG-TS generator. @@ -164,7 +171,7 @@ func NewEncoder(dst io.WriteCloser, log logger.LoggerIF, options ...func(*Encode } log.Debug("encoder options applied") - Meta.Add("writeRate", fmt.Sprintf("%f", 1/float64(e.writePeriod.Seconds()))) + Meta.Add(WriteRateKey, fmt.Sprintf("%f", 1/float64(e.writePeriod.Seconds()))) e.pmt.SyntaxSection.SpecificData.(*psi.PMT).StreamSpecificData.StreamType = e.streamID e.pmt.SyntaxSection.SpecificData.(*psi.PMT).StreamSpecificData.PID = e.mediaPID @@ -332,7 +339,7 @@ func updateMeta(b []byte, log logger.LoggerIF) ([]byte, error) { p := psi.PSIBytes(b) if RealTime.IsSet() { t := strconv.Itoa(int(RealTime.Get().Unix())) - Meta.Add("ts", t) + Meta.Add(TimestampKey, t) log.Debug("latest time added to meta", "time", t) } err := p.AddDescriptor(psi.MetadataTag, Meta.Encode()) diff --git a/container/mts/encoder_test.go b/container/mts/encoder_test.go index 58dd1ca1..048894e9 100644 --- a/container/mts/encoder_test.go +++ b/container/mts/encoder_test.go @@ -276,7 +276,7 @@ func TestMetaEncode1(t *testing.T) { t.Fatalf("could not create encoder, failed with error: %v", err) } - Meta.Add("ts", "12345678") + Meta.Add(TimestampKey, "12345678") if err := e.writePSI(); err != nil { t.Errorf("unexpected error: %v\n", err.Error()) } @@ -289,12 +289,9 @@ func TestMetaEncode1(t *testing.T) { 0x23, // Length of bytes to follow 0x00, 0x10, 0x00, 0x1f, } - rate := "writeRate=" + fmt.Sprintf("%f", float64(defaultRate)) - want = append(want, []byte(rate)...) // writeRate - want = append(want, []byte{ - '\t', 't', 's', '=', '1', '2', '3', '4', '5', '6', '7', '8', // timestamp - 0x1b, 0xe1, 0x00, 0xf0, 0x00, - }...) + rate := WriteRateKey + "=" + fmt.Sprintf("%f", float64(defaultRate)) + "\t" + TimestampKey + "=12345678" // timestamp + want = append(want, []byte(rate)...) // writeRate + want = append(want, []byte{0x1b, 0xe1, 0x00, 0xf0, 0x00}...) want = psi.AddCRC(want) want = psi.AddPadding(want) if !bytes.Equal(got, want) { @@ -313,8 +310,8 @@ func TestMetaEncode2(t *testing.T) { t.Fatalf("could not create MTS encoder, failed with error: %v", err) } - Meta.Add("ts", "12345678") - Meta.Add("loc", "1234,4321,1234") + Meta.Add(TimestampKey, "12345678") + Meta.Add(LocationKey, "1234,4321,1234") if err := e.writePSI(); err != nil { t.Errorf("did not expect error: %v from writePSI", err.Error()) } @@ -326,13 +323,10 @@ func TestMetaEncode2(t *testing.T) { 0x36, // Length of bytes to follow 0x00, 0x10, 0x00, 0x32, } - rate := "writeRate=" + fmt.Sprintf("%f", float64(defaultRate)) - want = append(want, []byte(rate)...) // writeRate - want = append(want, []byte{ - '\t', 't', 's', '=', '1', '2', '3', '4', '5', '6', '7', '8', '\t', // timestamp - 'l', 'o', 'c', '=', '1', '2', '3', '4', ',', '4', '3', '2', '1', ',', '1', '2', '3', '4', // location - 0x1b, 0xe1, 0x00, 0xf0, 0x00, - }...) + s := WriteRateKey + "=" + fmt.Sprintf("%f", float64(defaultRate)) + "\t" + + TimestampKey + "=12345678\t" + LocationKey + "=1234,4321,1234" + want = append(want, []byte(s)...) + want = append(want, []byte{0x1b, 0xe1, 0x00, 0xf0, 0x00}...) want = psi.AddCRC(want) want = psi.AddPadding(want) if !bytes.Equal(got, want) { @@ -350,8 +344,8 @@ func TestExtractMeta(t *testing.T) { t.Fatalf("could not create MTS encoder, failed with error: %v", err) } - Meta.Add("ts", "12345678") - Meta.Add("loc", "1234,4321,1234") + Meta.Add(TimestampKey, "12345678") + Meta.Add(LocationKey, "1234,4321,1234") if err := e.writePSI(); err != nil { t.Errorf("did not expect error: %v", err.Error()) } @@ -362,9 +356,9 @@ func TestExtractMeta(t *testing.T) { } rate := fmt.Sprintf("%f", float64(defaultRate)) want := map[string]string{ - "ts": "12345678", - "loc": "1234,4321,1234", - "writeRate": rate, + TimestampKey: "12345678", + LocationKey: "1234,4321,1234", + WriteRateKey: rate, } if !reflect.DeepEqual(got, want) { t.Errorf("did not get expected result.\ngot: %v\nwant: %v\n", got, want) diff --git a/container/mts/meta/meta.go b/container/mts/meta/meta.go index cc1966c7..e49e7cb3 100644 --- a/container/mts/meta/meta.go +++ b/container/mts/meta/meta.go @@ -58,7 +58,7 @@ var ( ErrUnexpectedMetaFormat = errors.New("Unexpected meta format") ) -// Metadata provides functionality for the storage and encoding of metadata +// Data provides functionality for the storage and encoding of metadata // using a map. type Data struct { mu sync.RWMutex diff --git a/container/mts/meta/meta_test.go b/container/mts/meta/meta_test.go index 2d65bc8c..3fc35611 100644 --- a/container/mts/meta/meta_test.go +++ b/container/mts/meta/meta_test.go @@ -32,11 +32,13 @@ import ( ) const ( - tstKey1 = "loc" - tstData1 = "a,b,c" - tstKey2 = "ts" - tstData2 = "12345678" - tstData3 = "d,e,f" + TimestampKey = "ts" + LocationKey = "loc" + tstKey1 = LocationKey + tstData1 = "a,b,c" + tstKey2 = TimestampKey + tstData2 = "12345678" + tstData3 = "d,e,f" ) // TestAddAndGet ensures that we can add metadata and then successfully get it. @@ -69,7 +71,7 @@ func TestUpdate(t *testing.T) { if data, ok := meta.Get(tstKey1); !ok { t.Errorf("Could not get data for key: %v\n", tstKey1) if data != tstData2 { - t.Error(`Data did not correctly update for key "loc"`) + t.Errorf("Data did not correctly update for key: %v\n", tstKey1) } } } @@ -143,11 +145,11 @@ func TestGetFrom(t *testing.T) { want string }{ { - "loc", + LocationKey, "a,b,c", }, { - "ts", + TimestampKey, "12345", }, } @@ -169,11 +171,11 @@ func TestGetAll(t *testing.T) { tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...) want := [][2]string{ { - "loc", + LocationKey, "a,b,c", }, { - "ts", + TimestampKey, "12345", }, } @@ -191,8 +193,8 @@ func TestGetAll(t *testing.T) { func TestGetAllAsMap(t *testing.T) { tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...) want := map[string]string{ - "loc": "a,b,c", - "ts": "12345", + LocationKey: "a,b,c", + TimestampKey: "12345", } got, err := GetAllAsMap(tstMeta) if err != nil { @@ -207,7 +209,7 @@ func TestGetAllAsMap(t *testing.T) { // the meta.Keys method. func TestKeys(t *testing.T) { tstMeta := append([]byte{0x00, 0x10, 0x00, 0x12}, "loc=a,b,c\tts=12345"...) - want := []string{"loc", "ts"} + want := []string{LocationKey, TimestampKey} got, err := Keys(tstMeta) if err != nil { t.Errorf("Unexpected error: %v\n", err) @@ -220,8 +222,8 @@ func TestKeys(t *testing.T) { // TestNewFromMap checks that we can successfully create a new data struct from a map. func TestNewFromMap(t *testing.T) { want := map[string]string{ - "loc": "a,b,c", - "ts": "12345", + LocationKey: "a,b,c", + TimestampKey: "12345", } meta := NewFromMap(want) @@ -236,8 +238,8 @@ func TestNewFromMap(t *testing.T) { // TestEncodeAsString checks that metadata is correctly encoded as a string. func TestEncodeAsString(t *testing.T) { meta := NewFromMap(map[string]string{ - "loc": "a,b,c", - "ts": "12345", + LocationKey: "a,b,c", + TimestampKey: "12345", }) got := meta.EncodeAsString() diff --git a/device/alsa/alsa.go b/device/alsa/alsa.go index cd6f13b8..eb35d1b7 100644 --- a/device/alsa/alsa.go +++ b/device/alsa/alsa.go @@ -95,7 +95,7 @@ type Config struct { Channels uint BitDepth uint RecPeriod float64 - Codec uint8 + Codec string } // Logger enables any implementation of a logger to be used. diff --git a/device/geovision/geovision.go b/device/geovision/geovision.go index 9da7453c..d12ae8bc 100644 --- a/device/geovision/geovision.go +++ b/device/geovision/geovision.go @@ -173,7 +173,7 @@ func (g *GeoVision) Set(c avconfig.Config) error { g.cfg.CameraIP, gvconfig.Channel(g.cfg.CameraChan), gvconfig.CodecOut( - map[uint8]gvconfig.Codec{ + map[string]gvconfig.Codec{ codecutil.H264: gvconfig.CodecH264, codecutil.H265: gvconfig.CodecH265, codecutil.MJPEG: gvconfig.CodecMJPEG, diff --git a/revid/audio_linux.go b/revid/audio_linux.go index f7455b5e..1553dc47 100644 --- a/revid/audio_linux.go +++ b/revid/audio_linux.go @@ -36,6 +36,15 @@ import ( "bitbucket.org/ausocean/utils/logger" ) +// Used to reliably read, write, and test audio metadata entry keys. +const ( + SampleRateKey = "sampleRate" + ChannelsKey = "channels" + PeriodKey = "period" + BitDepthKey = "bitDepth" + CodecKey = "codec" +) + func (r *Revid) setupAudio() error { // Create new ALSA device. d := alsa.New(r.cfg.Logger) @@ -59,19 +68,11 @@ func (r *Revid) setupAudio() error { r.lexTo = l.Lex // Add metadata. - mts.Meta.Add("sampleRate", strconv.Itoa(int(r.cfg.SampleRate))) - mts.Meta.Add("channels", strconv.Itoa(int(r.cfg.Channels))) - mts.Meta.Add("period", fmt.Sprintf("%.6f", r.cfg.RecPeriod)) - mts.Meta.Add("bitDepth", strconv.Itoa(int(r.cfg.BitDepth))) - - switch r.cfg.InputCodec { - case codecutil.PCM: - mts.Meta.Add("codec", "pcm") - case codecutil.ADPCM: - mts.Meta.Add("codec", "adpcm") - default: - r.cfg.Logger.Log(logger.Fatal, "no audio codec set in config") - } + mts.Meta.Add(SampleRateKey, strconv.Itoa(int(r.cfg.SampleRate))) + mts.Meta.Add(ChannelsKey, strconv.Itoa(int(r.cfg.Channels))) + mts.Meta.Add(PeriodKey, fmt.Sprintf("%.6f", r.cfg.RecPeriod)) + mts.Meta.Add(BitDepthKey, strconv.Itoa(int(r.cfg.BitDepth))) + mts.Meta.Add(CodecKey, r.cfg.InputCodec) return nil } diff --git a/revid/config/config.go b/revid/config/config.go index 012c8b46..1323c17e 100644 --- a/revid/config/config.go +++ b/revid/config/config.go @@ -168,7 +168,7 @@ type Config struct { // InputCodec defines the input codec we wish to use, and therefore defines the // lexer for use in the pipeline. This defaults to H264, but H265 is also a // valid option if we expect this from the input. - InputCodec uint8 + InputCodec string // InputPath defines the input file location for File Input. This must be // defined if File input is to be used. diff --git a/revid/config/config_test.go b/revid/config/config_test.go index 43484226..4130f14b 100644 --- a/revid/config/config_test.go +++ b/revid/config/config_test.go @@ -95,7 +95,7 @@ func TestUpdate(t *testing.T) { "HorizontalFlip": "true", "HTTPAddress": "http://address", "Input": "rtsp", - "InputCodec": "MJPEG", + "InputCodec": "mjpeg", "InputPath": "/inputpath", "logging": "Error", "Loop": "true", diff --git a/revid/config/variables.go b/revid/config/variables.go index 0edc815a..726faf16 100644 --- a/revid/config/variables.go +++ b/revid/config/variables.go @@ -299,24 +299,10 @@ var Variables = []struct { Name: KeyInputCodec, Type_: "enum:H264,H265,MJPEG,JPEG,PCM,ADPCM", Update: func(c *Config, v string) { - c.InputCodec = parseEnum( - KeyInputCodec, - v, - map[string]uint8{ - "h264": codecutil.H264, - "h265": codecutil.H265, - "mjpeg": codecutil.MJPEG, - "jpeg": codecutil.JPEG, - "pcm": codecutil.PCM, - "adpcm": codecutil.ADPCM, - }, - c, - ) + c.InputCodec = v }, Validate: func(c *Config) { - switch c.InputCodec { - case codecutil.H264, codecutil.MJPEG, codecutil.JPEG, codecutil.PCM, codecutil.ADPCM: - default: + if !codecutil.IsValid(c.InputCodec) { c.LogInvalidField(KeyInputCodec, defaultInputCodec) c.InputCodec = defaultInputCodec } diff --git a/revid/pipeline.go b/revid/pipeline.go index bad96fa7..097978e4 100644 --- a/revid/pipeline.go +++ b/revid/pipeline.go @@ -329,7 +329,7 @@ func (r *Revid) setupPipeline(mtsEnc func(dst io.WriteCloser, rate float64) (io. // setLexer sets the revid input lexer based on input codec and whether input // is RTSP or not, in which case an RTP/ extractor is used. -func (r *Revid) setLexer(c uint8, isRTSP bool) error { +func (r *Revid) setLexer(c string, isRTSP bool) error { switch c { case codecutil.H264: r.cfg.Logger.Log(logger.Debug, "using H.264 codec") diff --git a/revid/senders.go b/revid/senders.go index 11420d72..36310389 100644 --- a/revid/senders.go +++ b/revid/senders.go @@ -153,7 +153,7 @@ func extractMeta(r string, log func(lvl int8, msg string, args ...interface{})) log(logger.Debug, "No location in reply") } else { log(logger.Debug, fmt.Sprintf("got location: %v", g)) - mts.Meta.Add("loc", g) + mts.Meta.Add(mts.LocationKey, g) } return nil