commit 935e7232de45edecd618fb1625e517c2293c9497 Author: Garionion Date: Sun Feb 6 00:34:05 2022 +0100 inital commit diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..8392d15 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0151c6e --- /dev/null +++ b/.gitignore @@ -0,0 +1,92 @@ +assets + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..c295c1a --- /dev/null +++ b/api/api.go @@ -0,0 +1,166 @@ +package api + +import ( + "context" + "fmt" + "git.entr0py.de/garionion/gstreamer-graphix/gstreamer" + "github.com/google/uuid" + "github.com/rs/zerolog/log" + "sync" + "time" +) + +type PipelineService struct { + pipelineSlots map[string][]string + pipelineslotmapping map[string]string + UnimplementedPipelineServiceServer + *gstreamer.Gstreamer + Font, FontWeight string //default font + FontSize int // Fontsize 0 does not make sense + Context context.Context + sync.RWMutex +} + +func (s *PipelineService) UpdatePipeline(ctx context.Context, updates *PipelineUpdates) (*PipelineUpdateResponse, error) { + pipeline, err := s.Gstreamer.GetPipeline(updates.Id.GetId()) + if err != nil { + log.Error().Str("ID", updates.Id.GetId()).Err(err) + } else { + overlays := s.convertRPCOverlayToGstreamerOverlay(updates.GetOverlays()) + s.Lock() + pipeline.PipelineUpdates <- gstreamer.PipelineUpdates{Overlays: overlays} + s.Unlock() + } + + return &PipelineUpdateResponse{PipelineId: &PipelineID{Id: updates.Id.GetId()}}, nil +} + +func (s *PipelineService) StopPipeline(ctx context.Context, id *PipelineID) (*PipelineStopResponse, error) { + pipeline, err := s.Gstreamer.GetPipeline(id.GetId()) + if err != nil { + log.Error().Str("ID", id.GetId()).Err(err) + } else { + pipeline.Lock() + err = pipeline.Stop() + pipeline.Unlock() + } + return &PipelineStopResponse{PipelineId: &PipelineID{Id: id.GetId()}}, err +} + +func (s *PipelineService) StartPipeline(ctx context.Context, id *PipelineID) (*PipelineStartResponse, error) { + pipeline, err := s.Gstreamer.GetPipeline(id.GetId()) + if err != nil { + log.Error().Str("ID", id.GetId()).Err(err) + } else { + pipeline.Lock() + err = pipeline.Start() + pipeline.Unlock() + } + return &PipelineStartResponse{PipelineId: &PipelineID{Id: id.GetId()}}, err +} + +func (s *PipelineService) DeletePipeline(ctx context.Context, id *PipelineID) (*PipelineDeleteResponse, error) { + go s.Gstreamer.DeletePipeline(id.GetId()) + if slot, ok := s.pipelineslotmapping[id.GetId()]; ok { + s.Lock() + delete(s.pipelineslotmapping, id.GetId()) + var i int + for i := range s.pipelineSlots[slot] { + if s.pipelineSlots[slot][i] == id.GetId() { + break + } + } + s.pipelineSlots[slot] = append(s.pipelineSlots[slot][:i], s.pipelineSlots[slot][i+1:]...) + s.Unlock() + } + return &PipelineDeleteResponse{PipelineId: &PipelineID{Id: id.GetId()}}, nil +} + +func (s *PipelineService) CreatePipeline(ctx context.Context, pipeline *Pipeline) (*PipelineCreationResponse, error) { + uid, _ := uuid.NewRandom() + id := uid.String() + gstreamerPipeline := &gstreamer.Pipeline{ + Name: pipeline.GetName(), + ID: id, + InputElement: gstreamer.InputElement{ + Name: fmt.Sprintf("%s-input", pipeline.GetName()), + Type: "filesrc", + Properties: map[string]string{ + "location": pipeline.GetClip(), + }, + }, + OutputElement: gstreamer.OutputElement{ + Name: fmt.Sprintf("%s-output", pipeline.GetName()), + Type: pipeline.GetOutput(), + }, + } + gstreamerPipeline.Overlays = s.convertRPCOverlayToGstreamerOverlay(pipeline.Overlay) + gstreamerPipeline.Create() + s.Lock() + if _, ok := s.pipelineSlots[pipeline.GetName()]; ok { + s.pipelineSlots[pipeline.GetName()] = append(s.pipelineSlots[pipeline.GetName()], id) + } else { + s.pipelineSlots[pipeline.GetName()] = []string{id} + } + + s.pipelineslotmapping[id] = pipeline.GetName() + s.Unlock() + return &PipelineCreationResponse{ + PipelineId: &PipelineID{Id: id}, + }, nil +} + +func (s *PipelineService) convertRPCOverlayToGstreamerOverlay(apiOverlays []*Overlay) map[string]gstreamer.Overlay { + gstreamerOverlays := map[string]gstreamer.Overlay{} + for _, overlay := range apiOverlays { + switch overlay.GetOverlayData().(type) { + case *Overlay_Extra: + log.Error().Msgf("Extra Element not implemented, used by overlay %s", overlay.GetName()) + case *Overlay_ImageOverlay: + o := overlay.GetImageOverlay() + imageOverlay := &gstreamer.ImageOverlay{ + Name: overlay.GetName(), + Path: o.GetPath(), + X: int(overlay.GetX()), + Y: int(overlay.GetY()), + Display: overlay.GetDisplay(), + BlendIn: time.Duration(overlay.GetBlendIn()), + BlendOut: time.Duration(overlay.GetBlendOut()), + } + gstreamerOverlays[overlay.GetName()] = imageOverlay + case *Overlay_TextOverlay: + o := overlay.GetTextOverlay() + if o.GetText() == "" { + continue + } + font := s.Font + if o.GetFontName() != "" { + font = o.GetFontName() + } + fontsize := s.FontSize + if o.GetFontSize() != 0 { + fontsize = int(o.GetFontSize()) + } + fontweight := s.FontWeight + if o.GetFontWeight() != "" { + font = o.GetFontWeight() + } + textOverlay := &gstreamer.TextOverlay{ + Name: overlay.GetName(), + X: int(overlay.GetX()), + Y: int(overlay.GetY()), + Font: font, + FontSize: fontsize, + FontWeight: fontweight, + Color: o.GetFontColor(), + Value: o.GetText(), + Display: overlay.GetDisplay(), + MaxWidth: uint(o.GetMaxWidth()), + BlendIn: time.Duration(overlay.GetBlendIn()), + BlendOut: time.Duration(overlay.GetBlendOut()), + } + gstreamerOverlays[overlay.GetName()] = textOverlay + } + } + return gstreamerOverlays +} diff --git a/api/api.pb.go b/api/api.pb.go new file mode 100644 index 0000000..9a4fa5b --- /dev/null +++ b/api/api.pb.go @@ -0,0 +1,1141 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.19.0 +// source: api/api.proto + +package api + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Pipeline struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Clip string `protobuf:"bytes,2,opt,name=clip,proto3" json:"clip,omitempty"` + Output string `protobuf:"bytes,3,opt,name=output,proto3" json:"output,omitempty"` + Overlay []*Overlay `protobuf:"bytes,4,rep,name=overlay,proto3" json:"overlay,omitempty"` +} + +func (x *Pipeline) Reset() { + *x = Pipeline{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Pipeline) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pipeline) ProtoMessage() {} + +func (x *Pipeline) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pipeline.ProtoReflect.Descriptor instead. +func (*Pipeline) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{0} +} + +func (x *Pipeline) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Pipeline) GetClip() string { + if x != nil { + return x.Clip + } + return "" +} + +func (x *Pipeline) GetOutput() string { + if x != nil { + return x.Output + } + return "" +} + +func (x *Pipeline) GetOverlay() []*Overlay { + if x != nil { + return x.Overlay + } + return nil +} + +type Overlay struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + X int64 `protobuf:"varint,2,opt,name=x,proto3" json:"x,omitempty"` + Y int64 `protobuf:"varint,3,opt,name=y,proto3" json:"y,omitempty"` + Display bool `protobuf:"varint,4,opt,name=display,proto3" json:"display,omitempty"` + BlendIn int64 `protobuf:"varint,5,opt,name=blend_in,json=blendIn,proto3" json:"blend_in,omitempty"` + BlendOut int64 `protobuf:"varint,6,opt,name=blend_out,json=blendOut,proto3" json:"blend_out,omitempty"` + // Types that are assignable to OverlayData: + // *Overlay_Extra + // *Overlay_TextOverlay + // *Overlay_ImageOverlay + OverlayData isOverlay_OverlayData `protobuf_oneof:"overlay_data"` +} + +func (x *Overlay) Reset() { + *x = Overlay{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Overlay) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Overlay) ProtoMessage() {} + +func (x *Overlay) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Overlay.ProtoReflect.Descriptor instead. +func (*Overlay) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{1} +} + +func (x *Overlay) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Overlay) GetX() int64 { + if x != nil { + return x.X + } + return 0 +} + +func (x *Overlay) GetY() int64 { + if x != nil { + return x.Y + } + return 0 +} + +func (x *Overlay) GetDisplay() bool { + if x != nil { + return x.Display + } + return false +} + +func (x *Overlay) GetBlendIn() int64 { + if x != nil { + return x.BlendIn + } + return 0 +} + +func (x *Overlay) GetBlendOut() int64 { + if x != nil { + return x.BlendOut + } + return 0 +} + +func (m *Overlay) GetOverlayData() isOverlay_OverlayData { + if m != nil { + return m.OverlayData + } + return nil +} + +func (x *Overlay) GetExtra() *ExtraOverlay { + if x, ok := x.GetOverlayData().(*Overlay_Extra); ok { + return x.Extra + } + return nil +} + +func (x *Overlay) GetTextOverlay() *TextOverlay { + if x, ok := x.GetOverlayData().(*Overlay_TextOverlay); ok { + return x.TextOverlay + } + return nil +} + +func (x *Overlay) GetImageOverlay() *ImageOverlay { + if x, ok := x.GetOverlayData().(*Overlay_ImageOverlay); ok { + return x.ImageOverlay + } + return nil +} + +type isOverlay_OverlayData interface { + isOverlay_OverlayData() +} + +type Overlay_Extra struct { + Extra *ExtraOverlay `protobuf:"bytes,7,opt,name=extra,proto3,oneof"` +} + +type Overlay_TextOverlay struct { + TextOverlay *TextOverlay `protobuf:"bytes,8,opt,name=text_overlay,json=textOverlay,proto3,oneof"` +} + +type Overlay_ImageOverlay struct { + ImageOverlay *ImageOverlay `protobuf:"bytes,9,opt,name=image_overlay,json=imageOverlay,proto3,oneof"` +} + +func (*Overlay_Extra) isOverlay_OverlayData() {} + +func (*Overlay_TextOverlay) isOverlay_OverlayData() {} + +func (*Overlay_ImageOverlay) isOverlay_OverlayData() {} + +type ExtraOverlay struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Properties map[string]string `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *ExtraOverlay) Reset() { + *x = ExtraOverlay{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ExtraOverlay) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExtraOverlay) ProtoMessage() {} + +func (x *ExtraOverlay) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ExtraOverlay.ProtoReflect.Descriptor instead. +func (*ExtraOverlay) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{2} +} + +func (x *ExtraOverlay) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil +} + +type ImageOverlay struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + X int64 `protobuf:"varint,2,opt,name=x,proto3" json:"x,omitempty"` + Y int32 `protobuf:"varint,3,opt,name=y,proto3" json:"y,omitempty"` + Path string `protobuf:"bytes,7,opt,name=path,proto3" json:"path,omitempty"` +} + +func (x *ImageOverlay) Reset() { + *x = ImageOverlay{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImageOverlay) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImageOverlay) ProtoMessage() {} + +func (x *ImageOverlay) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ImageOverlay.ProtoReflect.Descriptor instead. +func (*ImageOverlay) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{3} +} + +func (x *ImageOverlay) GetX() int64 { + if x != nil { + return x.X + } + return 0 +} + +func (x *ImageOverlay) GetY() int32 { + if x != nil { + return x.Y + } + return 0 +} + +func (x *ImageOverlay) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type TextOverlay struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + X int64 `protobuf:"varint,2,opt,name=x,proto3" json:"x,omitempty"` + Y int64 `protobuf:"varint,3,opt,name=y,proto3" json:"y,omitempty"` + Text string `protobuf:"bytes,4,opt,name=text,proto3" json:"text,omitempty"` + FontSize uint64 `protobuf:"varint,5,opt,name=font_size,json=fontSize,proto3" json:"font_size,omitempty"` + FontName string `protobuf:"bytes,6,opt,name=font_name,json=fontName,proto3" json:"font_name,omitempty"` + FontWeight string `protobuf:"bytes,7,opt,name=font_weight,json=fontWeight,proto3" json:"font_weight,omitempty"` + FontColor uint32 `protobuf:"varint,8,opt,name=font_color,json=fontColor,proto3" json:"font_color,omitempty"` + MaxWidth uint64 `protobuf:"varint,9,opt,name=max_width,json=maxWidth,proto3" json:"max_width,omitempty"` +} + +func (x *TextOverlay) Reset() { + *x = TextOverlay{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TextOverlay) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TextOverlay) ProtoMessage() {} + +func (x *TextOverlay) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TextOverlay.ProtoReflect.Descriptor instead. +func (*TextOverlay) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{4} +} + +func (x *TextOverlay) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TextOverlay) GetX() int64 { + if x != nil { + return x.X + } + return 0 +} + +func (x *TextOverlay) GetY() int64 { + if x != nil { + return x.Y + } + return 0 +} + +func (x *TextOverlay) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +func (x *TextOverlay) GetFontSize() uint64 { + if x != nil { + return x.FontSize + } + return 0 +} + +func (x *TextOverlay) GetFontName() string { + if x != nil { + return x.FontName + } + return "" +} + +func (x *TextOverlay) GetFontWeight() string { + if x != nil { + return x.FontWeight + } + return "" +} + +func (x *TextOverlay) GetFontColor() uint32 { + if x != nil { + return x.FontColor + } + return 0 +} + +func (x *TextOverlay) GetMaxWidth() uint64 { + if x != nil { + return x.MaxWidth + } + return 0 +} + +type PipelineCreationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PipelineId *PipelineID `protobuf:"bytes,1,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` +} + +func (x *PipelineCreationResponse) Reset() { + *x = PipelineCreationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineCreationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineCreationResponse) ProtoMessage() {} + +func (x *PipelineCreationResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineCreationResponse.ProtoReflect.Descriptor instead. +func (*PipelineCreationResponse) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{5} +} + +func (x *PipelineCreationResponse) GetPipelineId() *PipelineID { + if x != nil { + return x.PipelineId + } + return nil +} + +type PipelineUpdates struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id *PipelineID `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Overlays []*Overlay `protobuf:"bytes,2,rep,name=overlays,proto3" json:"overlays,omitempty"` +} + +func (x *PipelineUpdates) Reset() { + *x = PipelineUpdates{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineUpdates) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineUpdates) ProtoMessage() {} + +func (x *PipelineUpdates) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineUpdates.ProtoReflect.Descriptor instead. +func (*PipelineUpdates) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{6} +} + +func (x *PipelineUpdates) GetId() *PipelineID { + if x != nil { + return x.Id + } + return nil +} + +func (x *PipelineUpdates) GetOverlays() []*Overlay { + if x != nil { + return x.Overlays + } + return nil +} + +type PipelineID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *PipelineID) Reset() { + *x = PipelineID{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineID) ProtoMessage() {} + +func (x *PipelineID) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineID.ProtoReflect.Descriptor instead. +func (*PipelineID) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{7} +} + +func (x *PipelineID) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type PipelineUpdateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PipelineId *PipelineID `protobuf:"bytes,1,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` +} + +func (x *PipelineUpdateResponse) Reset() { + *x = PipelineUpdateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineUpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineUpdateResponse) ProtoMessage() {} + +func (x *PipelineUpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineUpdateResponse.ProtoReflect.Descriptor instead. +func (*PipelineUpdateResponse) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{8} +} + +func (x *PipelineUpdateResponse) GetPipelineId() *PipelineID { + if x != nil { + return x.PipelineId + } + return nil +} + +type PipelineStartResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PipelineId *PipelineID `protobuf:"bytes,1,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` +} + +func (x *PipelineStartResponse) Reset() { + *x = PipelineStartResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineStartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineStartResponse) ProtoMessage() {} + +func (x *PipelineStartResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineStartResponse.ProtoReflect.Descriptor instead. +func (*PipelineStartResponse) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{9} +} + +func (x *PipelineStartResponse) GetPipelineId() *PipelineID { + if x != nil { + return x.PipelineId + } + return nil +} + +type PipelineStopResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PipelineId *PipelineID `protobuf:"bytes,1,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` +} + +func (x *PipelineStopResponse) Reset() { + *x = PipelineStopResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineStopResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineStopResponse) ProtoMessage() {} + +func (x *PipelineStopResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineStopResponse.ProtoReflect.Descriptor instead. +func (*PipelineStopResponse) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{10} +} + +func (x *PipelineStopResponse) GetPipelineId() *PipelineID { + if x != nil { + return x.PipelineId + } + return nil +} + +type PipelineDeleteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PipelineId *PipelineID `protobuf:"bytes,1,opt,name=pipeline_id,json=pipelineId,proto3" json:"pipeline_id,omitempty"` +} + +func (x *PipelineDeleteResponse) Reset() { + *x = PipelineDeleteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_api_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PipelineDeleteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PipelineDeleteResponse) ProtoMessage() {} + +func (x *PipelineDeleteResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_api_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PipelineDeleteResponse.ProtoReflect.Descriptor instead. +func (*PipelineDeleteResponse) Descriptor() ([]byte, []int) { + return file_api_api_proto_rawDescGZIP(), []int{11} +} + +func (x *PipelineDeleteResponse) GetPipelineId() *PipelineID { + if x != nil { + return x.PipelineId + } + return nil +} + +var File_api_api_proto protoreflect.FileDescriptor + +var file_api_api_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x03, 0x61, 0x70, 0x69, 0x22, 0x72, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6c, 0x69, 0x70, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x63, 0x6c, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x12, 0x26, 0x0a, 0x07, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x52, + 0x07, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x22, 0xb7, 0x02, 0x0a, 0x07, 0x4f, 0x76, 0x65, + 0x72, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x01, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x07, 0x62, 0x6c, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x65, + 0x6e, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x62, 0x6c, + 0x65, 0x6e, 0x64, 0x4f, 0x75, 0x74, 0x12, 0x29, 0x0a, 0x05, 0x65, 0x78, 0x74, 0x72, 0x61, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, 0x74, 0x72, + 0x61, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x65, 0x78, 0x74, 0x72, + 0x61, 0x12, 0x35, 0x0a, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, + 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x65, + 0x78, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x78, + 0x74, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x38, 0x0a, 0x0d, 0x69, 0x6d, 0x61, 0x67, + 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x6c, + 0x61, 0x79, 0x48, 0x00, 0x52, 0x0c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x6c, + 0x61, 0x79, 0x42, 0x0e, 0x0a, 0x0c, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x90, 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x72, 0x61, 0x4f, 0x76, 0x65, 0x72, + 0x6c, 0x61, 0x79, 0x12, 0x41, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x78, + 0x74, 0x72, 0x61, 0x4f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, + 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, + 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3e, 0x0a, 0x0c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x4f, 0x76, + 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x01, + 0x79, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0xe8, 0x01, 0x0a, 0x0b, 0x54, 0x65, 0x78, 0x74, 0x4f, 0x76, + 0x65, 0x72, 0x6c, 0x61, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x01, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, + 0x74, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x66, 0x6f, + 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x6f, 0x6e, 0x74, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6f, 0x6e, 0x74, 0x5f, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x6f, 0x6e, 0x74, 0x57, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x6f, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x66, 0x6f, 0x6e, 0x74, 0x43, 0x6f, + 0x6c, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x57, 0x69, 0x64, 0x74, 0x68, + 0x22, 0x4c, 0x0a, 0x18, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, + 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x49, 0x44, 0x52, 0x0a, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x5c, + 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x1f, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x28, 0x0a, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4f, 0x76, 0x65, 0x72, 0x6c, + 0x61, 0x79, 0x52, 0x08, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x73, 0x22, 0x1c, 0x0a, 0x0a, + 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x4a, 0x0a, 0x16, 0x50, 0x69, + 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x70, 0x69, 0x70, 0x65, + 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x49, 0x0a, 0x15, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x30, 0x0a, 0x0b, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, + 0x69, 0x6e, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, + 0x64, 0x22, 0x48, 0x0a, 0x14, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x70, 0x69, 0x70, + 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x52, + 0x0a, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x16, 0x50, + 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0b, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x70, 0x69, 0x70, + 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x32, 0xd0, 0x02, 0x0a, 0x0f, 0x50, 0x69, 0x70, 0x65, + 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x1d, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x0e, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x73, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x3c, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x49, 0x44, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, + 0x0a, 0x0c, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0f, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x1a, + 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x53, 0x74, + 0x6f, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0f, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x49, 0x44, 0x1a, 0x1b, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2f, + 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_api_api_proto_rawDescOnce sync.Once + file_api_api_proto_rawDescData = file_api_api_proto_rawDesc +) + +func file_api_api_proto_rawDescGZIP() []byte { + file_api_api_proto_rawDescOnce.Do(func() { + file_api_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_api_proto_rawDescData) + }) + return file_api_api_proto_rawDescData +} + +var file_api_api_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_api_api_proto_goTypes = []interface{}{ + (*Pipeline)(nil), // 0: api.Pipeline + (*Overlay)(nil), // 1: api.Overlay + (*ExtraOverlay)(nil), // 2: api.ExtraOverlay + (*ImageOverlay)(nil), // 3: api.ImageOverlay + (*TextOverlay)(nil), // 4: api.TextOverlay + (*PipelineCreationResponse)(nil), // 5: api.PipelineCreationResponse + (*PipelineUpdates)(nil), // 6: api.PipelineUpdates + (*PipelineID)(nil), // 7: api.PipelineID + (*PipelineUpdateResponse)(nil), // 8: api.PipelineUpdateResponse + (*PipelineStartResponse)(nil), // 9: api.PipelineStartResponse + (*PipelineStopResponse)(nil), // 10: api.PipelineStopResponse + (*PipelineDeleteResponse)(nil), // 11: api.PipelineDeleteResponse + nil, // 12: api.ExtraOverlay.PropertiesEntry +} +var file_api_api_proto_depIdxs = []int32{ + 1, // 0: api.Pipeline.overlay:type_name -> api.Overlay + 2, // 1: api.Overlay.extra:type_name -> api.ExtraOverlay + 4, // 2: api.Overlay.text_overlay:type_name -> api.TextOverlay + 3, // 3: api.Overlay.image_overlay:type_name -> api.ImageOverlay + 12, // 4: api.ExtraOverlay.properties:type_name -> api.ExtraOverlay.PropertiesEntry + 7, // 5: api.PipelineCreationResponse.pipeline_id:type_name -> api.PipelineID + 7, // 6: api.PipelineUpdates.id:type_name -> api.PipelineID + 1, // 7: api.PipelineUpdates.overlays:type_name -> api.Overlay + 7, // 8: api.PipelineUpdateResponse.pipeline_id:type_name -> api.PipelineID + 7, // 9: api.PipelineStartResponse.pipeline_id:type_name -> api.PipelineID + 7, // 10: api.PipelineStopResponse.pipeline_id:type_name -> api.PipelineID + 7, // 11: api.PipelineDeleteResponse.pipeline_id:type_name -> api.PipelineID + 0, // 12: api.PipelineService.CreatePipeline:input_type -> api.Pipeline + 6, // 13: api.PipelineService.UpdatePipeline:input_type -> api.PipelineUpdates + 7, // 14: api.PipelineService.StartPipeline:input_type -> api.PipelineID + 7, // 15: api.PipelineService.StopPipeline:input_type -> api.PipelineID + 7, // 16: api.PipelineService.DeletePipeline:input_type -> api.PipelineID + 5, // 17: api.PipelineService.CreatePipeline:output_type -> api.PipelineCreationResponse + 8, // 18: api.PipelineService.UpdatePipeline:output_type -> api.PipelineUpdateResponse + 9, // 19: api.PipelineService.StartPipeline:output_type -> api.PipelineStartResponse + 10, // 20: api.PipelineService.StopPipeline:output_type -> api.PipelineStopResponse + 11, // 21: api.PipelineService.DeletePipeline:output_type -> api.PipelineDeleteResponse + 17, // [17:22] is the sub-list for method output_type + 12, // [12:17] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name +} + +func init() { file_api_api_proto_init() } +func file_api_api_proto_init() { + if File_api_api_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_api_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Pipeline); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Overlay); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ExtraOverlay); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImageOverlay); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TextOverlay); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineCreationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineUpdates); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineID); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineUpdateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineStartResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineStopResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_api_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PipelineDeleteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_api_api_proto_msgTypes[1].OneofWrappers = []interface{}{ + (*Overlay_Extra)(nil), + (*Overlay_TextOverlay)(nil), + (*Overlay_ImageOverlay)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_api_api_proto_rawDesc, + NumEnums: 0, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_api_api_proto_goTypes, + DependencyIndexes: file_api_api_proto_depIdxs, + MessageInfos: file_api_api_proto_msgTypes, + }.Build() + File_api_api_proto = out.File + file_api_api_proto_rawDesc = nil + file_api_api_proto_goTypes = nil + file_api_api_proto_depIdxs = nil +} diff --git a/api/api.proto b/api/api.proto new file mode 100644 index 0000000..769f378 --- /dev/null +++ b/api/api.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; + +option go_package = "./api"; + +package api; + + +message Pipeline { + string name = 1; + string clip = 2; + string output = 3; + repeated Overlay overlay = 4; +} + +message Overlay { + string name = 1; + int64 x = 2; + int64 y = 3; + bool display = 4; + int64 blend_in = 5; + int64 blend_out = 6; + oneof overlay_data { + ExtraOverlay extra = 7; + TextOverlay text_overlay = 8; + ImageOverlay image_overlay = 9; + } +} + +message ExtraOverlay { + map properties = 1; +} + +message ImageOverlay { + int64 x = 2; + int32 y = 3; + string path = 7; +} + +message TextOverlay { + string name = 1; + int64 x = 2; + int64 y = 3; + string text = 4; + uint64 font_size = 5; + string font_name = 6; + string font_weight = 7; + uint32 font_color = 8; + uint64 max_width = 9; +} + +message PipelineCreationResponse { + PipelineID pipeline_id = 1; +} + +message PipelineUpdates { + PipelineID id = 1; + repeated Overlay overlays = 2; +} + +message PipelineID { + string id = 1; +} + +message PipelineUpdateResponse { + PipelineID pipeline_id = 1; +} + +message PipelineStartResponse { + PipelineID pipeline_id = 1; +} + +message PipelineStopResponse { + PipelineID pipeline_id = 1; +} + +message PipelineDeleteResponse { + PipelineID pipeline_id = 1; +} + +service PipelineService { + rpc CreatePipeline(Pipeline) returns (PipelineCreationResponse); + rpc UpdatePipeline(PipelineUpdates) returns (PipelineUpdateResponse); + rpc StartPipeline(PipelineID) returns (PipelineStartResponse); + rpc StopPipeline(PipelineID) returns (PipelineStopResponse); + rpc DeletePipeline(PipelineID) returns (PipelineDeleteResponse); +} \ No newline at end of file diff --git a/api/api_grpc.pb.go b/api/api_grpc.pb.go new file mode 100644 index 0000000..1a9981c --- /dev/null +++ b/api/api_grpc.pb.go @@ -0,0 +1,245 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package api + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// PipelineServiceClient is the client API for PipelineService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PipelineServiceClient interface { + CreatePipeline(ctx context.Context, in *Pipeline, opts ...grpc.CallOption) (*PipelineCreationResponse, error) + UpdatePipeline(ctx context.Context, in *PipelineUpdates, opts ...grpc.CallOption) (*PipelineUpdateResponse, error) + StartPipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineStartResponse, error) + StopPipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineStopResponse, error) + DeletePipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineDeleteResponse, error) +} + +type pipelineServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewPipelineServiceClient(cc grpc.ClientConnInterface) PipelineServiceClient { + return &pipelineServiceClient{cc} +} + +func (c *pipelineServiceClient) CreatePipeline(ctx context.Context, in *Pipeline, opts ...grpc.CallOption) (*PipelineCreationResponse, error) { + out := new(PipelineCreationResponse) + err := c.cc.Invoke(ctx, "/api.PipelineService/CreatePipeline", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pipelineServiceClient) UpdatePipeline(ctx context.Context, in *PipelineUpdates, opts ...grpc.CallOption) (*PipelineUpdateResponse, error) { + out := new(PipelineUpdateResponse) + err := c.cc.Invoke(ctx, "/api.PipelineService/UpdatePipeline", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pipelineServiceClient) StartPipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineStartResponse, error) { + out := new(PipelineStartResponse) + err := c.cc.Invoke(ctx, "/api.PipelineService/StartPipeline", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pipelineServiceClient) StopPipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineStopResponse, error) { + out := new(PipelineStopResponse) + err := c.cc.Invoke(ctx, "/api.PipelineService/StopPipeline", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pipelineServiceClient) DeletePipeline(ctx context.Context, in *PipelineID, opts ...grpc.CallOption) (*PipelineDeleteResponse, error) { + out := new(PipelineDeleteResponse) + err := c.cc.Invoke(ctx, "/api.PipelineService/DeletePipeline", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PipelineServiceServer is the server API for PipelineService service. +// All implementations must embed UnimplementedPipelineServiceServer +// for forward compatibility +type PipelineServiceServer interface { + CreatePipeline(context.Context, *Pipeline) (*PipelineCreationResponse, error) + UpdatePipeline(context.Context, *PipelineUpdates) (*PipelineUpdateResponse, error) + StartPipeline(context.Context, *PipelineID) (*PipelineStartResponse, error) + StopPipeline(context.Context, *PipelineID) (*PipelineStopResponse, error) + DeletePipeline(context.Context, *PipelineID) (*PipelineDeleteResponse, error) + mustEmbedUnimplementedPipelineServiceServer() +} + +// UnimplementedPipelineServiceServer must be embedded to have forward compatible implementations. +type UnimplementedPipelineServiceServer struct { +} + +func (UnimplementedPipelineServiceServer) CreatePipeline(context.Context, *Pipeline) (*PipelineCreationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreatePipeline not implemented") +} +func (UnimplementedPipelineServiceServer) UpdatePipeline(context.Context, *PipelineUpdates) (*PipelineUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdatePipeline not implemented") +} +func (UnimplementedPipelineServiceServer) StartPipeline(context.Context, *PipelineID) (*PipelineStartResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StartPipeline not implemented") +} +func (UnimplementedPipelineServiceServer) StopPipeline(context.Context, *PipelineID) (*PipelineStopResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method StopPipeline not implemented") +} +func (UnimplementedPipelineServiceServer) DeletePipeline(context.Context, *PipelineID) (*PipelineDeleteResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeletePipeline not implemented") +} +func (UnimplementedPipelineServiceServer) mustEmbedUnimplementedPipelineServiceServer() {} + +// UnsafePipelineServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PipelineServiceServer will +// result in compilation errors. +type UnsafePipelineServiceServer interface { + mustEmbedUnimplementedPipelineServiceServer() +} + +func RegisterPipelineServiceServer(s grpc.ServiceRegistrar, srv PipelineServiceServer) { + s.RegisterService(&PipelineService_ServiceDesc, srv) +} + +func _PipelineService_CreatePipeline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Pipeline) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PipelineServiceServer).CreatePipeline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.PipelineService/CreatePipeline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PipelineServiceServer).CreatePipeline(ctx, req.(*Pipeline)) + } + return interceptor(ctx, in, info, handler) +} + +func _PipelineService_UpdatePipeline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipelineUpdates) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PipelineServiceServer).UpdatePipeline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.PipelineService/UpdatePipeline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PipelineServiceServer).UpdatePipeline(ctx, req.(*PipelineUpdates)) + } + return interceptor(ctx, in, info, handler) +} + +func _PipelineService_StartPipeline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipelineID) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PipelineServiceServer).StartPipeline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.PipelineService/StartPipeline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PipelineServiceServer).StartPipeline(ctx, req.(*PipelineID)) + } + return interceptor(ctx, in, info, handler) +} + +func _PipelineService_StopPipeline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipelineID) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PipelineServiceServer).StopPipeline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.PipelineService/StopPipeline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PipelineServiceServer).StopPipeline(ctx, req.(*PipelineID)) + } + return interceptor(ctx, in, info, handler) +} + +func _PipelineService_DeletePipeline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PipelineID) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PipelineServiceServer).DeletePipeline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/api.PipelineService/DeletePipeline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PipelineServiceServer).DeletePipeline(ctx, req.(*PipelineID)) + } + return interceptor(ctx, in, info, handler) +} + +// PipelineService_ServiceDesc is the grpc.ServiceDesc for PipelineService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PipelineService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "api.PipelineService", + HandlerType: (*PipelineServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreatePipeline", + Handler: _PipelineService_CreatePipeline_Handler, + }, + { + MethodName: "UpdatePipeline", + Handler: _PipelineService_UpdatePipeline_Handler, + }, + { + MethodName: "StartPipeline", + Handler: _PipelineService_StartPipeline_Handler, + }, + { + MethodName: "StopPipeline", + Handler: _PipelineService_StopPipeline_Handler, + }, + { + MethodName: "DeletePipeline", + Handler: _PipelineService_DeletePipeline_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api/api.proto", +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..777eb61 --- /dev/null +++ b/default.nix @@ -0,0 +1,19 @@ +{ pkgs ? import { } }: +with pkgs; +mkShell { + nativeBuildInputs = [ + go_1_17 + gcc + gst_all_1.gstreamer + gst_all_1.gstreamer.dev + gst_all_1.gst-plugins-base + gst_all_1.gst-plugins-good + gst_all_1.gst-plugins-bad + gst_all_1.gst-plugins-ugly + pkg-config + protoc-gen-go + protoc-gen-go-grpc + protobuf + go-tools + ]; +} \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ac634de --- /dev/null +++ b/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1638122382, + "narHash": "sha256-sQzZzAbvKEqN9s0bzWuYmRaA03v40gaJ4+iL1LXjaeI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "74f7e4319258e287b0f9cb95426c9853b282730b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1642104392, + "narHash": "sha256-m71b7MgMh9FDv4MnI5sg9MiBVW6DhE1zq+d/KlLWSC8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5aaed40d22f0d9376330b6fa413223435ad6fee5", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9441a45 --- /dev/null +++ b/flake.nix @@ -0,0 +1,18 @@ +{ + description = "gstreamer grafik server"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let pkgs = nixpkgs.legacyPackages.${system}; in + { + devShell = import ./default.nix { inherit pkgs; }; + defaultPackage = import ./package.nix { inherit pkgs; inherit self; }; + } + ); +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..a6c14fb --- /dev/null +++ b/go.mod @@ -0,0 +1,27 @@ +module git.entr0py.de/garionion/gstreamer-graphix + +go 1.17 + +require ( + github.com/google/uuid v1.3.0 + github.com/ilyakaznacheev/cleanenv v1.2.6 + github.com/rs/zerolog v1.26.1 + github.com/tinyzimmer/go-glib v0.0.24 + github.com/tinyzimmer/go-gst v0.2.32 + google.golang.org/grpc v1.44.0 + google.golang.org/protobuf v1.27.1 +) + +require ( + github.com/BurntSushi/toml v1.0.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/joho/godotenv v1.4.0 // indirect + github.com/mattn/go-pointer v0.0.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c5e00d3 --- /dev/null +++ b/go.sum @@ -0,0 +1,184 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/ilyakaznacheev/cleanenv v1.2.6 h1:oJRaVZfAI0xdA5LJNguuKH2ldVJg44SP8GqkEn/cw7w= +github.com/ilyakaznacheev/cleanenv v1.2.6/go.mod h1:C3bB+MJ+LjECYlw2k7CSagKGfL1Ym2ywfjj40RjXJ24= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= +github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tinyzimmer/go-glib v0.0.24 h1:ktZZC22/9t88kGRgNEFV/SESgIWhGHE+q7Z7Qj++luw= +github.com/tinyzimmer/go-glib v0.0.24/go.mod h1:ltV0gO6xNFzZhsIRbFXv8RTq9NGoNT2dmAER4YmZfaM= +github.com/tinyzimmer/go-gst v0.2.32 h1:bwJ1VfLyoeQPxuE7LgCTwwvMXFufnFoSws7QhaCfsY8= +github.com/tinyzimmer/go-gst v0.2.32/go.mod h1:V4h+HPS3mVGSwUJ7IBi3WAkJWITZasebERyqk3TEXUM= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e h1:hXl9hnyOkeznztYpYxVPAVZfPzcbO6Q0C+nLXodza8k= +google.golang.org/genproto v0.0.0-20220204002441-d6cc3cc0770e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= diff --git a/gstreamer/Decklink.go b/gstreamer/Decklink.go new file mode 100644 index 0000000..4a7f70a --- /dev/null +++ b/gstreamer/Decklink.go @@ -0,0 +1,15 @@ +package gstreamer + +import "errors" + +func (g *Gstreamer) getDecklinkOutput(name string) (string, error) { + g.Decklink.Lock() + for slot, pipelineName := range g.Decklink.Slots { + if pipelineName == "" { + g.Decklink.Slots[slot] = name + g.Decklink.Unlock() + return slot, nil + } + } + return "", errors.New("No free Decklink slot") +} diff --git a/gstreamer/ImageOverlay.go b/gstreamer/ImageOverlay.go new file mode 100644 index 0000000..44b1f3d --- /dev/null +++ b/gstreamer/ImageOverlay.go @@ -0,0 +1,72 @@ +package gstreamer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" + "time" +) + +func (o *ImageOverlay) create(log zerolog.Logger) (*gst.Element, error) { + element, err := gst.NewElementWithName("gdkpixbufoverlay", o.Name) + if err != nil { + log.Error().Msgf("could not create image overlay %s: %v\n", o.Name, err) + return nil, fmt.Errorf("could not create image overlay %s", o.Name) + } + setPropertyWrapper(element, "positioning-mode", 1) // pixels-absolute + setPropertyWrapper(element, "location", o.Path) + + o.update(log) + o.element = element + + return element, err +} + +func (o *ImageOverlay) update(_ zerolog.Logger) { + setPropertyWrapper(o.element, "offset-x", o.X) + setPropertyWrapper(o.element, "offset-y", o.Y) + + if o.Display { + o.show() + } else { + o.hide() + } +} + +func (o *ImageOverlay) show() gst.ClockCallback { + return func(clock *gst.Clock, clockTime time.Duration) bool { + setPropertyWrapper(o.element, "alpha", 1) + return true + } +} + +func (o *ImageOverlay) hide() gst.ClockCallback { + return func(clock *gst.Clock, clockTime time.Duration) bool { + setPropertyWrapper(o.element, "alpha", 0) + return true + } +} + +func (o *ImageOverlay) getName() string { + return o.Name +} + +func (o *ImageOverlay) getBlendInTime() time.Duration { + return o.BlendIn +} + +func (o *ImageOverlay) getBlendOutTime() time.Duration { + return o.BlendOut +} + +func (o *ImageOverlay) getElement() *gst.Element { + return o.element +} + +func (o *ImageOverlay) setElement(element *gst.Element) { + o.element = element +} + +func (o *ImageOverlay) getType() string { + return "imageoverlay" +} diff --git a/gstreamer/TextOverlay.go b/gstreamer/TextOverlay.go new file mode 100644 index 0000000..d3330ca --- /dev/null +++ b/gstreamer/TextOverlay.go @@ -0,0 +1,109 @@ +package gstreamer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" + "strconv" + "strings" + "time" +) + +func (o *TextOverlay) create(log zerolog.Logger) (*gst.Element, error) { + + e, err := gst.NewElementWithName("textoverlay", o.Name) + if err != nil { + log.Error().Msgf("could not create element %s: %v\n", o.Name, err) + return nil, fmt.Errorf("could not create element %s", o.Name) + } + setPropertyWrapper(e, "draw-outline", false) + setPropertyWrapper(e, "draw-shadow", false) + setPropertyWrapper(e, "halignment", "0") //left + setPropertyWrapper(e, "valignment", "2") //top + setPropertyWrapper(e, "deltax", o.X) + setPropertyWrapper(e, "deltay", o.Y) + setPropertyWrapper(e, "color", o.Color) + setPropertyWrapper(e, "font-desc", fmt.Sprintf("%s, %s %v", o.Font, o.FontWeight, o.FontSize)) + + setPropertyWrapper(e, "text", o.Value) + + if o.Display { + o.show() + } else { + o.hide() + } + o.element = e + + return e, nil +} + +func (o *TextOverlay) update(log zerolog.Logger) { + text, _ := o.element.GetProperty("text") + if text != o.Value { + setPropertyWrapper(o.element, "text", o.Value) + setPropertyWrapper(o.element, "font-desc", fmt.Sprintf("Fira Sans, %s %v", o.FontWeight, o.FontSize)) + } + setPropertyWrapper(o.element, "deltax", o.X) + setPropertyWrapper(o.element, "deltay", o.Y) + if o.Display { + o.show() + } else { + o.hide() + } + o.resizeTextOverlay(log) +} + +func (o *TextOverlay) show() gst.ClockCallback { + return func(clock *gst.Clock, clockTime time.Duration) bool { + setPropertyWrapper(o.element, "silent", 0) + return true + } +} + +func (o *TextOverlay) hide() gst.ClockCallback { + return func(clock *gst.Clock, clockTime time.Duration) bool { + setPropertyWrapper(o.element, "silent", 1) + return true + } +} + +func (o *TextOverlay) getName() string { + return o.Name +} + +func (o *TextOverlay) resizeTextOverlay(log zerolog.Logger) { + if o.MaxWidth == 0 { + return + } + width, _ := o.element.GetProperty("text-width") + + for width.(uint) >= o.MaxWidth { + log.Debug().Str("name", o.Name).Uint("width", width.(uint)).Uint("max-width", o.MaxWidth).Msg("Shrinking Textoverlay") + fontdesc, _ := o.element.GetProperty("font-desc") + fd := strings.Fields(fontdesc.(string)) + fontsize, _ := strconv.Atoi(fd[len(fd)-1]) + setPropertyWrapper(o.element, "font-desc", fmt.Sprintf("%s, %s %v", o.Font, o.FontWeight, fontsize-1)) + time.Sleep(time.Millisecond * 60) + width, _ = o.element.GetProperty("text-width") + } +} + +func (o TextOverlay) getBlendInTime() time.Duration { + return o.BlendIn +} + +func (o TextOverlay) getBlendOutTime() time.Duration { + return o.BlendOut +} + +func (o *TextOverlay) getElement() *gst.Element { + return o.element +} + +func (o *TextOverlay) setElement(element *gst.Element) { + o.element = element +} + +func (o *TextOverlay) getType() string { + return "textoverlay" +} diff --git a/gstreamer/gstreamer.go b/gstreamer/gstreamer.go new file mode 100644 index 0000000..4c16d00 --- /dev/null +++ b/gstreamer/gstreamer.go @@ -0,0 +1,33 @@ +package gstreamer + +import ( + "errors" + "github.com/tinyzimmer/go-glib/glib" + "github.com/tinyzimmer/go-gst/gst" +) + +var gstreamer *Gstreamer + +func Init() *Gstreamer { + gst.Init(nil) + glib.NewMainLoop(glib.MainContextDefault(), true) + gstreamer = &Gstreamer{} + return gstreamer +} + +func (g *Gstreamer) DeletePipeline(id string) { + g.Lock() + if pipeline, err := g.GetPipeline(id); err == nil { + delete(g.Pipelines, id) + pipeline.ctxCancel() + } + g.Unlock() +} + +func (g *Gstreamer) GetPipeline(id string) (*Pipeline, error) { + g.RLock() + if pipeline, ok := g.Pipelines[id]; ok { + return pipeline, nil + } + return nil, errors.New("Pipeline not found") +} diff --git a/gstreamer/input.go b/gstreamer/input.go new file mode 100644 index 0000000..95f6e11 --- /dev/null +++ b/gstreamer/input.go @@ -0,0 +1,119 @@ +package gstreamer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" +) + +func (i *InputElement) create(pipeline *gst.Pipeline, log zerolog.Logger) error { + elements := map[string]element{} + e, err := gst.NewElementWithName(i.Type, i.Name) + if err != nil { + log.Error().Msgf("could not create element %s: %v\n", i.Name, err) + return fmt.Errorf("could not create element %s", i.Name) + } + for prop, value := range i.Properties { + setPropertyWrapper(e, prop, value) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add element %s to pipeline: %v\n", i.Name, err) + return fmt.Errorf("could not add element %s to pipeline", i.Name) + } + input := element{element: e, name: i.Name, next: fmt.Sprintf("%s-decodebin", i.Name)} + elements[i.Name] = input + // ==================== + + //==================== + // DECODEBIN + e, err = gst.NewElementWithName("decodebin", fmt.Sprintf("%s-decodebin", i.Name)) + if err != nil { + log.Error().Msgf("could not create decodebin for %s: %v\n", i.Name, err) + return fmt.Errorf("could not create decodebin for %s", i.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add decodebin for %s to pipeline: %v\n", i.Name, err) + return fmt.Errorf("could not add decodebin for %s to pipeline", i.Name) + } + decodebin := element{element: e, name: fmt.Sprintf("%s-decodebin", i.Name), prev: i.Name, next: fmt.Sprintf("%s-videoconvert", i.Name)} + elements[fmt.Sprintf("%s-decodebin", i.Name)] = decodebin + err = input.linkElementWrapper(&decodebin, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", i.Name, fmt.Sprintf("%s-decodebin", i.Name), err) + return fmt.Errorf("could not link %s to %s", i.Name, fmt.Sprintf("%s-decodebin", i.Name)) + } + //==================== + + // ==================== + // VIDEOCONVERT + e, err = gst.NewElementWithName("videoconvert", fmt.Sprintf("%s-videoconvert", i.Name)) + if err != nil { + log.Error().Msgf("could not create videoconvert for %s: %v\n", i.Name, err) + return fmt.Errorf("could not create videoconvert for %s", i.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videoconvert for %s to pipeline: %v\n", i.Name, err) + return fmt.Errorf("could not add videoconvert for %s to pipeline", i.Name) + } + videoconvert := element{element: e, name: fmt.Sprintf("%s-videoconvert", i.Name), prev: fmt.Sprintf("%s-decodebin", i.Name), next: fmt.Sprintf("%s-videoscale", i.Name)} + elements[fmt.Sprintf("%s-videoconvert", i.Name)] = videoconvert + err = input.linkElementWrapper(&videoconvert, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", i.Name, fmt.Sprintf("%s-videoconvert", i.Name), err) + return fmt.Errorf("could not link %s to %s", i.Name, fmt.Sprintf("%s-videoconvert", i.Name)) + } + //===================== + + //===================== + // VIDEOSCALE + e, err = gst.NewElementWithName("videoscale", fmt.Sprintf("%s-videoscale", i.Name)) + if err != nil { + log.Error().Msgf("could not create videoscale for %s: %v\n", i.Name, err) + return fmt.Errorf("could not create videoscale for %s", i.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videoscale for %s to pipeline: %v\n", i.Name, err) + return fmt.Errorf("could not add videoscale for %s to pipeline", i.Name) + } + videoscale := element{element: e, name: fmt.Sprintf("%s-videoscale", i.Name), prev: fmt.Sprintf("%s-videoconvert", i.Name), next: fmt.Sprintf("%s-videorate", i.Name)} + elements[fmt.Sprintf("%s-videoscale", i.Name)] = videoscale + err = videoconvert.linkElementWrapper(&videoscale, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", fmt.Sprintf("%s-videoconvert", i.Name), fmt.Sprintf("%s-videoscale", i.Name), err) + return fmt.Errorf("could not link %s to %s", fmt.Sprintf("%s-videoconvert", i.Name), fmt.Sprintf("%s-videoscale", i.Name)) + } + //===================== + + //===================== + // VIDEORATE + e, err = gst.NewElementWithName("videorate", fmt.Sprintf("%s-videorate", i.Name)) + if err != nil { + log.Error().Msgf("could not create videorate for %s: %v\n", i.Name, err) + return fmt.Errorf("could not create videorate for %s", i.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videorate for %s to pipeline: %v\n", i.Name, err) + return fmt.Errorf("could not add videorate for %s to pipeline", i.Name) + } + videorate := element{element: e, name: fmt.Sprintf("%s-videorate", i.Name), prev: fmt.Sprintf("%s-videoscale", i.Name), outputCaps: i.Caps} + elements[fmt.Sprintf("%s-videorate", i.Name)] = videorate + err = videoscale.linkElementWrapper(&videorate, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", fmt.Sprintf("%s-videoscale", i.Name), fmt.Sprintf("%s-videorate", i.Name), err) + return fmt.Errorf("could not link %s to %s", fmt.Sprintf("%s-videoscale", i.Name), fmt.Sprintf("%s-videorate", i.Name)) + } + + i.elements = elements + + return nil +} + +func (i *InputElement) getLastElement() *element { + e := i.elements[fmt.Sprintf("%s-videorate", i.Name)] + return &e +} diff --git a/gstreamer/output.go b/gstreamer/output.go new file mode 100644 index 0000000..ba3188c --- /dev/null +++ b/gstreamer/output.go @@ -0,0 +1,85 @@ +package gstreamer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" +) + +func (o *OutputElement) create(pipeline *gst.Pipeline, lastElement *element, log zerolog.Logger) error { + elements := map[string]element{} + + e, err := gst.NewElementWithName("videoconvert", fmt.Sprintf("%s-videoconvert", o.Name)) + if err != nil { + log.Error().Msgf("could not create videoconvert for %s: %v\n", o.Name, err) + return fmt.Errorf("could not create videoconvert for %s", o.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videoconvert for %s to pipeline: %v\n", o.Name, err) + return fmt.Errorf("could not add videoconvert for %s to pipeline", o.Name) + } + videoconvert := element{element: e, name: fmt.Sprintf("%s-videoconvert", o.Name), prev: lastElement.name, next: fmt.Sprintf("%s-videoscale", o.Name), inputCaps: o.Caps} + elements[fmt.Sprintf("%s-videoconvert", o.Name)] = videoconvert + err = lastElement.linkElementWrapper(&videoconvert, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", o.Name, fmt.Sprintf("%s-videoconvert", o.Name), err) + return fmt.Errorf("could not link %s to %s", o.Name, fmt.Sprintf("%s-videoconvert", o.Name)) + } + + e, err = gst.NewElementWithName("videoscale", fmt.Sprintf("%s-videoscale", o.Name)) + if err != nil { + log.Error().Msgf("could not create videoscale for %s: %v\n", o.Name, err) + return fmt.Errorf("could not create videoscale for %s", o.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videoscale for %s to pipeline: %v\n", o.Name, err) + return fmt.Errorf("could not add videoscale for %s to pipeline", o.Name) + } + videoscale := element{element: e, name: fmt.Sprintf("%s-videoscale", o.Name), prev: fmt.Sprintf("%s-videoconvert", o.Name), next: fmt.Sprintf("%s-videorate", o.Name)} + elements[fmt.Sprintf("%s-videoscale", o.Name)] = videoscale + err = videoconvert.linkElementWrapper(&videoscale, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", fmt.Sprintf("%s-videoconvert", o.Name), fmt.Sprintf("%s-videoscale", o.Name), err) + return fmt.Errorf("could not link %s to %s", fmt.Sprintf("%s-videoconvert", o.Name), fmt.Sprintf("%s-videoscale", o.Name)) + } + + e, err = gst.NewElementWithName("videorate", fmt.Sprintf("%s-videorate", o.Name)) + if err != nil { + log.Error().Msgf("could not create videorate for %s: %v\n", o.Name, err) + return fmt.Errorf("could not create videorate for %s", o.Name) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add videorate for %s to pipeline: %v\n", o.Name, err) + return fmt.Errorf("could not add videorate for %s to pipeline", o.Name) + } + videorate := element{element: e, name: fmt.Sprintf("%s-videorate", o.Name), prev: fmt.Sprintf("%s-videoscale", o.Name), next: o.Name} + elements[fmt.Sprintf("%s-videorate", o.Name)] = videorate + err = videoscale.linkElementWrapper(&videorate, log) + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", fmt.Sprintf("%s-videoscale", o.Name), fmt.Sprintf("%s-videorate", o.Name), err) + return fmt.Errorf("could not link %s to %s", fmt.Sprintf("%s-videoscale", o.Name), fmt.Sprintf("%s-videorate", o.Name)) + } + + e, err = gst.NewElementWithName(o.Type, o.Name) + if err != nil { + log.Error().Msgf("could not create element %s: %v\n", o.Name, err) + return fmt.Errorf("could not create element %s", o.Name) + } + for prop, value := range o.Properties { + setPropertyWrapper(e, prop, value) + } + err = pipeline.Add(e) + if err != nil { + log.Error().Msgf("could not add element %s to pipeline: %v\n", o.Name, err) + return fmt.Errorf("could not add element %s to pipeline", o.Name) + } + output := element{element: e, name: o.Name, prev: fmt.Sprintf("%s-videoscale", o.Name)} + elements[o.Name] = output + + o.elements = elements + + return nil +} diff --git a/gstreamer/pipeline.go b/gstreamer/pipeline.go new file mode 100644 index 0000000..b93e285 --- /dev/null +++ b/gstreamer/pipeline.go @@ -0,0 +1,157 @@ +package gstreamer + +import ( + "context" + "fmt" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/tinyzimmer/go-gst/gst" + "sync" +) + +func (p *Pipeline) Create() (chan PipelineUpdates, error) { + gstreamerLogger := log.With().Str("Component", "gstreamer").Str("Pipeline", p.Name).Logger() + p.logger = gstreamerLogger + pipeline, err := gst.NewPipeline(p.Name) + if err != nil { + return nil, err + } + + elements := map[string]element{} + + err = p.InputElement.create(pipeline, gstreamerLogger) + if err != nil { + return nil, err + } + for name, e := range p.InputElement.elements { + elements[name] = e + } + lastElement := p.InputElement.getLastElement() + + for _, overlay := range p.Overlays { + gstreamerLogger.Debug().Str("Overlay", overlay.getName()).Msg("Adding overlay") + overlayElement, _ := overlay.create(gstreamerLogger) + e := &element{ + element: overlayElement, + name: overlay.getName(), + } + lastElement.linkElementWrapper(e, gstreamerLogger) + lastElement = e + elements[overlay.getName()] = *lastElement + } + err = p.OutputElement.create(pipeline, lastElement, gstreamerLogger) + if err != nil { + return nil, err + } + for name, e := range p.OutputElement.elements { + elements[name] = e + } + + pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool { + switch msg.Type() { + case gst.MessageEOS: // When end-of-stream is received flush the pipeline and stop the main loop + pipeline.BlockSetState(gst.StateNull) + case gst.MessageError: // Error messages are always fatal + err := msg.ParseError() + gstreamerLogger.Error().Msg(err.Error()) + if debug := err.DebugString(); debug != "" { + gstreamerLogger.Debug().Msg(debug) + } + case gst.MessageStreamStart: + gstreamerLogger.Info().Msgf("STARTING Playout of %s", p.Name) + clock := pipeline.GetPipelineClock() + now := clock.GetTime() + for _, overlay := range p.Overlays { + if overlay.getBlendInTime() != -1 { + clock.NewSingleShotID(now + overlay.getBlendInTime()).WaitAsync(overlay.show()) + } + if overlay.getBlendOutTime() != -1 { + clock.NewSingleShotID(now + overlay.getBlendOutTime()).WaitAsync(overlay.hide()) + } + } + + case gst.MessageStateChanged: + _, newState := msg.ParseStateChanged() + if newState == gst.StatePlaying && msg.Source() == p.Name { + gstreamerLogger.Debug().Msgf("Pipeline %s started", p.Name) + p.resizeAllTextOverlays(gstreamerLogger) + } + default: + // All messages implement a Stringer. However, this is + // typically an expensive thing to do and should be avoided. + gstreamerLogger.Debug().Msgf(msg.String()) + } + return true + }) + + p.ctx, p.ctxCancel = context.WithCancel(gstreamer.ctx) + updateChannel := make(chan PipelineUpdates) + go p.overlayUpdater(updateChannel, gstreamerLogger) + p.PipelineUpdates = updateChannel + gstreamer.Lock() + gstreamer.Pipelines[p.ID] = p + gstreamer.Unlock() + + return updateChannel, nil +} + +//TODO check if output is decklink +func (p *Pipeline) Start() error { + output, err := gstreamer.getDecklinkOutput(p.Name) + setPropertyWrapper(p.elements[p.OutputElement.Name].element, "device-number", output) + err = p.pipeline.SetState(gst.StatePlaying) + if err != nil { + p.logger.Error().Msgf("Failed to set pipeline %s to playing: %v", p.Name, err) + return fmt.Errorf("Failed to set pipeline %s to playing: %v", p.Name, err) + } + return nil +} + +func (p *Pipeline) Stop() error { + err := p.pipeline.SetState(gst.StateNull) + if err != nil { + p.logger.Error().Msgf("Failed to set pipeline %s to null: %v", p.Name, err) + return fmt.Errorf("Failed to set pipeline %s to null: %v", p.Name, err) + } + return nil +} + +func (p *Pipeline) resizeAllTextOverlays(log zerolog.Logger) { + for _, overlay := range p.Overlays { + if x, ok := overlay.(*TextOverlay); ok { + go x.resizeTextOverlay(log) + } + } +} + +func (p *Pipeline) overlayUpdater(updateChannel chan PipelineUpdates, log zerolog.Logger) { + for { + select { + case <-p.ctx.Done(): + return + case updates := <-updateChannel: + log.Info().Msgf("Updating overlays with %d text overlays", len(updates.Overlays)) + + var wg sync.WaitGroup + for _, overlay := range updates.Overlays { + wg.Add(1) + go func(overlay Overlay) { + defer wg.Done() + o, ok := p.Overlays[overlay.getName()] + if !ok { + log.Error().Msgf("Could not find overlay %s", overlay.getName()) + return + } + overlay.setElement(o.getElement()) + overlay.update(log) + + }(overlay) + + } + wg.Wait() + log.Debug().Msg("Overlays updated") + //TODO channel done + } + } + +} diff --git a/gstreamer/types.go b/gstreamer/types.go new file mode 100644 index 0000000..4b56405 --- /dev/null +++ b/gstreamer/types.go @@ -0,0 +1,101 @@ +package gstreamer + +import ( + "context" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" + "sync" + "time" +) + +type Gstreamer struct { + Pipelines map[string]*Pipeline + *Decklink + ctx context.Context +} + +type Pipeline struct { + Name string + ID string + InputElement InputElement + OutputElement OutputElement + Overlays map[string]Overlay + PipelineUpdates chan PipelineUpdates + pipeline *gst.Pipeline + logger zerolog.Logger + elements map[string]element + ctx context.Context + ctxCancel context.CancelFunc + sync.RWMutex +} + +type Decklink struct { + Slots map[string]string + sync.RWMutex +} + +type PipelineUpdates struct { + Overlays map[string]Overlay +} + +type element struct { + prev, next string + element *gst.Element + name string + inputCaps []string + outputCaps []string +} + +type Overlay interface { + getName() string + create(log zerolog.Logger) (*gst.Element, error) + getBlendInTime() time.Duration + getBlendOutTime() time.Duration + show() gst.ClockCallback + hide() gst.ClockCallback + update(log zerolog.Logger) + getElement() *gst.Element + setElement(element *gst.Element) + getType() string +} + +type ImageOverlay struct { + element *gst.Element + Name string + Path string + X, Y int + Display bool + BlendIn time.Duration + BlendOut time.Duration +} + +type TextOverlay struct { + element *gst.Element + Name string + X, Y int + Font string + FontSize int + FontWeight string + Color uint32 + Value string + Display bool + MaxWidth uint + BlendIn time.Duration + BlendOut time.Duration +} + +type InputElement struct { + Name string + Type string + Properties map[string]string + Caps []string + elements map[string]element +} + +type OutputElement struct { + Name string + Type string + Properties map[string]string + Caps []string + elements map[string]element +} diff --git a/gstreamer/utils.go b/gstreamer/utils.go new file mode 100644 index 0000000..224f77a --- /dev/null +++ b/gstreamer/utils.go @@ -0,0 +1,47 @@ +package gstreamer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/tinyzimmer/go-gst/gst" + "strings" +) + +func setPropertyWrapper(element *gst.Element, name string, value interface{}) { + element.SetArg(name, fmt.Sprintf("%v", value)) +} + +func (element1 *element) linkElementWrapper(element2 *element, log zerolog.Logger, capabilities ...string) error { + var err error + var caps string + if len(capabilities) > 0 { + caps = strings.Join(capabilities, ", ") + } + if len(element1.outputCaps) > 0 { + if caps != "" { + log.Error().Str("action", "linking").Str("element1", element1.name).Str("element2", element2.name).Msg("element1 has output caps but you specified already caps so i'm only using them") + } else { + caps = strings.Join(element1.outputCaps, ",") + } + } + if len(element2.inputCaps) > 0 { + if caps != "" { + log.Error().Str("action", "linking").Str("element1", element1.name).Str("element2", element2.name).Msg("element2 has input caps but you specified already caps so i'm only using them") + } else { + caps = strings.Join(element2.inputCaps, ",") + } + } + if len(caps) > 0 { + capabilities := gst.NewCapsFromString(caps) + err = element1.element.LinkFiltered(element2.element, capabilities) + } else { + err = element1.element.Link(element2.element) + } + if err != nil { + log.Error().Msgf("could not link %s to %s: %v\n", element2.name, element1.name, err) + return fmt.Errorf("could not link %s to %s", element2.name, element1.name) + } + element1.next, element2.prev = element2.name, element1.name + + return nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..6adf7f7 --- /dev/null +++ b/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "flag" + "git.entr0py.de/garionion/gstreamer-graphix/api" + "git.entr0py.de/garionion/gstreamer-graphix/gstreamer" + "github.com/ilyakaznacheev/cleanenv" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/rs/zerolog/pkgerrors" + "google.golang.org/grpc" + "net" + "os" + "time" +) + +type Config struct { + Outputs []string `yaml:"outputs"` + Address string `yaml:"address" env:"ADDRESS" env-default:":3000"` + LogFile string `yaml:"logfile" env:"LOGFILE" env-default:"./gstreamer-graphix.log"` +} + +var cfg Config + +func main() { + zerolog.TimeFieldFormat = time.RFC1123Z + consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC1123Z} + log.Logger = log.Output(consoleWriter).With().Timestamp().Caller().Logger() + + if err := cleanenv.ReadConfig("config.toml", &cfg); err != nil { + log.Fatal().Msgf("No configfile: ", err) + } + + fset := flag.NewFlagSet("config", flag.ContinueOnError) + fset.Usage = cleanenv.FUsage(fset.Output(), &cfg, nil, fset.Usage) + fset.Parse(os.Args[1:]) + + l, err := os.OpenFile(cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664) + multi := zerolog.MultiLevelWriter(consoleWriter, l) + log.Logger = zerolog.New(multi).With().Timestamp().Caller().Logger() + + zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack + + ln, err := net.Listen("tcp", cfg.Address) + if err != nil { + log.Fatal().Err(err) + } + + gst := gstreamer.Init() + + pipelineService := &api.PipelineService{Gstreamer: gst} + + g := grpc.NewServer() + api.RegisterPipelineServiceServer(g, pipelineService) + + log.Fatal().Msgf("Failed to serve: %v", g.Serve(ln)) +} diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..1c2a6d5 --- /dev/null +++ b/package.nix @@ -0,0 +1,42 @@ +{ pkgs ? import { }, self }: +with pkgs; +assert lib.versionAtLeast go.version "1.16"; +let + version = "0.0.1"; + deps = [ + gst_all_1.gstreamer + gst_all_1.gstreamer.dev + gst_all_1.gst-plugins-base + gst_all_1.gst-plugins-good + gst_all_1.gst-plugins-bad + gst_all_1.gst-plugins-ugly + protoc-gen-go + ]; + nativeDeps = [ + gcc + pkg-config + ]; +in +pkgs.buildGo117Module { + pname = "gstreamer-grafix"; + inherit version; + + src = self; + + buildInputs = [ + ] ++deps; + nativeBuildInputs = [ + ] ++nativeDeps; + + tags = [ ]; + + allowGoReference = true; + + #vendorSha256 = lib.fakeSha256; + vendorSha256 = "sha256-Hhrhi/TBTo9pS6A5F4Q3LeJnW0z/ajN6jg5EAdo8dgA="; + + #meta = { + # description = "An MVC framework in Go, inspired by Ruby on Rails"; + # homepage = "https://gobuffalo.io"; + #}; +} \ No newline at end of file