【问题标题】:Enabling Camera Flash While Recording Video录制视频时启用相机闪光灯
【发布时间】:2012-07-22 07:35:09
【问题描述】:

我需要一种在 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-hacking,但这是我所知道的:

  • Camera.unlock() 是本机方法,因此我无法真正看到它锁定我的方式背后的机制
  • Camera.Parameter 有一个包含其所有参数的 HashMap
  • Camera.setParameters(Parameters) 获取 HashMap,将其转换为字符串,并将其传递给本地方法
  • 我可以从 HashMap 中消除除 TORCH-MODE 之外的所有参数,Camera 仍会接受它

所以,我仍然可以访问相机,但它不会听我告诉它的任何内容。 (这就是 Camera.unlock() 的目的)

编辑:

检查本机代码后,我可以看到在CameraService.cpp 中我对 Camera.setParameters(Parameters) 的调用被拒绝,因为我的进程 ID 与相机服务记录的进程 ID 不匹配。所以这似乎是我的障碍。

编辑2:

MediaPlayerService 似乎是在录制视频时控制摄像机的主要服务。我不知道这是否可能,但如果我能以某种方式在我自己的进程中启动该服务,我应该能够跳过 Camera.unlock() 调用。

编辑3:

如果我能以某种方式获得指向 CameraHardwareInterface 的指针,最后一个选择是。从外观上看,这是一个特定于设备的接口,可能不包括 PID 检查。不过,主要问题是我唯一能找到指向它的指针的地方是在 CameraService 中,而 CameraService 没有说话。

Edit4:(几个月后)

在这一点上,我认为不可能做到我最初想要的。我不想在有人回答的情况下删除该问题,但我并没有积极寻求答案。 (不过,收到一个有效的答案会很棒。)

【问题讨论】:

  • 我真的不明白为什么你会认为这是可能的。我希望视频录制能够完全控制相机。它需要在其中进行特殊编码,以允许其他东西同时控制相机。
  • 我完全同意,我认为目前也不可能,我只是在此时将问题留在这里,以防有人奇迹般地找到方法。

标签: java android c++ process android-camera


【解决方案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 属性的正确顺序,否则在调用 prepare() 或 start() 时会出现 IllegalStateException。说到录音,我就是这么做的。这通常由视图元素触发:

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 操作调用该方法时,我都可以更改 flash 模式,即使在录制期间也是如此。只需注意正确锁定相机即可。在录制过程中,一旦媒体记录器获得锁定,您就不必再次锁定/解锁相机。它甚至不起作用。这是在带有 Android 版本 4.1.2 的三星 Galaxy S3 上测试的。希望这种方法有所帮助。

【讨论】:

  • 我目前没有时间尝试这个。但为什么这行得通?看来,如果您当前正在录制, flash() 不会锁定相机,并且由于它已经解锁,因此 camera.setParameters() 将失败。感谢您成为第一个真正回答问题的人。
  • 我在这里没有深入的了解,但我假设记录器在调用 start() 和 stop() 之间获得了排他锁。因此,在此期间,其他进程对该资源的访问被拒绝,除了发生录制的 ui 主线程并且由于它还处理 ui 操作调用,因此这种方法应该可以正常工作。
  • 哦,所以只有UI线程可以改变flash状态?
  • 我猜,只要录制线程也处理 setParameters() 调用,它应该没关系,如果它是 ui 线程或任何其他。
  • 我又在做这个项目,所以如果这个方法有效,我可能会在接下来的几周内接受答案。
【解决方案2】:

准备好媒体记录器后,使用camera.lock(),然后设置你想要设置到camera的任何参数。 但是在开始录制之前你需要调用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】:

    要访问设备摄像头,您必须在 Android 清单中声明 CAMERA 权限。还要确保包含 &lt;uses-feature&gt; 清单元素以声明应用程序使用的相机功能。例如,如果您使用相机和自动对焦功能,您的清单应包括以下内容:

     <uses-permission android:name="android.permission.CAMERA" />
     <uses-feature android:name="android.hardware.camera" />
     <uses-feature android:name="android.hardware.camera.autofocus" />
    

    检查 Torch 支持的示例可能如下所示:

    //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

    【讨论】:

    • 这根本没有帮助。我知道如何打开闪光灯,问题是在录制视频时切换闪光灯。
    • 这没有回答问题。问题是如何在视频录制时打开闪光灯。
    猜你喜欢
    • 2011-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-30
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多