github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/ingress/validation.go (about)

     1  package ingress
     2  
     3  import (
     4  	"github.com/whoyao/protocol/livekit"
     5  )
     6  
     7  // This validates that ingress options have no consistency issues and provide enough parameters
     8  // to be usable by the ingress service. Options that pass this test may still need some fields to be poulated with default values
     9  // before being used in a media pipeline.
    10  
    11  func Validate(info *livekit.IngressInfo) error {
    12  	if info == nil {
    13  		return ErrInvalidIngress("missing IngressInfo")
    14  	}
    15  
    16  	// For now, require a room to be set. We should eventually allow changing the room on an active ingress
    17  	if info.RoomName == "" {
    18  		return ErrInvalidIngress("no room name")
    19  	}
    20  
    21  	return ValidateForSerialization(info)
    22  }
    23  
    24  // Sparse info with not all required fields populated are acceptable for serialization, provided values are consistent
    25  func ValidateForSerialization(info *livekit.IngressInfo) error {
    26  	if info == nil {
    27  		return ErrInvalidIngress("missing IngressInfo")
    28  	}
    29  
    30  	if info.InputType != livekit.IngressInput_RTMP_INPUT && info.InputType != livekit.IngressInput_WHIP_INPUT {
    31  		return ErrInvalidIngress("unsupported input type")
    32  	}
    33  
    34  	if info.StreamKey == "" {
    35  		return ErrInvalidIngress("no stream key")
    36  	}
    37  
    38  	if info.ParticipantIdentity == "" {
    39  		return ErrInvalidIngress("no participant identity")
    40  	}
    41  
    42  	err := ValidateVideoOptionsConsistency(info.Video)
    43  	if err != nil {
    44  		return err
    45  	}
    46  
    47  	err = ValidateAudioOptionsConsistency(info.Audio)
    48  	if err != nil {
    49  		return err
    50  	}
    51  
    52  	return nil
    53  
    54  }
    55  
    56  func ValidateVideoOptionsConsistency(options *livekit.IngressVideoOptions) error {
    57  	if options == nil {
    58  		return nil
    59  	}
    60  
    61  	switch options.Source {
    62  	case livekit.TrackSource_UNKNOWN,
    63  		livekit.TrackSource_CAMERA,
    64  		livekit.TrackSource_SCREEN_SHARE:
    65  		// continue
    66  	default:
    67  		return NewInvalidVideoParamsError("invalid track source")
    68  	}
    69  
    70  	switch o := options.EncodingOptions.(type) {
    71  	case nil:
    72  		// Default, continue
    73  	case *livekit.IngressVideoOptions_Preset:
    74  		_, ok := livekit.IngressVideoEncodingPreset_name[int32(o.Preset)]
    75  		if !ok {
    76  			return NewInvalidVideoParamsError("invalid preset")
    77  		}
    78  
    79  	case *livekit.IngressVideoOptions_Options:
    80  		err := ValidateVideoEncodingOptionsConsistency(o.Options)
    81  		if err != nil {
    82  			return err
    83  		}
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  func ValidateVideoEncodingOptionsConsistency(options *livekit.IngressVideoEncodingOptions) error {
    90  	if options == nil {
    91  		return NewInvalidVideoParamsError("empty options")
    92  	}
    93  
    94  	if options.FrameRate < 0 {
    95  		return NewInvalidVideoParamsError("invalid framerate")
    96  	}
    97  
    98  	switch options.VideoCodec {
    99  	case livekit.VideoCodec_DEFAULT_VC,
   100  		livekit.VideoCodec_H264_BASELINE,
   101  		livekit.VideoCodec_VP8:
   102  		// continue
   103  	default:
   104  		return NewInvalidVideoParamsError("video codec unsupported")
   105  	}
   106  
   107  	layersByQuality := make(map[livekit.VideoQuality]*livekit.VideoLayer)
   108  
   109  	for _, layer := range options.Layers {
   110  		if layer.Height == 0 || layer.Width == 0 {
   111  			return ErrInvalidOutputDimensions
   112  		}
   113  
   114  		if layer.Width%2 == 1 {
   115  			return ErrInvalidIngress("layer width must be even")
   116  		}
   117  
   118  		if layer.Height%2 == 1 {
   119  			return ErrInvalidIngress("layer height must be even")
   120  		}
   121  
   122  		if _, ok := layersByQuality[layer.Quality]; ok {
   123  			return NewInvalidVideoParamsError("more than one layer with the same quality level")
   124  		}
   125  		layersByQuality[layer.Quality] = layer
   126  	}
   127  
   128  	var oldLayerArea uint32
   129  	for q := livekit.VideoQuality_LOW; q <= livekit.VideoQuality_HIGH; q++ {
   130  		layer, ok := layersByQuality[q]
   131  		if !ok {
   132  			continue
   133  		}
   134  		layerArea := layer.Width * layer.Height
   135  
   136  		if layerArea <= oldLayerArea {
   137  			return NewInvalidVideoParamsError("video layers do not have increasing pixel count with increasing quality")
   138  		}
   139  		oldLayerArea = layerArea
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func ValidateAudioOptionsConsistency(options *livekit.IngressAudioOptions) error {
   146  	if options == nil {
   147  		return nil
   148  	}
   149  
   150  	switch options.Source {
   151  	case livekit.TrackSource_UNKNOWN,
   152  		livekit.TrackSource_MICROPHONE,
   153  		livekit.TrackSource_SCREEN_SHARE_AUDIO:
   154  		// continue
   155  	default:
   156  		return NewInvalidAudioParamsError("invalid track source")
   157  	}
   158  
   159  	switch o := options.EncodingOptions.(type) {
   160  	case nil:
   161  		// Default, continue
   162  	case *livekit.IngressAudioOptions_Preset:
   163  		_, ok := livekit.IngressAudioEncodingPreset_name[int32(o.Preset)]
   164  		if !ok {
   165  			return NewInvalidAudioParamsError("invalid preset")
   166  		}
   167  
   168  	case *livekit.IngressAudioOptions_Options:
   169  		err := ValidateAudioEncodingOptionsConsistency(o.Options)
   170  		if err != nil {
   171  			return err
   172  		}
   173  	}
   174  
   175  	return nil
   176  }
   177  
   178  func ValidateAudioEncodingOptionsConsistency(options *livekit.IngressAudioEncodingOptions) error {
   179  	if options == nil {
   180  		return NewInvalidAudioParamsError("empty options")
   181  	}
   182  
   183  	switch options.AudioCodec {
   184  	case livekit.AudioCodec_DEFAULT_AC,
   185  		livekit.AudioCodec_OPUS:
   186  		// continue
   187  	default:
   188  		return NewInvalidAudioParamsError("audio codec unsupported")
   189  	}
   190  
   191  	if options.Channels > 2 {
   192  		return NewInvalidAudioParamsError("invalid audio channel count")
   193  	}
   194  
   195  	return nil
   196  }