Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程
Android 8.1/9.0 MTK Camera源码分析之快门声音控制
在Android 8.1上mtk camera有控制快门声音的接口,但是并没有了控制录像快门声音的接口。之所以会有这个现象,主要原因是mtk camera仍旧使用的camera api1的接口。不同于camera api2,快门声音直接在上层控制,减少了很多麻烦。这一点在mtk 9.0的camera代码中就可以体现。
针对camera api1
快门声音控制
/frameworks/base/core/java/android/hardware/Camera.java中有定义控制快门声音的api。
ap中直接调用这个api来控制就可以快门声音,不过该api是camera api1,所以使用范围有限制。
public final boolean enableShutterSound(boolean enabled) {if (!enabled) {IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);IAudioService audioService = IAudioService.Stub.asInterface(b);try {if (audioService.isCameraSoundForced()) return false;} catch (RemoteException e) {Log.e(TAG, "Audio service is unavailable for queries");}}return _enableShutterSound(enabled);}
所以在ap层调用,直接使用parameter对象调用。
mCameraProxy.enableShutterSound(isShutterSoundOn); //控制拍照声音
api提供了enableShutterSound,也提供了disableShutterSound来直接关闭快门音:
public final boolean disableShutterSound() {return _enableShutterSound(/*enabled*/false);}
_enableShutterSound这个函数是调jni:
private native final boolean _enableShutterSound(boolean enabled);
在android_hardware_camera.cpp中定义:
static jboolean android_hardware_Camera_enableShutterSound(JNIEnv *env, jobject thiz,jboolean enabled)
{ALOGV("enableShutterSound");//get_native_camera指向的是Camera.h/Camera.cppsp<Camera> camera = get_native_camera(env, thiz, NULL);if (camera == 0) return JNI_FALSE;int32_t value = (enabled == JNI_TRUE) ? 1 : 0;//往client端发送指令CAMERA_CMD_ENABLE_SHUTTER_SOUNDstatus_t rc = camera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, value, 0);if (rc == NO_ERROR) {return JNI_TRUE;} else if (rc == PERMISSION_DENIED) {return JNI_FALSE;} else {jniThrowRuntimeException(env, "enable shutter sound failed");return JNI_FALSE;}
}
指令CAMERA_CMD_ENABLE_SHUTTER_SOUND最终会被传递到CameraClient.cpp。
文件路径:frameworks/av/services/camera/libcameraservice/api1/CameraClient.cpp。
status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) {LOG1("sendCommand (pid %d)", getCallingPid());int orientation;Mutex::Autolock lock(mLock);status_t result = checkPidAndHardware();if (result != NO_ERROR) return result;if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) {..........} else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) { //快门声音控制switch (arg1) {case 0:return enableShutterSound(false);//实际控制函数case 1:return enableShutterSound(true);//实际控制函数default:return BAD_VALUE;}return OK;} else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {sCameraService->playSound(CameraService::SOUND_RECORDING_START);} else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) {// Silently ignore this commandreturn INVALID_OPERATION;} else if (cmd == CAMERA_CMD_PING) {// If mHardware is 0, checkPidAndHardware will return error.return OK;}//将指令继续往hal层,用来通知底层,当快门按钮被按下时,及时响应音频播放。return mHardware->sendCommand(cmd, arg1, arg2);
}
然后再看这个enableShutterSound(true)实际控制快门音的函数:
// enable shutter sound
status_t CameraClient::enableShutterSound(bool enable) {LOG1("enableShutterSound (pid %d)", getCallingPid());status_t result = checkPidAndHardware();if (result != NO_ERROR) return result;if (enable) {mPlayShutterSound = true;return OK;}// the camera2 api legacy mode can unconditionally disable the shutter soundif (mLegacyMode) {ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__);mPlayShutterSound = false;return OK;}// Disabling shutter sound may not be allowed. In that case only// allow the mediaserver process to disable the sound.char value[PROPERTY_VALUE_MAX];设置了一个属性ro.camera.sound.forced,来通知property_get("ro.camera.sound.forced", value, "0");if (strcmp(value, "0") != 0) {// Disabling shutter sound is not allowed. Deny if the current// process is not mediaserver.if (getCallingPid() != getpid()) {ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid());return PERMISSION_DENIED;}}mPlayShutterSound = false;return OK;
}
看到这,有个想法,不知道在ap端直接设置系统属性property_get(“ro.camera.sound.forced”, value, “0”);能否达到控制快门音效果。后面在尝试一下看看。不过如果使用的api2的就不用上面这么麻烦了。
到这里其实已经看到,通过上层api调用,到service端接收指令,通知音频打开开关。那么触发时机还没有看到,不过正常逻辑一定是在按下快门之后,照片数据已经通过JpegCallback回调到上层时才开始播放。所以可以看下service中takePicture函数的处理。
// take a picture - image is returned in callback
status_t CameraClient::takePicture(int msgType) {LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType);Mutex::Autolock lock(mLock);status_t result = checkPidAndHardware();if (result != NO_ERROR) return result;if ((msgType & CAMERA_MSG_RAW_IMAGE) &&(msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"" cannot be both enabled");return BAD_VALUE;}// We only accept picture related message types// and ignore other types of messages for takePicture().//发送相关msg,其中就包括了shutter信息CAMERA_MSG_SHUTTERint picMsgType = msgType& (CAMERA_MSG_SHUTTER |CAMERA_MSG_POSTVIEW_FRAME |CAMERA_MSG_RAW_IMAGE |CAMERA_MSG_RAW_IMAGE_NOTIFY |CAMERA_MSG_COMPRESSED_IMAGE);enableMsgType(picMsgType); //往hal层继续传递//调用hal层takePicture函数return mHardware->takePicture();
}
而service把相关msg回调消息传递给hal层,也注册了消息回调机制,用来接收底层的回调。
可以看到在init中就注册了像hardware注册了回调接口。
status_t CameraClient::initialize(sp<CameraProviderManager> manager) {......char camera_device_name[10];snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);mHardware = new CameraHardwareInterface(camera_device_name);res = mHardware->initialize(manager);if (res != OK) {ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",__FUNCTION__, mCameraId, strerror(-res), res);mHardware.clear();return res;}//接口注册回调,其中notifyCallback就是消息通知的回调mHardware->setCallbacks(notifyCallback,dataCallback,dataCallbackTimestamp,handleCallbackTimestampBatch,(void *)(uintptr_t)mCameraId);......return OK;
}
接下来看下notifyCallback函数接收msg的处理。shutter的信息是CAMERA_MSG_SHUTTER。
void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,int32_t ext2, void* user) {LOG2("notifyCallback(%d)", msgType);sp<CameraClient> client = getClientFromCookie(user);if (client.get() == nullptr) return;if (!client->lockIfMessageWanted(msgType)) return;switch (msgType) {//!++case MTK_CAMERA_MSG_EXT_NOTIFY:client->handleMtkExtNotify(ext1, ext2); // Callback extended msg notification.break;
//!--case CAMERA_MSG_SHUTTER:// ext1 is the dimension of the yuv picture.client->handleShutter(); //调用handleShutter处理拍照快门声音break;default:client->handleGenericNotify(msgType, ext1, ext2);break;}
}
再看client->handleShutter()函数:
// snapshot taken callback
void CameraClient::handleShutter(void) {if (mPlayShutterSound) { //直接就指向到CameraService playSoundsCameraService->playSound(CameraService::SOUND_SHUTTER);}sp<hardware::ICameraClient> c = mRemoteCallback;if (c != 0) {mLock.unlock();c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return;}disableMsgType(CAMERA_MSG_SHUTTER);// Shutters only happen in response to takePicture, so mark device as// idle now, until preview is restartedsCameraService->updateProxyDeviceState(hardware::ICameraServiceProxy::CAMERA_STATE_IDLE,mCameraIdStr, mCameraFacing, mClientPackageName);mLock.unlock();
}
在cameraservers.cpp中分别有:加载声音、资源播放声音等等方法,快门音就是playSound函数。
加载声音,资源路径的引用。
void CameraService::loadSound() {ATRACE_CALL();Mutex::Autolock lock(mSoundLock);LOG1("CameraService::loadSound ref=%d", mSoundRef);if (mSoundRef++) return;//!++#if 0mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");mSoundPlayer[SOUND_RECORDING_START] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");mSoundPlayer[SOUND_RECORDING_STOP] = newMediaPlayer("/system/media/audio/ui/VideoStop.ogg");#elseif( pthread_create(&mloadSoundTThreadHandle, NULL, loadSoundThread, this) != 0 ){ALOGE("loadSound pthread create failed");}#endif//!--
}
//!++
void CameraService::loadSoundImp() {LOG1("[CameraService::loadSoundImp] E");mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");mSoundPlayer[SOUND_RECORDING_START] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");mSoundPlayer[SOUND_RECORDING_STOP] = newMediaPlayer("/system/media/audio/ui/VideoStop.ogg");LOG1("[CameraService::loadSoundImp] X");
}
再看声音播放playSound函数,需要先等待waitloadSoundDone()加载完毕,在进行资源播放。播放方式是使用的MediaPlayer。
void CameraService::playSound(sound_kind kind) {ATRACE_CALL();LOG1("playSound(%d)", kind);Mutex::Autolock lock(mSoundLock);//!++waitloadSoundDone();//!--sp<MediaPlayer> player = mSoundPlayer[kind];if (player != 0) {player->seekTo(0);player->start();}
}
针对camera api2
快门声音控制
mtk9.0 底层使用hal3.0,上层使用camera api2,所以针对8.1的上添加拍照快门声音的控制不同了。已经用不到了。
而在mtk9.0,camera2 app中,控制拍照声音、录像快门声音都是在app中来进行设置的。这样更加方便了。
拍照通过soundPlay的方式来进行控制:mICameraContext.getSoundPlayback().play(ISoundPlayback.SHUTTER_CLICK);
在注册了captureSession后,回调函数onCaptureStarted中表明按下快门,capture捕捉数据开始,在此响应拍照声音即可。
@Overridepublic void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, longtimestamp, long frameNumber) {super.onCaptureStarted(session, request, timestamp, frameNumber);if (mCamera2Proxy == null || session.getDevice() != mCamera2Proxy.getCameraDevice()) {return;}if (CameraUtil.isStillCaptureTemplate(request)) {LogHelper.d(TAG, "[onCaptureStarted] capture started, frame: " + frameNumber);if(isShutterSoundOn() ) {mICameraContext.getSoundPlayback().play(ISoundPlayback.SHUTTER_CLICK);}}}
api1的拍照快门声音流程相对麻烦,而api2的拍照快门音,完全可以通过captureSession回调中的函数来进行播放,这样非常方便,流程上简洁了很多。
Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程相关推荐
- Android 8.1/9.0 MTK Camera源码分析之录像快门声音控制流程
前面已经针对拍照快门声音控制流程进行了分析,接下来分析一下录像快门声音的控制流程. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程 这两篇文章其实都是相对于手机系统RO ...
- Android 9 (P)之init进程启动源码分析指南之三
Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...
- Android 9(P)之init进程启动源码分析指南之一
Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...
- Apollo 2.0 框架及源码分析(一) | 软硬件框架
原文地址:https://zhuanlan.zhihu.com/p/33059132 前言 如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法.关于 Apollo 介绍的文章已经有许多, ...
- 《MySQL 8.0.22执行器源码分析(3.2)关于HashJoinIterator》
在本文章之前,应该了解的概念: 连接的一些概念.NLJ.BNL.HashJoin算法. 目录 关于join连接 probe行保存概念 Hashjoin执行流程(十分重要) HashJoinIterat ...
- mosquitto客户端对象“struct mosquitto *mosq”管理下篇(mosquitto2.0.15客户端源码分析之四)
文章目录 前言 5 设置网络参数 5.1 客户端连接服务器使用的端口号 `mosq->port` 5.2 指定绑定的网络地址 `mosq->bind_address` 5.3 客户端连接服 ...
- HBase源码分析之HRegion上compact流程分析(三)
在<HBase源码分析之HRegion上compact流程分析(二)>一文中,我们没有讲解真正执行合并的CompactionContext的compact()方法.现在我们来分析下它的具体 ...
- Spark源码分析之Sort-Based Shuffle读写流程
一 概述 我们知道Spark Shuffle机制总共有三种: # 未优化的Hash Shuffle:每一个ShuffleMapTask都会为每一个ReducerTask创建一个单独的文件,总的文件数是 ...
- 【Android CameraX】CameraXBasic —— 官方CameraX实例源码分析
一.简介 二.源码分析 2.1 build.gradle 2.2 代码结构 2.3 变量 2.3.1 lensFacing 2.3.2 preview 2.3.3 Image capture 2.3. ...
最新文章
- 【端口号和服务漫谈】
- jsp访问WEB-INF下面的资源
- Office 2010的贴心功能:迷你翻译器
- linux下nginx+python+fastcgi部署总结(web.py版)
- Open quote is expected for attribute {1} associated with an element type name.
- 机器学习中为啥要有验证集?只要训练集和测试集不就可以了吗?
- 数据库中的时间数据在页面显示时后面会多一个.0的问题
- 删除所有的视图,存储过程
- Java集合—哈希(hash)表
- 网络自己发 sip invite_IP话机网页配置SIP账号
- Python回文判断代码优化与6个思考题
- Python练习题10道(含答案)
- excel 汉字转拼音
- 计划超越苹果!诺基亚在印度推出笔记本电脑
- 独立对honor荣耀来说有哪些好处?
- 计算二维紧束缚模型费米面和nesting程序新思路
- ABBYY FineReader OCR图文识别软件如何快速将纸质文档转为电子档教程
- win10系统开启IIS服务
- 自动控制原理(根轨迹)
- xcode连接新的iPhone进行app调试教程
热门文章
- android:gravity=“bottom|center_horizontal“的妙用
- idea打成war包
- vue中使用three绘制星空效果
- 三位一体:打造软硬服一体化的区块链平台
- 个人信息非法流出助长电信网络犯罪
- 河北专接本计算机课程网课,河北专接本网课资源那个好?
- 如何使EndNoteX9支持GB/T 7714-2015格式参考文献解决作者名字全为大写去掉文章标题中的%J
- 微信朋友圈是访问腾讯服务器吗,微信朋友圈疑似增加“来访”新功能?网友:再也不看朋友圈了!...
- Java 编写一个程序,为一个给定的年份找出中国生肖。中国生肖12年一个周期,每年用一个动物表示——鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗、猪。
- 如何写出一份高逼格的简历?新技能 get√