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源码分析之快门声音控制流程相关推荐

  1. Android 8.1/9.0 MTK Camera源码分析之录像快门声音控制流程

    前面已经针对拍照快门声音控制流程进行了分析,接下来分析一下录像快门声音的控制流程. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程 这两篇文章其实都是相对于手机系统RO ...

  2. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

  3. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  4. Apollo 2.0 框架及源码分析(一) | 软硬件框架

    原文地址:https://zhuanlan.zhihu.com/p/33059132 前言 如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法.关于 Apollo 介绍的文章已经有许多, ...

  5. 《MySQL 8.0.22执行器源码分析(3.2)关于HashJoinIterator》

    在本文章之前,应该了解的概念: 连接的一些概念.NLJ.BNL.HashJoin算法. 目录 关于join连接 probe行保存概念 Hashjoin执行流程(十分重要) HashJoinIterat ...

  6. mosquitto客户端对象“struct mosquitto *mosq”管理下篇(mosquitto2.0.15客户端源码分析之四)

    文章目录 前言 5 设置网络参数 5.1 客户端连接服务器使用的端口号 `mosq->port` 5.2 指定绑定的网络地址 `mosq->bind_address` 5.3 客户端连接服 ...

  7. HBase源码分析之HRegion上compact流程分析(三)

    在<HBase源码分析之HRegion上compact流程分析(二)>一文中,我们没有讲解真正执行合并的CompactionContext的compact()方法.现在我们来分析下它的具体 ...

  8. Spark源码分析之Sort-Based Shuffle读写流程

    一 概述 我们知道Spark Shuffle机制总共有三种: # 未优化的Hash Shuffle:每一个ShuffleMapTask都会为每一个ReducerTask创建一个单独的文件,总的文件数是 ...

  9. 【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. ...

最新文章

  1. 【端口号和服务漫谈】
  2. jsp访问WEB-INF下面的资源
  3. Office 2010的贴心功能:迷你翻译器
  4. linux下nginx+python+fastcgi部署总结(web.py版)
  5. Open quote is expected for attribute {1} associated with an element type name.
  6. 机器学习中为啥要有验证集?只要训练集和测试集不就可以了吗?
  7. 数据库中的时间数据在页面显示时后面会多一个.0的问题
  8. 删除所有的视图,存储过程
  9. Java集合—哈希(hash)表
  10. 网络自己发 sip invite_IP话机网页配置SIP账号
  11. Python回文判断代码优化与6个思考题
  12. Python练习题10道(含答案)
  13. excel 汉字转拼音
  14. 计划超越苹果!诺基亚在印度推出笔记本电脑
  15. 独立对honor荣耀来说有哪些好处?
  16. 计算二维紧束缚模型费米面和nesting程序新思路
  17. ABBYY FineReader OCR图文识别软件如何快速将纸质文档转为电子档教程
  18. win10系统开启IIS服务
  19. 自动控制原理(根轨迹)
  20. xcode连接新的iPhone进行app调试教程

热门文章

  1. android:gravity=“bottom|center_horizontal“的妙用
  2. idea打成war包
  3. vue中使用three绘制星空效果
  4. 三位一体:打造软硬服一体化的区块链平台
  5. 个人信息非法流出助长电信网络犯罪
  6. 河北专接本计算机课程网课,河北专接本网课资源那个好?
  7. 如何使EndNoteX9支持GB/T 7714-2015格式参考文献解决作者名字全为大写去掉文章标题中的%J
  8. 微信朋友圈是访问腾讯服务器吗,微信朋友圈疑似增加“来访”新功能?网友:再也不看朋友圈了!...
  9. Java 编写一个程序,为一个给定的年份找出中国生肖。中国生肖12年一个周期,每年用一个动物表示——鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗、猪。
  10. 如何写出一份高逼格的简历?新技能 get√