diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 8f28016fc..609798a83 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2323,6 +2323,16 @@ groupCallVideoQualityMedium = GroupCallVideoQuality; groupCallVideoQualityFull = GroupCallVideoQuality; +//@description Describes an available stream in a group call +//@channel_id Identifier of an audio/video channel +//@scale Scale of segment durations in the stream. The duration is 1000/(2**scale) milliseconds +//@time_offset Point in time when the stream currently ends; Unix timestamp in milliseconds +groupCallStream channel_id:int32 scale:int32 time_offset:int53 = GroupCallStream; + +//@description Represents a list of group call streams @streams A list of group call streams +groupCallStreams streams:vector = GroupCallStreams; + + //@description Describes a recently speaking participant in a group call @participant_id Group call participant identifier @is_speaking True, is the user has spoken recently groupCallRecentSpeaker participant_id:MessageSender is_speaking:Bool = GroupCallRecentSpeaker; @@ -5361,6 +5371,9 @@ leaveGroupCall group_call_id:int32 = Ok; //@description Ends a group call. Requires groupCall.can_be_managed @group_call_id Group call identifier endGroupCall group_call_id:int32 = Ok; +//@description Returns information about available group call streams @group_call_id Group call identifier +getGroupCallStreams group_call_id:int32 = GroupCallStreams; + //@description Returns a file with a segment of a group call stream in a modified OGG format for audio or MPEG-4 format for video //@group_call_id Group call identifier //@time_offset Point in time when the stream segment begins; Unix timestamp in milliseconds diff --git a/td/telegram/GroupCallManager.cpp b/td/telegram/GroupCallManager.cpp index b8ba2c345..73aaefb1b 100644 --- a/td/telegram/GroupCallManager.cpp +++ b/td/telegram/GroupCallManager.cpp @@ -33,6 +33,38 @@ namespace td { +class GetGroupCallStreamChannelsQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetGroupCallStreamChannelsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(InputGroupCallId input_group_call_id, DcId stream_dc_id) { + send_query(G()->net_query_creator().create( + telegram_api::phone_getGroupCallStreamChannels(input_group_call_id.get_input_group_call()), {}, stream_dc_id)); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + auto ptr = result_ptr.move_as_ok(); + auto streams = transform(ptr->channels_, [](const tl_object_ptr &channel) { + return td_api::make_object(channel->channel_, channel->scale_, + channel->last_timestamp_ms_); + }); + promise_.set_value(td_api::make_object(std::move(streams))); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + class GetGroupCallStreamQuery final : public Td::ResultHandler { Promise promise_; @@ -2302,6 +2334,47 @@ int32 GroupCallManager::cancel_join_group_call_presentation_request(InputGroupCa return audio_source; } +void GroupCallManager::get_group_call_streams(GroupCallId group_call_id, + Promise> &&promise) { + TRY_STATUS_PROMISE(promise, G()->close_status()); + TRY_RESULT_PROMISE(promise, input_group_call_id, get_input_group_call_id(group_call_id)); + + auto *group_call = get_group_call(input_group_call_id); + if (group_call == nullptr || !group_call->is_inited) { + reload_group_call(input_group_call_id, + PromiseCreator::lambda([actor_id = actor_id(this), group_call_id, promise = std::move(promise)]( + Result> &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &GroupCallManager::get_group_call_streams, group_call_id, + std::move(promise)); + } + })); + return; + } + if (!group_call->is_active || !group_call->stream_dc_id.is_exact()) { + return promise.set_error(Status::Error(400, "Group call can't be streamed")); + } + if (!group_call->is_joined) { + if (is_group_call_being_joined(input_group_call_id) || group_call->need_rejoin) { + group_call->after_join.push_back(PromiseCreator::lambda( + [actor_id = actor_id(this), group_call_id, promise = std::move(promise)](Result &&result) mutable { + if (result.is_error()) { + promise.set_error(result.move_as_error()); + } else { + send_closure(actor_id, &GroupCallManager::get_group_call_streams, group_call_id, std::move(promise)); + } + })); + return; + } + return promise.set_error(Status::Error(400, "GROUPCALL_JOIN_MISSING")); + } + + td_->create_handler(std::move(promise)) + ->send(input_group_call_id, group_call->stream_dc_id); +} + void GroupCallManager::get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, int32 channel_id, td_api::object_ptr quality, diff --git a/td/telegram/GroupCallManager.h b/td/telegram/GroupCallManager.h index 73523bbe8..72b88fd88 100644 --- a/td/telegram/GroupCallManager.h +++ b/td/telegram/GroupCallManager.h @@ -59,6 +59,9 @@ class GroupCallManager final : public Actor { void reload_group_call(InputGroupCallId input_group_call_id, Promise> &&promise); + void get_group_call_streams(GroupCallId group_call_id, + Promise> &&promise); + void get_group_call_stream_segment(GroupCallId group_call_id, int64 time_offset, int32 scale, int32 channel_id, td_api::object_ptr quality, Promise &&promise); diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 5dfbaa5e3..ca6d1015f 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5938,6 +5938,12 @@ void Td::on_request(uint64 id, const td_api::endGroupCall &request) { group_call_manager_->discard_group_call(GroupCallId(request.group_call_id_), std::move(promise)); } +void Td::on_request(uint64 id, const td_api::getGroupCallStreams &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + group_call_manager_->get_group_call_streams(GroupCallId(request.group_call_id_), std::move(promise)); +} + void Td::on_request(uint64 id, td_api::getGroupCallStreamSegment &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 9d44115a5..3aae6c132 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -790,6 +790,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::endGroupCall &request); + void on_request(uint64 id, const td_api::getGroupCallStreams &request); + void on_request(uint64 id, td_api::getGroupCallStreamSegment &request); void on_request(uint64 id, const td_api::upgradeBasicGroupChatToSupergroupChat &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 026e1498a..bce002ae9 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -3025,6 +3025,10 @@ class CliClient final : public Actor { send_request(td_api::make_object(chat_id, title, start_date, is_rtmp_stream)); } else if (op == "ggc") { send_request(td_api::make_object(as_group_call_id(args))); + } else if (op == "ggcs") { + string group_call_id; + get_args(args, group_call_id); + send_request(td_api::make_object(as_group_call_id(group_call_id))); } else if (op == "ggcss") { string group_call_id; int32 channel_id;