Включение вспышки камеры во время записи видео
Мне нужен способ управлять вспышкой камеры на устройстве Android, когда он записывает видео. Я создаю приложение для стробоскопа, и съемка с мигающим светом строба приведет к возможности записи объектов, движущихся с высокой скоростью, например, лопасти вентилятора.
Вспышку можно включить только путем запуска предварительного просмотра видео и установки FLASH_MODE_TORCH в параметрах камеры. Это будет выглядеть так:
Camera c = Camera.open();
Camera.Parameters p = c.getParameters();
p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
c.setParameters(p);
c.startPreview();
Как только предварительный просмотр запущен, я могу перевернуть этот параметр взад и вперед, чтобы включить и выключить свет. Это работает хорошо, пока я не попытаюсь записать видео. Проблема в том, что для того, чтобы дать камеру MediaRecorder, я сначала должен ее разблокировать.
MediaRecorder m = new MediaRecorder();
c.unlock(); // the killer
m.setCamera(c);
После этой разблокировки я больше не могу изменять параметры камеры и поэтому не могу изменить состояние вспышки.
Я не знаю, действительно ли это возможно, поскольку я не лучший в java-взломе, но вот что я знаю:
- Camera.unlock() - это собственный метод, поэтому я не могу увидеть механизм, который блокирует меня.
- Camera.Parameter имеет HashMap, который содержит все его параметры
- Параметры Camera.setParameters(Parameters) принимают HashMap, преобразуют его в строку и передают его собственному методу
- Я могу удалить все параметры, но TORCH-MODE из HashMap, и камера все равно примет его
Итак, я все еще могу получить доступ к Camera, но он не будет слушать ничего, о чем я говорю. (Какова цель Camera.unlock())
Edit:
После изучения собственного кода я вижу, что в CameraService.cpp мои вызовы на Camera.setParameters(Parameters) отклоняются, потому что мой Идентификатор процесса не соответствует идентификатору процесса, на котором установлена служба камеры. Таким образом, казалось бы, это мое препятствие.
Edit2:
Похоже, что MediaPlayerService является основной услугой, которая управляет камерой при записи видео. Я не знаю, возможно ли это, но если бы я мог как-то запустить эту службу в моем собственном процессе, я должен был бы пропустить вызов Camera.unlock().
Edit3:
Один из последних вариантов был бы, если бы я мог каким-то образом получить указатель на CameraHardwareInterface. По внешнему виду это интерфейс, специфичный для устройства, и, вероятно, не включает проверки PID. Основная проблема заключается в том, что единственное место, где я могу найти указатель на него, - в CameraService, а CameraService не говорит.
Edit4: (несколько месяцев спустя)
В этот момент я не думаю, что можно делать то, что я изначально хотел. Я не хочу удалять вопрос о том, что кто-то отвечает на него, но я не активно ищу ответ. (Хотя получение правильного ответа было бы удивительным.)
Ответы
Ответ 1
Я столкнулся с подобной проблемой. Пользователь должен иметь возможность изменять режим вспышки во время записи для удовлетворения своих потребностей в зависимости от ситуации с освещением. После некоторых исследований я пришел к следующему решению:
Я предполагаю, что вы уже создали правильный SurfaceView и SurfaceHolder с необходимыми обратными вызовами. Первое, что я сделал, это предоставить этот код (не объявленные переменные являются глобальными):
public void surfaceCreated(SurfaceHolder holder) {
try {
camera = Camera.open();
parameters = camera.getParameters();
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameters);
camera.setPreviewDisplay(holder);
camera.startPreview();
recorder = new MediaRecorder();
} catch (IOException e) {
e.printStackTrace();
}
}
Мой следующий шаг состоял в инициализации и подготовке рекордера:
private void initialize() {
camera.unlock();
recorder.setCamera(camera);
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
recorder.setVideoFrameRate(20);
recorder.setOutputFile(filePath);
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
finish();
} catch (IOException e) {
e.printStackTrace();
finish();
}
}
Важно отметить, что camera.unlock() нужно называть ПЕРЕД всей инициализацией медиа-рекордера. При этом также следует знать правильный порядок каждого свойства set, иначе вы получите исключение IllegalStateException при вызове prepare() или start(). Когда дело доходит до записи, я делаю это. Обычно это будет вызвано элементом вида:
public void record(View view) {
if (recording) {
recorder.stop();
//TODO: do stuff....
recording = false;
} else {
recording = true;
initialize();
recorder.start();
}
}
Итак, теперь я, наконец, смогу записать правильно. Но что с этой вспышкой? И последнее, но не менее важное: сюда приходит волшебство за кадром:
public void flash(View view) {
if(!recording) {
camera.lock();
}
parameters.setFlashMode(parameters.getFlashMode().equals(Parameters.FLASH_MODE_TORCH) ? Parameters.FLASH_MODE_OFF : Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameters);
if(!recording) {
camera.unlock();
}
}
Каждый раз, когда я вызываю этот метод с помощью действия onClick, я могу изменить режим вспышки даже во время записи. Просто позаботьтесь о правильной фиксации камеры. После того, как замок во время записи используется медиарекордером, вам не нужно снова блокировать/разблокировать камеру. Это даже не работает. Это было протестировано на Samsung Galaxy S3 с Android-версией 4.1.2. Надеюсь, что этот подход поможет.
Ответ 2
После подготовки медиарекордера используйте camera.lock(), а затем установите все параметры, которые вы хотите установить на камеру.
Но перед началом записи вам нужно вызвать camera.unlock(), а после остановки медиа-рекордера вам нужно вызвать camera.lock(), чтобы начать предварительный просмотр.
Наслаждайтесь!!!
Ответ 3
Попробуйте это.. надеюсь, это сработает..:)
private static Torch torch;
public Torch() {
super();
torch = this;
}
public static Torch getTorch() {
return torch;
}
private void getCamera() {
if (mCamera == null) {
try {
mCamera = Camera.open();
} catch (RuntimeException e) {
Log.e(TAG, "Camera.open() failed: " + e.getMessage());
}
}
}
public void toggleLight(View view) {
toggleLight();
}
private void toggleLight() {
if (lightOn) {
turnLightOff();
} else {
turnLightOn();
}
}
private void turnLightOn() {
if (!eulaAgreed) {
return;
}
if (mCamera == null) {
Toast.makeText(this, "Camera not found", Toast.LENGTH_LONG);
button.setBackgroundColor(COLOR_WHITE);
return;
}
lightOn = true;
Parameters parameters = mCamera.getParameters();
if (parameters == null) {
button.setBackgroundColor(COLOR_WHITE);
return;
}
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes == null) {
button.setBackgroundColor(COLOR_WHITE);
return;
}
String flashMode = parameters.getFlashMode();
Log.i(TAG, "Flash mode: " + flashMode);
Log.i(TAG, "Flash modes: " + flashModes);
if (!Parameters.FLASH_MODE_TORCH.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_TORCH)) {
parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(parameters);
button.setBackgroundColor(COLOR_LIGHT);
startWakeLock();
} else {
Toast.makeText(this, "Flash mode (torch) not supported",
Toast.LENGTH_LONG);
button.setBackgroundColor(COLOR_WHITE);
Log.e(TAG, "FLASH_MODE_TORCH not supported");
}
}
}
private void turnLightOff() {
if (lightOn) {
button.setBackgroundColor(COLOR_DARK);
lightOn = false;
if (mCamera == null) {
return;
}
Parameters parameters = mCamera.getParameters();
if (parameters == null) {
return;
}
List<String> flashModes = parameters.getSupportedFlashModes();
String flashMode = parameters.getFlashMode();
if (flashModes == null) {
return;
}
Log.i(TAG, "Flash mode: " + flashMode);
Log.i(TAG, "Flash modes: " + flashModes);
if (!Parameters.FLASH_MODE_OFF.equals(flashMode)) {
if (flashModes.contains(Parameters.FLASH_MODE_OFF)) {
parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
stopWakeLock();
} else {
Log.e(TAG, "FLASH_MODE_OFF not supported");
}
}
}
}
private void startPreview() {
if (!previewOn && mCamera != null) {
mCamera.startPreview();
previewOn = true;
}
}
private void stopPreview() {
if (previewOn && mCamera != null) {
mCamera.stopPreview();
previewOn = false;
}
}
private void startWakeLock() {
if (wakeLock == null) {
Log.d(TAG, "wakeLock is null, getting a new WakeLock");
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Log.d(TAG, "PowerManager acquired");
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
Log.d(TAG, "WakeLock set");
}
wakeLock.acquire();
Log.d(TAG, "WakeLock acquired");
}
private void stopWakeLock() {
if (wakeLock != null) {
wakeLock.release();
Log.d(TAG, "WakeLock released");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Eula.show(this)) {
eulaAgreed = true;
}
setContentView(R.layout.main);
button = findViewById(R.id.button);
surfaceView = (SurfaceView) this.findViewById(R.id.surfaceview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
disablePhoneSleep();
Log.i(TAG, "onCreate");
}
Ответ 4
Чтобы получить доступ к камере устройства, вы должны объявить разрешение CAMERA в своем манифесте Android. Также не забудьте включить элемент манифеста <uses-feature>
, чтобы объявить функции камеры, используемые вашим приложением. Например, если вы используете функцию камеры и автофокуса, ваш манифест должен включать следующее:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
Образец, который проверяет поддержку факела, может выглядеть примерно так:
//Create camera and parameter objects
private Camera mCamera;
private Camera.Parameters mParameters;
private boolean mbTorchEnabled = false;
//... later in a click handler or other location, assuming that the mCamera object has already been instantiated with Camera.open()
mParameters = mCamera.getParameters();
//Get supported flash modes
List flashModes = mParameters.getSupportedFlashModes ();
//Make sure that torch mode is supported
//EDIT - wrong and dangerous to check for torch support this way
//if(flashModes != null && flashModes.contains("torch")){
if(flashModes != null && flashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)){
if(mbTorchEnabled){
//Set the flash parameter to off
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
}
else{
//Set the flash parameter to use the torch
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
}
//Commit the camera parameters
mCamera.setParameters(mParameters);
mbTorchEnabled = !mbTorchEnabled;
}
Чтобы включить факел, вы просто устанавливаете параметр камеры Camera.Parameters.FLASH_MODE_TORCH
Camera mCamera;
Camera.Parameters mParameters;
//Get a reference to the camera/parameters
mCamera = Camera.open();
mParameters = mCamera.getParameters();
//Set the torch parameter
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
//Comit camera parameters
mCamera.setParameters(mParameters);
Чтобы выключить факел, установите Camera.Parameters.FLASH_MODE_OFF