v210A flowdef
This commit is contained in:
@@ -355,7 +355,7 @@ func buildVideoConfig(args appArgs) (*video.Config, error) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
definition, err = flowdef.ParseV210Video(data)
|
definition, err = flowdef.ParseVideo(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"parse video flow definition %q: %w",
|
"parse video flow definition %q: %w",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const (
|
|||||||
FormatVideo = "urn:x-nmos:format:video"
|
FormatVideo = "urn:x-nmos:format:video"
|
||||||
FormatAudio = "urn:x-nmos:format:audio"
|
FormatAudio = "urn:x-nmos:format:audio"
|
||||||
MediaTypeV210 = "video/v210"
|
MediaTypeV210 = "video/v210"
|
||||||
|
MediaTypeV210A = "video/v210a"
|
||||||
MediaTypeFloat32 = "audio/float32"
|
MediaTypeFloat32 = "audio/float32"
|
||||||
|
|
||||||
InterlaceProgressive = "progressive"
|
InterlaceProgressive = "progressive"
|
||||||
@@ -75,7 +76,12 @@ type VideoComponent struct {
|
|||||||
BitDepth uint `json:"bit_depth"`
|
BitDepth uint `json:"bit_depth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewV210Video(id string, width, height uint, rate Rational) (Video, error) {
|
func newVideo(
|
||||||
|
id string,
|
||||||
|
width, height uint,
|
||||||
|
rate Rational,
|
||||||
|
mediaType string,
|
||||||
|
) (Video, error) {
|
||||||
definition := Video{
|
definition := Video{
|
||||||
Common: Common{
|
Common: Common{
|
||||||
Description: "go-mxl-pattern-gen generated video",
|
Description: "go-mxl-pattern-gen generated video",
|
||||||
@@ -86,7 +92,7 @@ func NewV210Video(id string, width, height uint, rate Rational) (Video, error) {
|
|||||||
Format: FormatVideo,
|
Format: FormatVideo,
|
||||||
Label: "go-mxl-pattern-gen generated video",
|
Label: "go-mxl-pattern-gen generated video",
|
||||||
Parents: []string{},
|
Parents: []string{},
|
||||||
MediaType: MediaTypeV210,
|
MediaType: mediaType,
|
||||||
},
|
},
|
||||||
GrainRate: rate,
|
GrainRate: rate,
|
||||||
FrameWidth: width,
|
FrameWidth: width,
|
||||||
@@ -105,7 +111,15 @@ func NewV210Video(id string, width, height uint, rate Rational) (Video, error) {
|
|||||||
return definition, nil
|
return definition, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseV210Video(data []byte) (Video, error) {
|
func NewV210Video(id string, width, height uint, rate Rational) (Video, error) {
|
||||||
|
return newVideo(id, width, height, rate, MediaTypeV210)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewV210AVideo(id string, width, height uint, rate Rational) (Video, error) {
|
||||||
|
return newVideo(id, width, height, rate, MediaTypeV210A)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseVideo(data []byte) (Video, error) {
|
||||||
var definition Video
|
var definition Video
|
||||||
if err := json.Unmarshal(data, &definition); err != nil {
|
if err := json.Unmarshal(data, &definition); err != nil {
|
||||||
return Video{}, fmt.Errorf("decode video flow definition: %w", err)
|
return Video{}, fmt.Errorf("decode video flow definition: %w", err)
|
||||||
@@ -123,8 +137,10 @@ func (v Video) Validate() error {
|
|||||||
if v.Format != FormatVideo {
|
if v.Format != FormatVideo {
|
||||||
return fmt.Errorf("format must be %q, got %q", FormatVideo, v.Format)
|
return fmt.Errorf("format must be %q, got %q", FormatVideo, v.Format)
|
||||||
}
|
}
|
||||||
if v.MediaType != MediaTypeV210 {
|
switch v.MediaType {
|
||||||
return fmt.Errorf("media_type must be %q, got %q", MediaTypeV210, v.MediaType)
|
case MediaTypeV210, MediaTypeV210A:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("media_type must be %q or %q, got %q", MediaTypeV210, MediaTypeV210A, v.MediaType)
|
||||||
}
|
}
|
||||||
if v.InterlaceMode != InterlaceProgressive {
|
if v.InterlaceMode != InterlaceProgressive {
|
||||||
return fmt.Errorf("interlace_mode must be %q, got %q", InterlaceProgressive, v.InterlaceMode)
|
return fmt.Errorf("interlace_mode must be %q, got %q", InterlaceProgressive, v.InterlaceMode)
|
||||||
|
|||||||
@@ -28,6 +28,29 @@ func TestNewV210Video(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewV210AVideo(t *testing.T) {
|
||||||
|
definition, err := NewV210AVideo(testVideoID, 1280, 720, Rational{Numerator: 50, Denominator: 1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewV210AVideo: %v", err)
|
||||||
|
}
|
||||||
|
if definition.MediaType != MediaTypeV210A {
|
||||||
|
t.Fatalf("media type = %q, want %q", definition.MediaType, MediaTypeV210A)
|
||||||
|
}
|
||||||
|
wantComponents := []VideoComponent{
|
||||||
|
{Name: "Y", Width: 1280, Height: 720, BitDepth: 10},
|
||||||
|
{Name: "Cb", Width: 640, Height: 720, BitDepth: 10},
|
||||||
|
{Name: "Cr", Width: 640, Height: 720, BitDepth: 10},
|
||||||
|
}
|
||||||
|
if len(definition.Components) != len(wantComponents) {
|
||||||
|
t.Fatalf("component count = %d, want %d", len(definition.Components), len(wantComponents))
|
||||||
|
}
|
||||||
|
for i, want := range wantComponents {
|
||||||
|
if definition.Components[i] != want {
|
||||||
|
t.Fatalf("component %d = %+v, want %+v", i, definition.Components[i], want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewV210VideoRejectsOddWidth(t *testing.T) {
|
func TestNewV210VideoRejectsOddWidth(t *testing.T) {
|
||||||
_, err := NewV210Video(testVideoID, 1919, 1080, Rational{Numerator: 25, Denominator: 1})
|
_, err := NewV210Video(testVideoID, 1919, 1080, Rational{Numerator: 25, Denominator: 1})
|
||||||
if err == nil || !strings.Contains(err.Error(), "even") {
|
if err == nil || !strings.Contains(err.Error(), "even") {
|
||||||
@@ -35,7 +58,7 @@ func TestNewV210VideoRejectsOddWidth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseV210Video(t *testing.T) {
|
func TestParseVideo(t *testing.T) {
|
||||||
want, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1})
|
want, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("NewV210Video: %v", err)
|
t.Fatalf("NewV210Video: %v", err)
|
||||||
@@ -45,28 +68,58 @@ func TestParseV210Video(t *testing.T) {
|
|||||||
t.Fatalf("json.Marshal: %v", err)
|
t.Fatalf("json.Marshal: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := ParseV210Video(data)
|
got, err := ParseVideo(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ParseV210Video: %v", err)
|
t.Fatalf("ParseVideo: %v", err)
|
||||||
}
|
}
|
||||||
if got.ID != want.ID || got.FrameWidth != want.FrameWidth || got.GrainRate != want.GrainRate {
|
if got.ID != want.ID || got.FrameWidth != want.FrameWidth || got.GrainRate != want.GrainRate {
|
||||||
t.Fatalf("parsed definition = %+v, want %+v", got, want)
|
t.Fatalf("parsed definition = %+v, want %+v", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseV210VideoRejectsAudio(t *testing.T) {
|
func TestParseVideoAcceptsV210A(t *testing.T) {
|
||||||
|
want, err := NewV210AVideo(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewV210AVideo: %v", err)
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(want)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("json.Marshal: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := ParseVideo(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ParseVideo: %v", err)
|
||||||
|
}
|
||||||
|
if got.MediaType != MediaTypeV210A {
|
||||||
|
t.Fatalf("media type = %q, want %q", got.MediaType, MediaTypeV210A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseVideoRejectsAudio(t *testing.T) {
|
||||||
data := []byte(`{
|
data := []byte(`{
|
||||||
"id":"5fbec3b1-1b0f-417d-9059-8b94a47197ed",
|
"id":"5fbec3b1-1b0f-417d-9059-8b94a47197ed",
|
||||||
"format":"urn:x-nmos:format:audio",
|
"format":"urn:x-nmos:format:audio",
|
||||||
"media_type":"audio/float32"
|
"media_type":"audio/float32"
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
_, err := ParseV210Video(data)
|
_, err := ParseVideo(data)
|
||||||
if err == nil || !strings.Contains(err.Error(), "format must be") {
|
if err == nil || !strings.Contains(err.Error(), "format must be") {
|
||||||
t.Fatalf("error = %v, want video format error", err)
|
t.Fatalf("error = %v, want video format error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVideoRejectsUnknownMediaType(t *testing.T) {
|
||||||
|
definition, err := NewV210Video(testVideoID, 1920, 1080, Rational{Numerator: 25, Denominator: 1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewV210Video: %v", err)
|
||||||
|
}
|
||||||
|
definition.MediaType = "video/unknown"
|
||||||
|
if err := definition.Validate(); err == nil || !strings.Contains(err.Error(), "media_type") {
|
||||||
|
t.Fatalf("error = %v, want media_type error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewFloat32Audio(t *testing.T) {
|
func TestNewFloat32Audio(t *testing.T) {
|
||||||
definition, err := NewFloat32Audio(testAudioID, 2, Rational{Numerator: 48000, Denominator: 1})
|
definition, err := NewFloat32Audio(testAudioID, 2, Rational{Numerator: 48000, Denominator: 1})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user