use std::sync::atomic::Ordering;
use crate::constants::QCODES;
use crate::dvrip::DVRIPCam;
use crate::error::Result;
use async_trait::async_trait;
use serde_json::json;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AudioCodec {
PCMA,
PCMU,
}
#[async_trait]
pub trait Backchannel: Send + Sync {
async fn start_talk(&self, codec: AudioCodec) -> Result<()>;
async fn send_audio(&self, data: Vec<u8>) -> Result<()>;
async fn stop_talk(&self) -> Result<()>;
}
#[async_trait]
impl Backchannel for DVRIPCam {
async fn start_talk(&self, codec: AudioCodec) -> Result<()> {
let cmd = "OPTalk";
let code = QCODES.get(cmd).copied().unwrap_or(1434);
let data = json!({
"Action": "Claim",
"AudioFormat": {
"EncodeType": match codec {
AudioCodec::PCMA => "G711_ALAW",
AudioCodec::PCMU => "G711_ULAW",
},
}
});
self.set_command(cmd, data, Some(code as u32)).await?;
let session = self.session.load(Ordering::Acquire);
let start = json!({
"Name" : cmd,
"SessionID": format!("0x{:08X}", session),
"OPTalk" : {
"Action": "Start",
"AudioFormat": {
"EncodeType": match codec {
AudioCodec::PCMA => "G711_ALAW",
AudioCodec::PCMU => "G711_ULAW",
},
}
}
});
let start_code = QCODES.get("OPTalkStart").copied().unwrap_or(1430);
self.send_command(start_code, start, false).await?;
*self.codec.lock().await = Some(codec);
Ok(())
}
async fn send_audio(&self, data: Vec<u8>) -> Result<()> {
let Some(codec) = *self.codec.lock().await else {
return Err(crate::DVRIPError::NotInitialized());
};
let mut buffer = self.backchannel_buffer.lock().await;
buffer.extend_from_slice(&data);
let cmd = "OPTalkData";
let code = QCODES.get(cmd).copied().unwrap_or(1432);
let packet_size = 320;
let codec_id = match codec {
AudioCodec::PCMA => 14,
AudioCodec::PCMU => 10,
};
while buffer.len() >= packet_size {
let chunk: Vec<u8> = buffer.drain(0..packet_size).collect();
let mut buf = Vec::with_capacity(8 + packet_size);
buf.extend_from_slice(&0x1FAu32.to_be_bytes());
buf.push(codec_id);
buf.push(2);
buf.extend_from_slice(&(packet_size as u16).to_le_bytes());
buf.extend_from_slice(&chunk);
self.send_raw_packet(code, buf, false, false).await?;
}
Ok(())
}
async fn stop_talk(&self) -> Result<()> {
let cmd = "OPTalk";
let code = QCODES.get(cmd).copied().unwrap_or(1434);
let data = json!({
"Name": cmd,
"SessionID": format!("0x{:08X}", self.session_id()),
"OPTalk": {
"Action": "Stop"
}
});
self.set_command(cmd, data["OPTalk"].clone(), Some(code as u32))
.await?;
*self.codec.lock().await = None;
Ok(())
}
}