mirror of
https://github.com/chatmail/core.git
synced 2026-05-05 22:36:30 +03:00
feat: allow clients to specify whether a call has video initially or not (#7740)
This commit is contained in:
committed by
GitHub
parent
d6bce56d18
commit
63bf4c4f33
@@ -1242,9 +1242,12 @@ uint32_t dc_init_webxdc_integration (dc_context_t* context, uint32_t c
|
|||||||
* This needs to be a one-to-one chat.
|
* This needs to be a one-to-one chat.
|
||||||
* @param place_call_info any data that other devices receive
|
* @param place_call_info any data that other devices receive
|
||||||
* in #DC_EVENT_INCOMING_CALL.
|
* in #DC_EVENT_INCOMING_CALL.
|
||||||
|
* @param has_video Whether the call has video initially.
|
||||||
|
* This allows the recipient's client to adjust incoming call UX.
|
||||||
|
* A call can be upgraded to include video later.
|
||||||
* @return ID of the system message announcing the call.
|
* @return ID of the system message announcing the call.
|
||||||
*/
|
*/
|
||||||
uint32_t dc_place_outgoing_call (dc_context_t* context, uint32_t chat_id, const char* place_call_info);
|
uint32_t dc_place_outgoing_call (dc_context_t* context, uint32_t chat_id, const char* place_call_info, int has_video);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1181,6 +1181,7 @@ pub unsafe extern "C" fn dc_place_outgoing_call(
|
|||||||
context: *mut dc_context_t,
|
context: *mut dc_context_t,
|
||||||
chat_id: u32,
|
chat_id: u32,
|
||||||
place_call_info: *const libc::c_char,
|
place_call_info: *const libc::c_char,
|
||||||
|
has_video: bool,
|
||||||
) -> u32 {
|
) -> u32 {
|
||||||
if context.is_null() || chat_id == 0 {
|
if context.is_null() || chat_id == 0 {
|
||||||
eprintln!("ignoring careless call to dc_place_outgoing_call()");
|
eprintln!("ignoring careless call to dc_place_outgoing_call()");
|
||||||
@@ -1190,7 +1191,7 @@ pub unsafe extern "C" fn dc_place_outgoing_call(
|
|||||||
let chat_id = ChatId::new(chat_id);
|
let chat_id = ChatId::new(chat_id);
|
||||||
let place_call_info = to_string_lossy(place_call_info);
|
let place_call_info = to_string_lossy(place_call_info);
|
||||||
|
|
||||||
block_on(ctx.place_outgoing_call(chat_id, place_call_info))
|
block_on(ctx.place_outgoing_call(chat_id, place_call_info, has_video))
|
||||||
.context("Failed to place call")
|
.context("Failed to place call")
|
||||||
.log_err(ctx)
|
.log_err(ctx)
|
||||||
.map(|msg_id| msg_id.to_u32())
|
.map(|msg_id| msg_id.to_u32())
|
||||||
|
|||||||
@@ -2167,10 +2167,11 @@ impl CommandApi {
|
|||||||
account_id: u32,
|
account_id: u32,
|
||||||
chat_id: u32,
|
chat_id: u32,
|
||||||
place_call_info: String,
|
place_call_info: String,
|
||||||
|
has_video: bool,
|
||||||
) -> Result<u32> {
|
) -> Result<u32> {
|
||||||
let ctx = self.get_context(account_id).await?;
|
let ctx = self.get_context(account_id).await?;
|
||||||
let msg_id = ctx
|
let msg_id = ctx
|
||||||
.place_outgoing_call(ChatId::new(chat_id), place_call_info)
|
.place_outgoing_call(ChatId::new(chat_id), place_call_info, has_video)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(msg_id.to_u32())
|
Ok(msg_id.to_u32())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
|
|
||||||
use deltachat::calls::{call_state, sdp_has_video, CallState};
|
use deltachat::calls::{call_state, CallState};
|
||||||
use deltachat::context::Context;
|
use deltachat::context::Context;
|
||||||
use deltachat::message::MsgId;
|
use deltachat::message::MsgId;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -15,7 +15,7 @@ pub struct JsonrpcCallInfo {
|
|||||||
/// even if incoming call event was missed.
|
/// even if incoming call event was missed.
|
||||||
pub sdp_offer: String,
|
pub sdp_offer: String,
|
||||||
|
|
||||||
/// True if SDP offer has a video.
|
/// True if the call is started as a video call.
|
||||||
pub has_video: bool,
|
pub has_video: bool,
|
||||||
|
|
||||||
/// Call state.
|
/// Call state.
|
||||||
@@ -30,7 +30,7 @@ impl JsonrpcCallInfo {
|
|||||||
format!("Attempting to get call state of non-call message {msg_id}")
|
format!("Attempting to get call state of non-call message {msg_id}")
|
||||||
})?;
|
})?;
|
||||||
let sdp_offer = call_info.place_call_info.clone();
|
let sdp_offer = call_info.place_call_info.clone();
|
||||||
let has_video = sdp_has_video(&sdp_offer).unwrap_or_default();
|
let has_video = call_info.has_video_initially();
|
||||||
let state = JsonrpcCallState::from_msg_id(context, msg_id).await?;
|
let state = JsonrpcCallState::from_msg_id(context, msg_id).await?;
|
||||||
|
|
||||||
Ok(JsonrpcCallInfo {
|
Ok(JsonrpcCallInfo {
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ class Chat:
|
|||||||
f.flush()
|
f.flush()
|
||||||
self._rpc.send_msg(self.account.id, self.id, {"viewtype": ViewType.VCARD, "file": f.name})
|
self._rpc.send_msg(self.account.id, self.id, {"viewtype": ViewType.VCARD, "file": f.name})
|
||||||
|
|
||||||
def place_outgoing_call(self, place_call_info: str) -> Message:
|
def place_outgoing_call(self, place_call_info: str, has_video_initially: bool) -> Message:
|
||||||
"""Starts an outgoing call."""
|
"""Starts an outgoing call."""
|
||||||
msg_id = self._rpc.place_outgoing_call(self.account.id, self.id, place_call_info)
|
msg_id = self._rpc.place_outgoing_call(self.account.id, self.id, place_call_info, has_video_initially)
|
||||||
return Message(self.account, msg_id)
|
return Message(self.account, msg_id)
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ def test_calls(acfactory) -> None:
|
|||||||
alice_contact_bob = alice.create_contact(bob, "Bob")
|
alice_contact_bob = alice.create_contact(bob, "Bob")
|
||||||
alice_chat_bob = alice_contact_bob.create_chat()
|
alice_chat_bob = alice_contact_bob.create_chat()
|
||||||
bob.create_chat(alice) # Accept the chat so incoming call causes a notification.
|
bob.create_chat(alice) # Accept the chat so incoming call causes a notification.
|
||||||
outgoing_call_message = alice_chat_bob.place_outgoing_call(place_call_info)
|
outgoing_call_message = alice_chat_bob.place_outgoing_call(place_call_info, has_video_initially=True)
|
||||||
assert outgoing_call_message.get_call_info().state.kind == "Alerting"
|
assert outgoing_call_message.get_call_info().state.kind == "Alerting"
|
||||||
|
|
||||||
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
||||||
assert incoming_call_event.place_call_info == place_call_info
|
assert incoming_call_event.place_call_info == place_call_info
|
||||||
assert not incoming_call_event.has_video # Cannot be parsed as SDP, so false by default
|
assert incoming_call_event.has_video
|
||||||
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
||||||
assert incoming_call_message.get_call_info().state.kind == "Alerting"
|
assert incoming_call_message.get_call_info().state.kind == "Alerting"
|
||||||
assert not incoming_call_message.get_call_info().has_video
|
assert incoming_call_message.get_call_info().has_video
|
||||||
|
|
||||||
incoming_call_message.accept_incoming_call(accept_call_info)
|
incoming_call_message.accept_incoming_call(accept_call_info)
|
||||||
assert incoming_call_message.get_call_info().sdp_offer == place_call_info
|
assert incoming_call_message.get_call_info().sdp_offer == place_call_info
|
||||||
@@ -41,46 +41,38 @@ def test_video_call(acfactory) -> None:
|
|||||||
#
|
#
|
||||||
# `s=` cannot be empty according to RFC 3264,
|
# `s=` cannot be empty according to RFC 3264,
|
||||||
# so it is more clear as `s=-`.
|
# so it is more clear as `s=-`.
|
||||||
place_call_info = """v=0\r
|
|
||||||
o=alice 2890844526 2890844526 IN IP6 2001:db8::3\r
|
|
||||||
s=-\r
|
|
||||||
c=IN IP6 2001:db8::3\r
|
|
||||||
t=0 0\r
|
|
||||||
a=group:BUNDLE foo bar\r
|
|
||||||
\r
|
|
||||||
m=audio 10000 RTP/AVP 0 8 97\r
|
|
||||||
b=AS:200\r
|
|
||||||
a=mid:foo\r
|
|
||||||
a=rtcp-mux\r
|
|
||||||
a=rtpmap:0 PCMU/8000\r
|
|
||||||
a=rtpmap:8 PCMA/8000\r
|
|
||||||
a=rtpmap:97 iLBC/8000\r
|
|
||||||
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r
|
|
||||||
\r
|
|
||||||
m=video 10002 RTP/AVP 31 32\r
|
|
||||||
b=AS:1000\r
|
|
||||||
a=mid:bar\r
|
|
||||||
a=rtcp-mux\r
|
|
||||||
a=rtpmap:31 H261/90000\r
|
|
||||||
a=rtpmap:32 MPV/90000\r
|
|
||||||
a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r
|
|
||||||
"""
|
|
||||||
|
|
||||||
alice, bob = acfactory.get_online_accounts(2)
|
alice, bob = acfactory.get_online_accounts(2)
|
||||||
|
|
||||||
bob.create_chat(alice) # Accept the chat so incoming call causes a notification.
|
bob.create_chat(alice) # Accept the chat so incoming call causes a notification.
|
||||||
alice_contact_bob = alice.create_contact(bob, "Bob")
|
alice_contact_bob = alice.create_contact(bob, "Bob")
|
||||||
alice_chat_bob = alice_contact_bob.create_chat()
|
alice_chat_bob = alice_contact_bob.create_chat()
|
||||||
alice_chat_bob.place_outgoing_call(place_call_info)
|
alice_chat_bob.place_outgoing_call("offer", has_video_initially=True)
|
||||||
|
|
||||||
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
||||||
assert incoming_call_event.place_call_info == place_call_info
|
assert incoming_call_event.place_call_info == "offer"
|
||||||
assert incoming_call_event.has_video
|
assert incoming_call_event.has_video
|
||||||
|
|
||||||
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
||||||
assert incoming_call_message.get_call_info().has_video
|
assert incoming_call_message.get_call_info().has_video
|
||||||
|
|
||||||
|
|
||||||
|
def test_audio_call(acfactory) -> None:
|
||||||
|
alice, bob = acfactory.get_online_accounts(2)
|
||||||
|
|
||||||
|
bob.create_chat(alice) # Accept the chat so incoming call causes a notification.
|
||||||
|
alice_contact_bob = alice.create_contact(bob, "Bob")
|
||||||
|
alice_chat_bob = alice_contact_bob.create_chat()
|
||||||
|
alice_chat_bob.place_outgoing_call("offer", has_video_initially=False)
|
||||||
|
|
||||||
|
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
||||||
|
assert incoming_call_event.place_call_info == "offer"
|
||||||
|
assert not incoming_call_event.has_video
|
||||||
|
|
||||||
|
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
||||||
|
assert not incoming_call_message.get_call_info().has_video
|
||||||
|
|
||||||
|
|
||||||
def test_ice_servers(acfactory) -> None:
|
def test_ice_servers(acfactory) -> None:
|
||||||
alice = acfactory.get_online_account()
|
alice = acfactory.get_online_account()
|
||||||
|
|
||||||
@@ -92,7 +84,7 @@ def test_no_contact_request_call(acfactory) -> None:
|
|||||||
alice, bob = acfactory.get_online_accounts(2)
|
alice, bob = acfactory.get_online_accounts(2)
|
||||||
|
|
||||||
alice_chat_bob = alice.create_chat(bob)
|
alice_chat_bob = alice.create_chat(bob)
|
||||||
alice_chat_bob.place_outgoing_call("offer")
|
alice_chat_bob.place_outgoing_call("offer", has_video_initially=True)
|
||||||
alice_chat_bob.send_text("Hello!")
|
alice_chat_bob.send_text("Hello!")
|
||||||
|
|
||||||
# Notification for "Hello!" message should arrive
|
# Notification for "Hello!" message should arrive
|
||||||
@@ -119,7 +111,7 @@ def test_who_can_call_me_nobody(acfactory) -> None:
|
|||||||
bob.create_chat(alice)
|
bob.create_chat(alice)
|
||||||
|
|
||||||
alice_chat_bob = alice.create_chat(bob)
|
alice_chat_bob = alice.create_chat(bob)
|
||||||
alice_chat_bob.place_outgoing_call("offer")
|
alice_chat_bob.place_outgoing_call("offer", has_video_initially=True)
|
||||||
alice_chat_bob.send_text("Hello!")
|
alice_chat_bob.send_text("Hello!")
|
||||||
|
|
||||||
# Notification for "Hello!" message should arrive
|
# Notification for "Hello!" message should arrive
|
||||||
@@ -144,7 +136,7 @@ def test_who_can_call_me_everybody(acfactory) -> None:
|
|||||||
bob.set_config("who_can_call_me", "0")
|
bob.set_config("who_can_call_me", "0")
|
||||||
|
|
||||||
alice_chat_bob = alice.create_chat(bob)
|
alice_chat_bob = alice.create_chat(bob)
|
||||||
alice_chat_bob.place_outgoing_call("offer")
|
alice_chat_bob.place_outgoing_call("offer", has_video_initially=True)
|
||||||
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
incoming_call_event = bob.wait_for_event(EventType.INCOMING_CALL)
|
||||||
|
|
||||||
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
incoming_call_message = Message(bob, incoming_call_event.msg_id)
|
||||||
|
|||||||
35
src/calls.rs
35
src/calls.rs
@@ -19,9 +19,7 @@ use crate::tools::{normalize_text, time};
|
|||||||
use anyhow::{Context as _, Result, ensure};
|
use anyhow::{Context as _, Result, ensure};
|
||||||
use deltachat_derive::{FromSql, ToSql};
|
use deltachat_derive::{FromSql, ToSql};
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use sdp::SessionDescription;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::io::Cursor;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
@@ -126,6 +124,14 @@ impl CallInfo {
|
|||||||
self.msg.param.exists(CALL_ACCEPTED_TIMESTAMP)
|
self.msg.param.exists(CALL_ACCEPTED_TIMESTAMP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the call is started as a video call.
|
||||||
|
pub fn has_video_initially(&self) -> bool {
|
||||||
|
self.msg
|
||||||
|
.param
|
||||||
|
.get_bool(Param::WebrtcHasVideoInitially)
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns true if the call is missed
|
/// Returns true if the call is missed
|
||||||
/// because the caller canceled it
|
/// because the caller canceled it
|
||||||
/// explicitly before ringing stopped.
|
/// explicitly before ringing stopped.
|
||||||
@@ -185,6 +191,7 @@ impl Context {
|
|||||||
&self,
|
&self,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
place_call_info: String,
|
place_call_info: String,
|
||||||
|
has_video_initially: bool,
|
||||||
) -> Result<MsgId> {
|
) -> Result<MsgId> {
|
||||||
let chat = Chat::load_from_db(self, chat_id).await?;
|
let chat = Chat::load_from_db(self, chat_id).await?;
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -199,6 +206,8 @@ impl Context {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
call.param.set(Param::WebrtcRoom, &place_call_info);
|
call.param.set(Param::WebrtcRoom, &place_call_info);
|
||||||
|
call.param
|
||||||
|
.set_int(Param::WebrtcHasVideoInitially, has_video_initially.into());
|
||||||
call.id = send_msg(self, chat_id, &mut call).await?;
|
call.id = send_msg(self, chat_id, &mut call).await?;
|
||||||
|
|
||||||
let wait = RINGING_SECONDS;
|
let wait = RINGING_SECONDS;
|
||||||
@@ -344,13 +353,6 @@ impl Context {
|
|||||||
} else {
|
} else {
|
||||||
call.update_text(self, "Incoming call").await?;
|
call.update_text(self, "Incoming call").await?;
|
||||||
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
|
self.emit_msgs_changed(call.msg.chat_id, call_id); // ringing calls are not additionally notified
|
||||||
let has_video = match sdp_has_video(&call.place_call_info) {
|
|
||||||
Ok(has_video) => has_video,
|
|
||||||
Err(err) => {
|
|
||||||
warn!(self, "Failed to determine if SDP offer has video: {err:#}.");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let can_call_me = match who_can_call_me(self).await? {
|
let can_call_me = match who_can_call_me(self).await? {
|
||||||
WhoCanCallMe::Contacts => ChatIdBlocked::lookup_by_contact(self, from_id)
|
WhoCanCallMe::Contacts => ChatIdBlocked::lookup_by_contact(self, from_id)
|
||||||
.await?
|
.await?
|
||||||
@@ -377,7 +379,7 @@ impl Context {
|
|||||||
msg_id: call.msg.id,
|
msg_id: call.msg.id,
|
||||||
chat_id: call.msg.chat_id,
|
chat_id: call.msg.chat_id,
|
||||||
place_call_info: call.place_call_info.to_string(),
|
place_call_info: call.place_call_info.to_string(),
|
||||||
has_video,
|
has_video: call.has_video_initially(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let wait = call.remaining_ring_seconds();
|
let wait = call.remaining_ring_seconds();
|
||||||
@@ -507,19 +509,6 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if SDP offer has a video.
|
|
||||||
pub fn sdp_has_video(sdp: &str) -> Result<bool> {
|
|
||||||
let mut cursor = Cursor::new(sdp);
|
|
||||||
let session_description =
|
|
||||||
SessionDescription::unmarshal(&mut cursor).context("Failed to parse SDP")?;
|
|
||||||
for media_description in &session_description.media_descriptions {
|
|
||||||
if media_description.media_name.media == "video" {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// State of the call for display in the message bubble.
|
/// State of the call for display in the message bubble.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum CallState {
|
pub enum CallState {
|
||||||
|
|||||||
@@ -25,13 +25,6 @@ async fn assert_text(t: &TestContext, call_id: MsgId, text: &str) -> Result<()>
|
|||||||
const PLACE_INFO: &str = "v=0\r\no=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\ns=-\r\nc=IN IP4 host.anywhere.com\r\nt=0 0\r\nm=audio 62986 RTP/AVP 0 4 18\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=rtpmap:18 G729/8000\r\na=inactive\r\n";
|
const PLACE_INFO: &str = "v=0\r\no=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\ns=-\r\nc=IN IP4 host.anywhere.com\r\nt=0 0\r\nm=audio 62986 RTP/AVP 0 4 18\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=rtpmap:18 G729/8000\r\na=inactive\r\n";
|
||||||
const ACCEPT_INFO: &str = "v=0\r\no=bob 2890844730 2890844731 IN IP4 host.example.com\r\ns=\r\nc=IN IP4 host.example.com\r\nt=0 0\r\nm=audio 54344 RTP/AVP 0 4\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=inactive\r\n";
|
const ACCEPT_INFO: &str = "v=0\r\no=bob 2890844730 2890844731 IN IP4 host.example.com\r\ns=\r\nc=IN IP4 host.example.com\r\nt=0 0\r\nm=audio 54344 RTP/AVP 0 4\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=inactive\r\n";
|
||||||
|
|
||||||
/// Example from <https://datatracker.ietf.org/doc/rfc9143/>
|
|
||||||
/// with `s= ` replaced with `s=-`.
|
|
||||||
///
|
|
||||||
/// `s=` cannot be empty according to RFC 3264,
|
|
||||||
/// so it is more clear as `s=-`.
|
|
||||||
const PLACE_INFO_VIDEO: &str = "v=0\r\no=alice 2890844526 2890844526 IN IP6 2001:db8::3\r\ns=-\r\nc=IN IP6 2001:db8::3\r\nt=0 0\r\na=group:BUNDLE foo bar\r\n\r\nm=audio 10000 RTP/AVP 0 8 97\r\nb=AS:200\r\na=mid:foo\r\na=rtcp-mux\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:97 iLBC/8000\r\na=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r\n\r\nm=video 10002 RTP/AVP 31 32\r\nb=AS:1000\r\na=mid:bar\r\na=rtcp-mux\r\na=rtpmap:31 H261/90000\r\na=rtpmap:32 MPV/90000\r\na=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r\n";
|
|
||||||
|
|
||||||
async fn setup_call() -> Result<CallSetup> {
|
async fn setup_call() -> Result<CallSetup> {
|
||||||
let mut tcm = TestContextManager::new();
|
let mut tcm = TestContextManager::new();
|
||||||
let alice = tcm.alice().await;
|
let alice = tcm.alice().await;
|
||||||
@@ -52,7 +45,7 @@ async fn setup_call() -> Result<CallSetup> {
|
|||||||
bob2.create_chat(&alice).await;
|
bob2.create_chat(&alice).await;
|
||||||
|
|
||||||
let test_msg_id = alice
|
let test_msg_id = alice
|
||||||
.place_outgoing_call(alice_chat.id, PLACE_INFO.to_string())
|
.place_outgoing_call(alice_chat.id, PLACE_INFO.to_string(), true)
|
||||||
.await?;
|
.await?;
|
||||||
let sent1 = alice.pop_sent_msg().await;
|
let sent1 = alice.pop_sent_msg().await;
|
||||||
assert_eq!(sent1.sender_msg_id, test_msg_id);
|
assert_eq!(sent1.sender_msg_id, test_msg_id);
|
||||||
@@ -68,6 +61,7 @@ async fn setup_call() -> Result<CallSetup> {
|
|||||||
assert!(!info.is_incoming());
|
assert!(!info.is_incoming());
|
||||||
assert!(!info.is_accepted());
|
assert!(!info.is_accepted());
|
||||||
assert_eq!(info.place_call_info, PLACE_INFO);
|
assert_eq!(info.place_call_info, PLACE_INFO);
|
||||||
|
assert_eq!(info.has_video_initially(), true);
|
||||||
assert_text(t, m.id, "Outgoing call").await?;
|
assert_text(t, m.id, "Outgoing call").await?;
|
||||||
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
|
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
|
||||||
}
|
}
|
||||||
@@ -89,6 +83,7 @@ async fn setup_call() -> Result<CallSetup> {
|
|||||||
assert!(info.is_incoming());
|
assert!(info.is_incoming());
|
||||||
assert!(!info.is_accepted());
|
assert!(!info.is_accepted());
|
||||||
assert_eq!(info.place_call_info, PLACE_INFO);
|
assert_eq!(info.place_call_info, PLACE_INFO);
|
||||||
|
assert_eq!(info.has_video_initially(), true);
|
||||||
assert_text(t, m.id, "Incoming call").await?;
|
assert_text(t, m.id, "Incoming call").await?;
|
||||||
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
|
assert_eq!(call_state(t, m.id).await?, CallState::Alerting);
|
||||||
}
|
}
|
||||||
@@ -525,13 +520,6 @@ async fn test_update_call_text() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
||||||
async fn test_sdp_has_video() {
|
|
||||||
assert!(sdp_has_video("foobar").is_err());
|
|
||||||
assert_eq!(sdp_has_video(PLACE_INFO).unwrap(), false);
|
|
||||||
assert_eq!(sdp_has_video(PLACE_INFO_VIDEO).unwrap(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tests that calls are forwarded as text messages.
|
/// Tests that calls are forwarded as text messages.
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_forward_call() -> Result<()> {
|
async fn test_forward_call() -> Result<()> {
|
||||||
@@ -542,7 +530,7 @@ async fn test_forward_call() -> Result<()> {
|
|||||||
|
|
||||||
let alice_bob_chat = alice.create_chat(bob).await;
|
let alice_bob_chat = alice.create_chat(bob).await;
|
||||||
let alice_msg_id = alice
|
let alice_msg_id = alice
|
||||||
.place_outgoing_call(alice_bob_chat.id, PLACE_INFO.to_string())
|
.place_outgoing_call(alice_bob_chat.id, PLACE_INFO.to_string(), true)
|
||||||
.await
|
.await
|
||||||
.context("Failed to place a call")?;
|
.context("Failed to place a call")?;
|
||||||
let alice_call = Message::load_from_db(alice, alice_msg_id).await?;
|
let alice_call = Message::load_from_db(alice, alice_msg_id).await?;
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ pub enum HeaderDef {
|
|||||||
ChatDispositionNotificationTo,
|
ChatDispositionNotificationTo,
|
||||||
ChatWebrtcRoom,
|
ChatWebrtcRoom,
|
||||||
ChatWebrtcAccepted,
|
ChatWebrtcAccepted,
|
||||||
|
ChatWebrtcHasVideoInitially,
|
||||||
|
|
||||||
/// This message deletes the messages listed in the value by rfc724_mid.
|
/// This message deletes the messages listed in the value by rfc724_mid.
|
||||||
ChatDelete,
|
ChatDelete,
|
||||||
|
|||||||
@@ -1840,6 +1840,12 @@ impl MimeFactory {
|
|||||||
mail_builder::headers::raw::Raw::new(b_encode(answer)).into(),
|
mail_builder::headers::raw::Raw::new(b_encode(answer)).into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if let Some(has_video) = msg.param.get(Param::WebrtcHasVideoInitially) {
|
||||||
|
headers.push((
|
||||||
|
"Chat-Webrtc-Has-Video-Initially",
|
||||||
|
mail_builder::headers::raw::Raw::new(b_encode(has_video)).into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if msg.viewtype == Viewtype::Voice
|
if msg.viewtype == Viewtype::Voice
|
||||||
|| msg.viewtype == Viewtype::Audio
|
|| msg.viewtype == Viewtype::Audio
|
||||||
|
|||||||
@@ -801,6 +801,9 @@ impl MimeMessage {
|
|||||||
let accepted = self
|
let accepted = self
|
||||||
.get_header(HeaderDef::ChatWebrtcAccepted)
|
.get_header(HeaderDef::ChatWebrtcAccepted)
|
||||||
.map(|s| s.to_string());
|
.map(|s| s.to_string());
|
||||||
|
let has_video = self
|
||||||
|
.get_header(HeaderDef::ChatWebrtcHasVideoInitially)
|
||||||
|
.map(|s| s.to_string());
|
||||||
if let Some(part) = self.parts.first_mut() {
|
if let Some(part) = self.parts.first_mut() {
|
||||||
if let Some(room) = room {
|
if let Some(room) = room {
|
||||||
if content == "call" {
|
if content == "call" {
|
||||||
@@ -810,6 +813,9 @@ impl MimeMessage {
|
|||||||
} else if let Some(accepted) = accepted {
|
} else if let Some(accepted) = accepted {
|
||||||
part.param.set(Param::WebrtcAccepted, accepted);
|
part.param.set(Param::WebrtcAccepted, accepted);
|
||||||
}
|
}
|
||||||
|
if let Some(has_video) = has_video {
|
||||||
|
part.param.set(Param::WebrtcHasVideoInitially, has_video);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ pub enum Param {
|
|||||||
/// For Messages
|
/// For Messages
|
||||||
WebrtcAccepted = b'7',
|
WebrtcAccepted = b'7',
|
||||||
|
|
||||||
|
/// For Messages
|
||||||
|
WebrtcHasVideoInitially = b'z',
|
||||||
|
|
||||||
/// For Messages: space-separated list of messaged IDs of forwarded copies.
|
/// For Messages: space-separated list of messaged IDs of forwarded copies.
|
||||||
///
|
///
|
||||||
/// This is used when a [crate::message::Message] is in the
|
/// This is used when a [crate::message::Message] is in the
|
||||||
|
|||||||
Reference in New Issue
Block a user