# frozen_string_literal: true require "livekit/proto/livekit_egress_twirp" require "livekit/auth_mixin" require 'livekit/utils' module LiveKit class EgressServiceClient < Twirp::Client client_for Proto::EgressService include AuthMixin attr_accessor :api_key, :api_secret def initialize(base_url, api_key: nil, api_secret: nil) super(File.join(Utils.to_http_url(base_url), "/twirp")) @api_key = api_key @api_secret = api_secret end def start_room_composite_egress( room_name, # EncodedFileOutput, SegmentedFileOutput, StreamOutput, ImageOutput or array containing up to one of each output, # EncodingOptionsPreset, only one of preset or advanced could be set preset: nil, # EncodingOptions, only one of preset or advanced could be set advanced: nil, # egress template url, otherwise uses default templates custom_base_url: nil, # egress layout layout: nil, # true to record only audio audio_only: false, # true to record only video video_only: false ) request = Proto::RoomCompositeEgressRequest.new( room_name: room_name, preset: preset, advanced: advanced, layout: layout, custom_base_url: custom_base_url, audio_only: audio_only, video_only: video_only, ) self.set_output(request, output) self.rpc( :StartRoomCompositeEgress, request, headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def start_participant_egress( room_name, identity, # EncodedFileOutput, SegmentedFileOutput, StreamOutput, ImageOutput, or array containing up to one of each output, # true to record the participant's screenshare and screenshare_audio track screen_share: false, # EncodingOptionsPreset, only one of preset or advanced could be set preset: nil, # EncodingOptions, only one of preset or advanced could be set advanced: nil ) request = Proto::ParticipantEgressRequest.new( room_name: room_name, identity: identity, screen_share: screen_share, preset: preset, advanced: advanced, ) self.set_output(request, output) self.rpc( :StartParticipantEgress, request, headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def start_track_composite_egress( room_name, # EncodedFileOutput, SegmentedFileOutput, StreamOutput, ImageOutput, or array containing up to one of each output, # TrackID of an audio track audio_track_id: nil, # TrackID of a video track video_track_id: nil, # EncodingOptionsPreset, only one of preset or advanced could be set preset: nil, # EncodingOptions, only one of preset or advanced could be set advanced: nil ) request = Proto::TrackCompositeEgressRequest.new( room_name: room_name, preset: preset, advanced: advanced, audio_track_id: audio_track_id, video_track_id: video_track_id, ) self.set_output(request, output) self.rpc( :StartTrackCompositeEgress, request, headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def start_track_egress( room_name, # either a DirectFileOutput - egress to storage or string - egress to WebSocket URL output, track_id ) request = Proto::TrackEgressRequest.new( room_name: room_name, track_id: track_id, ) if output.filepath request.file = output else request.websocket_url = output end self.rpc( :StartTrackEgress, request, headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def start_web_egress( url, # EncodedFileOutput, SegmentedFileOutput, StreamOutput, ImageOutput, or array containing up to one of each output, # EncodingOptionsPreset, only one of preset or advanced could be set preset: nil, # EncodingOptions, only one of preset or advanced could be set advanced: nil, # true to record only audio audio_only: false, # true to record only video video_only: false, # true to await START_RECORDING chrome log await_start_signal: false ) request = Proto::WebEgressRequest.new( url: url, preset: preset, advanced: advanced, audio_only: audio_only, video_only: video_only, await_start_signal: await_start_signal, ) self.set_output(request, output) self.rpc( :StartWebEgress, request, headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def update_layout(egress_id, layout) self.rpc( :UpdateLayout, Proto::UpdateLayoutRequest.new( egress_id: egress_id, layout: layout, ), headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def update_stream(egress_id, add_output_urls: [], remove_output_urls: [] ) self.rpc( :UpdateStream, Proto::UpdateStreamRequest.new( egress_id: egress_id, add_output_urls: add_output_urls, remove_output_urls: remove_output_urls, ), headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end # list all egress or only egress for a room def list_egress( room_name: nil, egress_id: nil, active: false ) self.rpc( :ListEgress, Proto::ListEgressRequest.new( room_name: room_name, active: active, egress_id: egress_id, ), headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end def stop_egress(egress_id) self.rpc( :StopEgress, Proto::StopEgressRequest.new(egress_id: egress_id), headers:auth_header(video_grant: VideoGrant.new(roomRecord: true)), ) end private # helper that sets output to file or stream def set_output(request, output) raise "output cannot be nil" if output.nil? # note: because we are setting the outputs fields here, instead of in the initialilzer # we'll need to use the ugly Google::Protobuf::RepeatedField wrappers instead of # a regular array if output.is_a? Array output.each do |out| if out.is_a? LiveKit::Proto::EncodedFileOutput raise "cannot add multiple file outputs" if request.file_outputs.any? request.file_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::EncodedFileOutput, [out]) elsif out.is_a? LiveKit::Proto::SegmentedFileOutput raise "cannot add multiple segmented file outputs" if request.segment_outputs.any? request.segment_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::SegmentedFileOutput, [out]) elsif out.is_a? LiveKit::Proto::StreamOutput raise "cannot add multiple stream outputs" if request.stream_outputs.any? request.stream_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::StreamOutput, [out]) elsif out.is_a? LiveKit::Proto::ImageOutput raise "cannot add multiple image outputs" if request.image_outputs.any? request.image_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::ImageOutput, [out]) end end elsif output.is_a? LiveKit::Proto::EncodedFileOutput request.file = output request.file_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::EncodedFileOutput, [output]) elsif output.is_a? LiveKit::Proto::SegmentedFileOutput request.segments = output request.segment_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::SegmentedFileOutput, [output]) elsif output.is_a? LiveKit::Proto::StreamOutput request.stream = output request.stream_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::StreamOutput, [output]) elsif output.is_a? LiveKit::Proto::ImageOutput request.image_outputs = Google::Protobuf::RepeatedField.new(:message, Proto::ImageOutput, [output]) end end end end