Asterisk怎样实现通话录音
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Asterisk怎样实现通话录音相关的知识,希望对你有一定的参考价值。
参考技术A Monitor():用于记录通话过程中录音的功能。语法:Monitor(ext,basename,flags)
ext: 用于指定改录音文件的格式,默认的将是wav文件
basename: 该录音文件的文件名
flags : 。。。。。。。。。。。。
实例一:
复制代码
exten => _x.,1,Answer()
exten => _x.,n,Wait(1)
exten => _x.,n,Monitor(wav,asterisk-help)
exten => _x.,n,Dial(SIP/$EXTEN)
exten => _x.,n,Hangup()
当呼分机后,将在 “/var/spool/asterisk/monitor”目录下生成 "asterisk-help-in.wav"和"asterisk-help-out.wav"两个文件
实例二:指定录音路径
复制代码
exten => _x.,1,Answer()
exten => _x.,n,Wait(1)
exten => _x.,n,Monitor(wav,/root/asterisk-help) ;这里还可以指定到录音到/root目录下
exten => _x.,n,Dial(SIP/$EXTEN)
exten => _x.,n,Hangup()
Android 通话录音功能
通话录音功能因为涉及隐私问题,Android 6.0上就移除官方的通话录音接口,只能通过其他方式去获取调用。
录音时需要设置音频类型,系统中定义以下几种
(MediaRecorder.AudioSource)
CAMCORDER 录音来源于同方向的相机麦克风相同,若相机无内置相机或无法识别,则使用预设的麦克风
DEFAULT 默认音频源
MIC 录音来源为主麦克风
REMOTE_SUBMIX 用于远程呈现的音频流的子混音的音频源,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请
UNPROCESSED 与默认相同
VOICE_CALL 记录上行与下行音频源,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请
VOICE_COMMUNICATION 麦克风音频源针对VoIP等语音通信进行了调整,可以接收到通话的双方语音
VOICE_DOWNLINK、VOICE_UPLINK 上行下行的语音,需要Manifest.permission.CAPTURE_AUDIO_OUTPUT权限,第三方应用无法申请
VOICE_PERFORMANCE 捕获音频的来源意味着要实时处理并播放以进行现场演出
VOICE_RECOGNITION 用于获取语音进行语音识别
但在通话时,类型会变成 VOICE_CALL 。这种状态下录音,需要申请 android.permission.CAPTURE_AUDIO_OUTPUT 权限,UID改成 “android.uid.system” ,只能是系统应用使用。
系统中可以通过修改源码,放开此限制,代码路径 frameworks\\av\\services\\audiopolicy\\service\\AudioPolicyInterfaceImpl.cpp
// 这里判断是否有普通录制权限
if (!recordingAllowed(opPackageName, pid, uid)) {
ALOGE("%s permission denied: recording not allowed for uid %d pid %d",
__func__, uid, pid);
return PERMISSION_DENIED;
}
// 是否是以下三种类型且未申请 CAPTURE_AUDIO_OUTPUT 权限
if ((attr->source == AUDIO_SOURCE_VOICE_UPLINK ||
attr->source == AUDIO_SOURCE_VOICE_DOWNLINK ||
attr->source == AUDIO_SOURCE_VOICE_CALL) &&
!captureAudioOutputAllowed(pid, uid)) {
return PERMISSION_DENIED;
}
if ((attr->source == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed(pid, uid)) {
return BAD_VALUE;
}
sp<AudioPolicyEffects>audioPolicyEffects;
{
status_t status;
AudioPolicyInterface::input_type_t inputType;
Mutex::Autolock _l(mLock);
{
AutoCallerClear acc;
// the audio_in_acoustics_t parameter is ignored by get_input()
status = mAudioPolicyManager->getInputForAttr(attr, input, session, uid,
config,
flags, selectedDeviceId,
&inputType, portId);
}
audioPolicyEffects = mAudioPolicyEffects;
if (status == NO_ERROR) {
// enforce permission (if any) required for each type of input
switch (inputType) {
case AudioPolicyInterface::API_INPUT_LEGACY:
break;
case AudioPolicyInterface::API_INPUT_TELEPHONY_RX:
// FIXME: use the same permission as for remote submix for now.
case AudioPolicyInterface::API_INPUT_MIX_CAPTURE:
if (!captureAudioOutputAllowed(pid, uid)) {
ALOGE("getInputForAttr() permission allowed: capture allowed");
//cczheng annotation for don't check android.Manifest.permission.CAPTURE_AUDIO_OUTPUT
/*ALOGE("getInputForAttr() permission denied: capture not allowed");
status = PERMISSION_DENIED;*/
}
break;
case AudioPolicyInterface::API_INPUT_MIX_EXT_POLICY_REROUTE:
if (!modifyAudioRoutingAllowed()) {
ALOGE("getInputForAttr() permission denied: modify audio routing not allowed");
status = PERMISSION_DENIED;
}
break;
case AudioPolicyInterface::API_INPUT_INVALID:
default:
LOG_ALWAYS_FATAL("getInputForAttr() encountered an invalid input type %d",
(int)inputType);
}
}
权限检查代码 frameworks/av/media/utils/ServiceUtilities.cpp
// 判断是否AUDIO服务、root应用、已申请权限
bool captureAudioOutputAllowed(pid_t pid, uid_t uid) {
if (isAudioServerOrRootUid(uid)) return true;
static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
bool ok = PermissionCache::checkPermission(sCaptureAudioOutput, pid, uid);
if (!ok) ALOGV("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
return ok;
}
// 判断是否系统服务或者root进程,申请普通录音权限
static bool checkRecordingInternal(const String16& opPackageName, pid_t pid,
uid_t uid, bool start) {
// Okay to not track in app ops as audio server or media server is us and if
// device is rooted security model is considered compromised.
// system_server loses its RECORD_AUDIO permission when a secondary
// user is active, but it is a core system service so let it through.
// TODO(b/141210120): UserManager.DISALLOW_RECORD_AUDIO should not affect system user 0
if (isAudioServerOrMediaServerOrSystemServerOrRootUid(uid)) return true;
// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
// may open a record track on behalf of a client. Note that pid may be a tid.
// IMPORTANT: DON'T USE PermissionCache - RUNTIME PERMISSIONS CHANGE.
PermissionController permissionController;
const bool ok = permissionController.checkPermission(sAndroidPermissionRecordAudio, pid, uid);
if (!ok) {
ALOGE("Request requires %s", String8(sAndroidPermissionRecordAudio).c_str());
return false;
}
String16 resolvedOpPackageName = resolveCallingPackage(
permissionController, opPackageName, uid);
if (resolvedOpPackageName.size() == 0) {
return false;
}
AppOpsManager appOps;
const int32_t op = appOps.permissionToOpCode(sAndroidPermissionRecordAudio);
if (start) {
if (appOps.startOpNoThrow(op, uid, resolvedOpPackageName, /*startIfModeDefault*/ false)
!= AppOpsManager::MODE_ALLOWED) {
ALOGE("Request denied by app op: %d", op);
return false;
}
} else {
if (appOps.checkOp(op, uid, resolvedOpPackageName) != AppOpsManager::MODE_ALLOWED) {
ALOGE("Request denied by app op: %d", op);
return false;
}
}
return true;
}
解决以上权限问题后,正常录音即可,否则即使录音了,文件也是无声的。录音最好采用 amr 编码格式,体积小音质还行,综合相对比较适合录音。
private MediaRecorder mMediaRecorder;
private boolean isRecording;
private void initMediaRecorder() {
mMediaRecorder = new MediaRecorder();
// 设置音频来源 MIC == 麦克
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置默认音频输出格式 .amr 格式
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
// 设置默认音频编码方式 .amr 编码
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 指定音频输出文件路径
SimpleDateFormat simpleDateFormat = null;// HH:mm:ss
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
Date date = new Date(System.currentTimeMillis());
String fileName = simpleDateFormat.format(date);
File file = new File(Environment.getExternalStorageDirectory(), fileName + ".amr");
mMediaRecorder.setOutputFile(file);
}
}
// 开始录音
public void start() {
new Thread() {
@Override
public void run() {
super.run();
if (mMediaRecorder == null) {
initMediaRecorder();
}
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isRecording) {
try {
isRecording = true;
mMediaRecorder.prepare();
mMediaRecorder.start();
//开始录制
} catch (IOException e) {
e.printStackTrace();
isRecording = false;
}
}
}
}.start();
}
// 停止录音
public void stop() {
if (mMediaRecorder != null && isRecording) {
isRecording = false;
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
}
}
以上是关于Asterisk怎样实现通话录音的主要内容,如果未能解决你的问题,请参考以下文章
asterisk里 freeiris 系统自动录音怎么改录音名?
关于voip电话的几个问题 asterisk 软件 openwrt路由器 sip协议