2011年2月11日 星期五

Android 中使用 Camera

1. 在 AndroidManifest.xml 中加上 permission 和 feature

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


2. 再來在 layout 中加上 SurfaceView


<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="320px"
android:layout_height="240px">
</SurfaceView>

3. 程式碼

參考 Android SDK 網頁
http://developer.android.com/reference/android/hardware/Camera.html
下面是範例code


package newslab.video.server;

import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class VideoServer extends Activity implements SurfaceHolder.Callback {
TextView testView;

Camera camera;
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;

private final String tag = "VideoServer";

Button start, stop;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
      
        start = (Button)findViewById(R.id.btn_start);
        start.setOnClickListener(new Button.OnClickListener()
        {
public void onClick(View arg0) {
start_camera();
}
        });
        stop = (Button)findViewById(R.id.btn_stop);
        stop.setOnClickListener(new Button.OnClickListener()
        {
public void onClick(View arg0) {
stop_camera();
}
        });
      
        surfaceView = (SurfaceView)findViewById(R.id.surfaceView1);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
  
    private void start_camera()
    {
     try{
     camera = Camera.open();
     }catch(RuntimeException e){
     Log.e(tag, "init_camera: " + e);
     return;
     }
     Camera.Parameters param;
     param = camera.getParameters();
     //modify parameter
     param.setPreviewFrameRate(20);
     param.setPreviewSize(176, 144);
     camera.setParameters(param);
     try {
camera.setPreviewDisplay(surfaceHolder);
camera.startPreview();
} catch (Exception e) {
Log.e(tag, "init_camera: " + e);
return;
}
    }
  
    private void stop_camera()
    {
     camera.stopPreview();
     camera.release();
    }

public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
// TODO Auto-generated method stub
}

public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
}

public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}


startPreview 之後 就可以看是要照像還是拍影片了

56 則留言:

  1. 我在google搜尋android surfaceholder
    你的網頁是第二個耶好酷哦~~!!

    回覆刪除
  2. 你好!想請問一下~
    我有在手機上實作出來,可是好像沒辦法儲存
    我是想要實作錄影
    不知道startPreview之後是要如何改
    謝謝喔~

    回覆刪除
  3. 如果想要把影片存到 SD 卡的話,要用到 MediaRecorder

    先加上權限


    接著在 camera.startPreview(); 之後 加上下面的code

    camera.unlock();
    mediaRecorder = new MediaRecorder();
    mediaRecorder.setCamera(camera);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
    mediaRecorder.setOutputFile("/sdcard/record.3gp");
    mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
    mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
    mediaRecorder.setVideoSize(176, 144);
    mediaRecorder.prepare();
    mediaRecorder.start();

    mediaRecorder 各項設定有一定的順序與必要性
    可以參考 android sdk 的網站,有比較詳細的解說
    這邊只寫出最少且可以執行的設定



    ===============================

    在 camera.stopPreview() 之前 加上下面的 code

    mediaRecorder.stop();
    mediaRecorder.reset();
    mediaRecorder.release();
    try {
    camera.reconnect();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    Log.e(tag, "stop_camera: " + e);
    }

    回覆刪除
  4. 補充一下 要加上的權限是 WRITE_EXTERNAL_STORAGE
    這樣才可以寫入SD卡中

    回覆刪除
  5. 你好!請問一下
    我在camera.reconnect();這地方還是一直出錯
    出現的訊息是
    The method reconnect() is undefined for the type Camera
    不知道該怎麼解決~
    謝謝喔~

    回覆刪除
  6. 這應該是 Android 版本的問題
    reconnect 要 android 2.2 之後才能用

    試試 camera.lock();

    回覆刪除
  7. 你好!
    難怪了,我用的是2.1版本
    不過換了camera.lock();
    變成IOException e有問題
    然後這整段要刪掉
    try {
    camera.reconnect();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    Log.e(tag, "stop_camera: " + e);
    }

    這樣會有影響嗎?

    這樣改完後可以成功執行
    存檔也成功

    不過播出來的是雜訊?!
    會是編碼的問題嗎?

    謝謝囉~

    回覆刪除
  8. 那整段刪掉應該是沒問題
    我改成 lock()之後執行,畫面也是正常的

    你可以試試看看各項設定

    回覆刪除
  9. 你好!不好意思耶
    在請問一下,如果我想要一開始就有預覽畫面
    而不是按Start之後才有
    這樣應該怎麼改?

    謝謝你~

    回覆刪除
    回覆
    1. 請問如何弄成一開始就有預覽畫面...我弄不出來.

      刪除
  10. 你好~
    然後在你說改lock()的地方
    try {
    camera.lock();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    Log.e(tag, "stop_camera: " + e);
    }

    IOException e 是不是應該是Exception e?

    謝謝~

    回覆刪除
  11. 我是把整個 try 都去掉
    只有 camera.lock();

    回覆刪除
  12. 謝謝你囉~
    我後來有改出可以存檔也可以順利播放
    一開始也有顯示預覽畫面
    太感恩了~

    回覆刪除
    回覆
    1. Hello Tommy,
      參考了您和顧顧的討論過程,實作出來了"可錄製、可存檔"狀態
      但是"開始就顯示預覽畫面"這一步沒有做出來,
      請教您這一步也如何做到呢?

      感謝!

      刪除
  13. 請問一下,怎樣字定義相機,加入瞄準框??大概要怎麼做

    回覆刪除
  14. 不好意思 問一下
    我想取得鏡頭畫面的pixel值,並將pixel值傳給JNI做影像的處理
    我要如何取得鏡頭畫面的pixel值(surfaceview畫面)

    回覆刪除
    回覆
    1. 取得相機的畫面可以用 Camera.PreviewCallback 這個interface
      相機每抓取一張畫面就會call一次callback function

      刪除
    2. 我開啟相機的方式如下
      private Button.OnClickListener start_camera = new Button.OnClickListener()
      {
      public void onClick(View v)
      {
      try
      {
      camera = Camera.open();
      camera.setPreviewDisplay(surface_buf_inter);
      camera.startPreview();
      Log.e("Camera_test", "Camera open success!!! : ");
      }
      catch(RuntimeException err)
      {
      Log.e("Camera_test", "Camera open fail!!! : " + err);
      }
      catch(IOException err)
      {
      Log.e("Camera_test", "Set preview fail!!! : " + err);
      }
      camera.startPreview();
      }
      };

      並非使用SurfaceHolder.Callback 來開起相機
      這樣的話我還是一樣可以用Camera.PreviewCallback  來抓取畫面嗎??

      刪除
    3. Camera.PreviewCallback 只有這種方式可以抓取嗎?

      刪除
    4. Camera.PreviewCallback 是一個 interface
      你需要一個class 去implement 他
      然後用 camera.setPreviewCallback()
      這個function 來設定
      可以參考
      http://developer.android.com/reference/android/hardware/Camera.PreviewCallback.html

      刪除
    5. Camera.PreviewCallback
      每次會一直刷新畫面
      這樣我要怎把之前的畫面存再暫存器...
      因為我要利用JNI 將每張畫面給錄檔....
      我是新手.....不知怎將每次刷新的畫面存在暫存器

      刪除
    6. 單純錄影應該不需要用到 JNI 吧?
      可以直接用 mediaRecorder 就可以錄了

      刪除
  15. hi~請問一下如何把相機調整成和螢幕同側的相機???

    回覆刪除
    回覆
    1. 可以參考一下這邊的回答
      http://stackoverflow.com/questions/2779002/how-to-open-front-camera-on-android-platform

      刪除
    2. 十分感謝!!!!!

      刪除
  16. 不好意思在請問一下~請問如果要相機連拍如何實做??

    回覆刪除
  17. 不好意思 請問一下 ...
    我有相機的程式了
    可是 每次開起都為橫向 ...
    想要請問一下 要怎麼將他改為直向呢 ... ?

    回覆刪除
    回覆
    1. 可以參考一下網站,應該可以改過來

      http://www.fish24k.com/?p=655965

      刪除
    2. Hi 顧顧,
      根據您的範例,我加入setCameraDisplayOrientation的程式碼卻仍然橫向預覽,請教您要更改何處才對呢?

      刪除
  18. 請問要怎麼寫才可以不讓這個程式覆蓋先前錄的影片?!

    回覆刪除
    回覆
    1. 儲存的檔名是用 setOutputFile 來指定的,可以判斷檔案如果存在的話就換一個檔名

      刪除
    2. private void start_camera()
      {
      try{
      camera = Camera.open();
      camera.setDisplayOrientation(90);
      }catch(RuntimeException e){
      Log.e(tag, "init_camera: " + e);
      return;
      }
      Camera.Parameters param;
      param = camera.getParameters();
      //modify parameter
      param.setPreviewFrameRate(20);
      param.setPreviewSize(176, 144);
      camera.setParameters(param);
      String fileName = "video.3gp";
      try {
      camera.setPreviewDisplay(surfaceHolder);
      camera.startPreview();
      camera.unlock();
      File SDCardpath = Environment.getExternalStorageDirectory();
      File myDataPath = new File( SDCardpath.getAbsolutePath() + "/video" );
      if( !myDataPath.exists() ) myDataPath.mkdirs();
      File recodeFile = new File(SDCardpath.getAbsolutePath() + "/video/"+fileName);

      mrecorder = new MediaRecorder();
      mrecorder.setCamera(camera);
      mrecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
      mrecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
      mrecorder.setOutputFile(mrecAudioFile
      .getAbsolutePath());
      mrecorder.setPreviewDisplay(surfaceHolder.getSurface());
      mrecorder.setOutputFile(recodeFile.getAbsolutePath());

      mrecorder.setVideoSize(176, 144);
      mrecorder.prepare();
      mrecorder.start();
      }
      catch (Exception e) {
      Log.e(tag, "init_camera: " + e);
      return;
      }
      }

      private void stop_camera()
      {
      mrecorder.stop();
      mrecorder.reset();
      mrecorder.release();
      try {
      camera.reconnect();
      }
      catch (IOException e) {
      // TODO Auto-generated catch block
      Log.e(tag, "stop_camera: " + e);
      }

      camera.stopPreview();
      camera.release();
      }



      我把setOutputFile成這樣又多加了幾行,但是這樣跑的時候變成停止鍵沒辦法按!!
      但是我不知道是錯哪?

      刪除
    3. 不好意思
      我加了這行String fileName = "video.3gp";
      然後在後面又加了
      File SDCardpath=Environment.getExternalStorageDirectory();
      File myDataPath=new File SDCardpath.getAbsolutePath()+"/DCIM/Camera");
      if( !myDataPath.exists() ) myDataPath.mkdirs();
      File recodeFile = new File(SDCardpath.getAbsolutePath() + "/DCIM/Camera/"+fileName);

      然後把setOutputFile後面改成(recodeFile.getAbsolutePath());


      改成這樣可以跑,但是還是會覆蓋原檔!
      因為對這個還不是很熟,所以一直看不出來要改哪些東西

      刪除
    4. fileName 就是要存的檔名,如果存在的話就要重設一個,不改的話都是存在 video.3gp 裡

      刪除
    5. 摁摁,謝謝 我知道怎麼改了

      刪除
    6. 可以錄了,但是好像沒聲音!!
      是要加mrecorder01.setAudioSource(MediaRecorder.AudioSource.MIC);這行嗎?
      可是加了就沒辦法按停止了

      刪除
  19. 請問一下會有版本的問題嗎?
    我是使用Android 4.0.3版本
    沒加入錄影那段code之前,程式可以正常start preview和stop preview

    加入錄影的code之後,只有start preview能正常work,
    但stop preview按下沒有反應。

    謝謝

    回覆刪除
  20. 您好 請問我要在相機預覽狀態下讓它自己抓圖片(不靠)並存在手機裡該如何改?

    回覆刪除
    回覆
    1. 取得相機的畫面可以用 Camera.PreviewCallback 這個interface
      相機每抓取一張畫面就會call一次callback function,這時再存入SD卡中即可

      刪除
  21. 版大您好
    請問為什麼直接把您po的code貼上去後,燒錄到手機裡裡執行時,按下start的button畫面還是黑色的呢?

    回覆刪除
    回覆
    1. 你好:

      我自己的code都是有測試過可以執行的,不過經過多次 eclipse 和 Android 改版,說不定會有問題,可以用 LogCat 來看看程式是否有成功執行或是有發生 Exception

      刪除



    2. 要加反斜線

      刪除
  22. 版大您好:
    想問一下,可以不使用preview的方式,直接開啟camera,然後拍照嗎?

    回覆刪除
    回覆
    1. 根據 SDK 寫的
      http://developer.android.com/reference/android/hardware/Camera.html
      一定要執行 startPreview 才行開始照相

      不過我想應該是可以用一個看不見的 surfaceview 來達到沒有預覽畫面可是可以拍照的功能,你可以試試看

      刪除
  23. 板大你好 我是照您的code方法做的
    但是只有畫面 start鈕 無法擷取照片和儲存??

    回覆刪除
  24. 你好~我想請問使用setPreviewCallback(this)要放在哪裡?
    我將取得的圖片數據透過JNI做成灰階圖片再傳回用ImageView顯示
    如果我設置一按鈕啟用PreviewCallback後,成功顯示一張,但之後整個程式就停止運作
    如果放在surfaceCreated裡感覺沒反應

    回覆刪除
    回覆
    1. setPreviewCallback 是要放在 Camara open 之後,設定完後還要 implement 一個 Callback Function 讓相機把預覽到的畫面傳給他,才可以接收到畫面

      刪除
    2. 這些我有做啊 ~ 我把抓到的影像數據轉成灰階圖片後,可顯示一張,但是沒辦法一直重復顯示,有東西沒釋放掉的感覺,也不能做其他的操作

      刪除
  25. 大大您好,想請問一下
    我有實作出來預覽畫面
    現在我想做出拍照的功能
    但是不太了解startPreview之後是要怎麼改

    謝謝您~

    回覆刪除
  26. 大大您好,想請問一下
    我使用inventor來做app開啟相機
    但是inventor只能開啟相機
    無法再使用相機ㄉ其他進階功能
    請問~我可以
    使用inventor製作版面
    再來嵌入其他程式來使用相機ㄉ進階功能ㄇ?
    如~只能拍灰階ㄉ照片~

    回覆刪除
  27. 請問你會實作 錄影暫停的功能嗎? 暫停後還可以繼續錄!

    回覆刪除
    回覆
    1. 你好:這個部分android並沒有內建,如果需要的話最簡單的方法就是錄製兩個影片後再把他們接起來,一樣可以達到暫停的效果

      刪除
  28. 你好~我想問 設定錄影格式有甚麼限制嗎?
    我用了官網的方法setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
    在停止錄影時會報錯

    所以改用自己設定格式不會報錯,但儲存完的影片用手機撥放器撥放,畫面是錯誤的

    回覆刪除
  29. 請問一下
    他說 btn_start cannot be resolved or is not a field
    還有 btn_stop 跟 surfaceView1 都是一樣情形
    請問該如何解決
    謝謝

    回覆刪除
  30. 作者已經移除這則留言。

    回覆刪除
  31. 感謝,非常實用,收穫良多

    回覆刪除