mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
has_video_initially
This commit is contained in:
@@ -1235,9 +1235,11 @@ 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_initially Whether the call has video.
|
||||||
|
* This allows the recipient's client to adjust UX.
|
||||||
* @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_initially);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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_initially: 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_initially))
|
||||||
.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())
|
||||||
|
|||||||
@@ -2141,10 +2141,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_initially: 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_initially)
|
||||||
.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,8 +15,8 @@ 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_initially: bool,
|
||||||
|
|
||||||
/// Call state.
|
/// Call state.
|
||||||
///
|
///
|
||||||
@@ -30,12 +30,12 @@ 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_initially = 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 {
|
||||||
sdp_offer,
|
sdp_offer,
|
||||||
has_video,
|
has_video_initially,
|
||||||
state,
|
state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +10,7 @@ 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, 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)
|
||||||
@@ -71,7 +71,7 @@ a=extmap:1 urn:ietf:params:rtp-hdrext:sdes:mid\r
|
|||||||
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(place_call_info, 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 == place_call_info
|
||||||
@@ -92,7 +92,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", 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
|
||||||
|
|||||||
29
src/calls.rs
29
src/calls.rs
@@ -123,6 +123,11 @@ 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::CallHasVideoInitially).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.
|
||||||
@@ -182,6 +187,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!(
|
||||||
@@ -196,6 +202,7 @@ 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::CallHasVideoInitially, has_video_initially as i32);
|
||||||
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;
|
||||||
@@ -341,13 +348,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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(chat_id_blocked) =
|
if let Some(chat_id_blocked) =
|
||||||
ChatIdBlocked::lookup_by_contact(self, from_id).await?
|
ChatIdBlocked::lookup_by_contact(self, from_id).await?
|
||||||
{
|
{
|
||||||
@@ -357,7 +357,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(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Blocked::Yes | Blocked::Request => {
|
Blocked::Yes | Blocked::Request => {
|
||||||
@@ -496,19 +496,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 {
|
||||||
|
|||||||
@@ -52,7 +52,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);
|
||||||
@@ -525,13 +525,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 +535,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?;
|
||||||
|
|||||||
@@ -148,6 +148,9 @@ pub enum Param {
|
|||||||
/// For Messages
|
/// For Messages
|
||||||
WebrtcAccepted = b'7',
|
WebrtcAccepted = b'7',
|
||||||
|
|
||||||
|
/// For Messages
|
||||||
|
CallHasVideoInitially = b'8',
|
||||||
|
|
||||||
/// 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