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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zimu_2080/article/details/86642310

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.cpp
    sp<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_SOUND
    status_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 command
        return 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 sound
    if (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_SHUTTER
    int 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 playSound
        sCameraService->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 restarted
    sCameraService->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 0
    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");
    #else
    if( 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捕捉数据开始,在此响应拍照声音即可。

        @Override
        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long
                timestamp, 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回调中的函数来进行播放,这样非常方便,流程上简洁了很多。

猜你喜欢

转载自blog.csdn.net/zimu_2080/article/details/86642310