PS:看來算法和數據結構還是非常有用的,以后每天都練習兩道算法題目...這次忘了對代碼進行折疊了..導致篇幅過長...
學習內容:
1.Android如何實現相機功能...
2.如何實現音頻的錄制...
3.如何實現視頻的錄制...
Android的相機是Android手機內部必不可少的一個應用軟件...那么Android的相機機制是如何實現的呢?在Android內部提供了一個內部類android.view.SurfaceHolder這個類,這個類提供了SurfaceView,這個類可以幫助我們實現照相功能...SurfaceView想必都很熟悉了...使用它我們還可以進行視頻音頻的播放...在這個類的基礎上,我們還可以通過Camera類提供的內部方法,對相機的參數進行一些相應的設置,以達到我們拍照時想要的效果...Camera內部還定義了若干個接口來完成一些相應的操作...比如說按下快門的時候要處理什么樣的操作...
通過Camera來操控攝像頭需要按照步驟來...
i.首先調用Camera的open方法來打開攝像頭...
//現在的相機一般都有兩個攝像頭,我們需要定義我們想要打開哪個攝像頭,默認的是后置的攝像頭...這里打開的也是后置的攝像頭... if(Camera.getNumberOfCameras()==2){ camera=Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); }else{ camera=Camera.open(0); }
ii.接着調用getParammeters來獲取相機的參數...
Parameters param=MainActivity.this.camera.getParameters();//獲取相機參數的方法...
iii.設置相機的相應參數,來達到我們想要的效果...
param.setPreviewFrameRate(5);//設置預覽時每秒五幀進行顯示 param.setPictureFormat(PixelFormat.JPEG);//設置圖片的格式為JPEG param.set("jpeg-quality", 80);//設置圖片的質量為80,最大值為100
iv.調用setParammeters來講我們設置的參數傳遞到相機,達到我們拍照時想要出現的效果...
camera.setParameters(param);
v.在我們進行拍照的時候我們必然需要先進行預覽全景,在預覽的某一時刻進行拍照...那么這個預覽必然就需要一個SurfaceView來幫助我們來顯示當前我們預覽的場景...
try { MainActivity.this.camera.setPreviewDisplay(MainActivity.this.sufh);//設置預覽的對象為SurfaceHolder。。。 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } MainActivity.this.camera.startPreview();//開始預覽..
vi.開始拍照...獲取圖片,然后對圖片進行保存,最后釋放內存...總體也就分這么幾個步驟...
然后我們首先一個簡單的拍照功能...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <Button android:id="@+id/but" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="開始拍照..."/> <SurfaceView android:id="@+id/surface" android:layout_width="fill_parent" android:layout_height="match_parent"/> </LinearLayout>
簡單說一下java文件里如何實現拍照的一個思路,首先我們必然需要打開一個攝像頭,然后接着我們需要獲取攝像頭的參數,然后自定義我們拍照時想要的一些設置,這些設置呢那么必然需要先傳遞到攝像頭,否則不進行傳遞的話,攝像頭也就不會按照我們設置的參數進行工作,因此我們需要傳遞參數,傳遞完參數后,我們就需要進行拍照了,在拍照的時候呢,我們需要預覽景觀,那么預覽的功能就需要使用到SurfaceView這個類定義一個表面的視圖控件,說白了就是需要一塊面積來顯示現在用戶想看到的東西,然后在預覽的時候我們就可以進行拍照了,拍照需要使用takepicture()這個方法來完成,然后獲取到圖片,最后我們把照片數據通過IO流來寫入到內存當中,最后進行保存...這就是實現代碼...
package com.example.camera; /* * 1.首先調用Camera的open()方法打開攝像機... * private Camera camera; * camera=Camera.open(0); * 2.調用Camera.setParameters()方法獲取攝像機的參數... * Parameters param=camera.setParameters(); * 3.設置攝像機的拍照參數來配置拍照信息... * param.setPreviewSize(display.getWidth(),display.getHeight());設置預覽大小.. * param.setPreviewFrameRate(4)..以每秒四幀顯示圖像信息... * param.setPictureFormat(PixelFormat.JPEG);設置圖片的格式... * param.set("jpeg-quality",85);設置圖片的質量,最高為100... * parameters.setPictureSize(screenWidth,screenHeight);設置照片的大小... * 4.param.setParamters(param);將參數傳遞給相機,使相機可以指定相應的參數來完成拍攝... * 5.使用setPreview(SurfaceView)設置使用哪個SurfaceView來顯示要預覽的景象... * MainActivity.this.cma.setPreView(SurfaceHolder holder)...防止主線程阻塞..因此另外開啟線程... * MainActivity.this.cma.startPreView();開始預覽... * MainActivity.this.cma.autoFocus(afcb); * 6.進行拍照,然后獲取拍到的圖片進行保存... * cma.takePicture(sc,pc,jpgcall);獲取圖片... * 7.結束預覽釋放資源... * cma.stopPreView(); * cma.release();釋放資源.. * 8.在AndroidManifest設置權限... * <uses-feature android:name="android.hardware.camera" /> * <uses-feature android:name="android.hardware.camera.autofocus"/> * <uses-permission android:name="android.permission.CAMERA"/> * <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> * <uses-permission android:name="android.permission.WRITE_EXTENAL_STORAGE"/> * */ import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import android.os.Bundle; import android.os.Environment; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.view.Display; import android.view.Menu; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.view.WindowManager; import android.widget.Toast; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Picture; import android.graphics.PixelFormat; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.Parameters; import android.hardware.Camera.PictureCallback; import android.hardware.Camera.ShutterCallback; import android.hardware.Camera.Size; public class MainActivity extends Activity implements View.OnClickListener { private boolean previewrunning=true; private Camera camera=null; private SurfaceView suf; private SurfaceHolder sufh; @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏顯示... super.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//高亮顯示... setContentView(R.layout.activity_main); suf=(SurfaceView) findViewById(R.id.surface); sufh=suf.getHolder();//獲取SurfaceHolder... sufh.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設置一個緩沖區...就是一個緩沖的Surface...這個Surface的數據來源於Camera.. sufh.setFixedSize(480, 800);//設置分辨率為480*800 findViewById(R.id.but).setOnClickListener(this); sufh.addCallback(new SurfaceHolder.Callback() {//這里就很清晰了,在使用到了SurfaceView時必然要獲取SurfaceHolder接口,然后進行回調.. @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub if(MainActivity.this.camera!=null){ if(MainActivity.this.previewrunning){ MainActivity.this.camera.stopPreview(); MainActivity.this.previewrunning=false; } MainActivity.this.camera.release(); } } @SuppressLint("NewApi") @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub if(Camera.getNumberOfCameras()==2){//獲取相機... camera=Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK); }else{ camera=Camera.open(0); } camera.setDisplayOrientation(90);//這個就是設置屏幕需要旋轉90度,一般沒這句話屏幕內的東西都是縱向的... WindowManager manager=(WindowManager) MainActivity.this.getSystemService(Context.WINDOW_SERVICE);//獲取窗口服務.. Display display=manager.getDefaultDisplay();//獲取display對象.. Parameters param=MainActivity.this.camera.getParameters();//獲取參數 param.setPreviewSize(display.getWidth(), display.getHeight());//設置預覽時圖片的大小.. param.setPictureSize(display.getWidth(), display.getHeight());//設置拍照后圖片的大小.. param.setPreviewFrameRate(5);//設置預覽的時候以每秒五幀進行顯示... param.setPictureFormat(PixelFormat.JPEG);//設置圖片的格式為JPEG... param.set("jpeg-quality", 80);//設置圖片的質量... // List<Size> listsize=param.getSupportedPreviewSizes(); // System.out.println(listsize.size()); // if(null!=listsize && 0<listsize.size()){ // int height[]=new int[listsize.size()]; // Map<Integer,Integer>map=new HashMap<Integer,Integer>(); // for(int i=0;i<listsize.size();i++){ // Size size=(Size)listsize.get(i); // int sizeheight=size.height; // int sizewidth=size.width; // height[i]=sizeheight; // map.put(sizeheight, sizewidth); // } //Arrays.sort(height); // } // for(int j=0;j<listsize.size();j++){ // System.out.println(listsize.get(j).height+" "+listsize.get(j).width); // } /* * 下面就是進行參數的傳遞,但是出現了一個極大的問題...在我的手機終端上,這句話無法實現...一直會報fail setParamters.. * 這個的原因我也查到了很多.. * 一種就是我們的相機沒有自動對焦功能..這個一般是不太會出現的... * 還有一個原因就是我們設置: * param.setPreviewSize(display.getWidth(), display.getHeight()); * param.setPictureSize(display.getWidth(), display.getHeight());這兩個方法傳遞的參數不一樣,導致預覽圖片時的大小 * 和拍照后的圖片大小不相同導致的... * 還有就是由於手機型號的不同導致我們設置的預覽時的分辨率大小和手機的所支持的分辨率大小不匹配導致的... * 這是以上出現fail setParamters的三種原因..但是這三種情況都沒有解決我的手機出現的問題...因此我把setParameters()這句話 * 給去掉了... * 如果我們不清楚自己手機所支持的分辨率,那么我們就可以按照上面注釋的方法..定義一個List進行動態查找,把所有支持的分辨率 * 全部都找出來,最后篩選出一個合適的去設置... * */ // camera.setParameters(param); try { MainActivity.this.camera.setPreviewDisplay(MainActivity.this.sufh);//設置我們預覽時的SurfaceHolder... } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } MainActivity.this.camera.startPreview();//開始預覽... MainActivity.this.previewrunning=true; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub if(MainActivity.this.camera!=null){ MainActivity.this.camera.autoFocus(new Camera.AutoFocusCallback() {//這里就是觸發按鈕來完成事件..這里是自動聚焦函數..內部需要實現三種方法.. private PictureCallback raw =new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub //把圖片放入sd卡... } }; private PictureCallback jpeg=new PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { // TODO Auto-generated method stub //獲取到圖片信息的最原始數據,可以對這些原始數據進行相應操作... Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);//把圖片轉化成字節數據... //下面獲取sd卡的根目錄,設置我們需要保存的路徑... String filename=Environment.getExternalStorageState().toString()+File.separator+"CameraPhoto"+File.separator+"picture"+".jpg"; File file=new File(filename); if(!file.getParentFile().exists()){//如果父文件夾不存在則進行新建... file.getParentFile().mkdirs(); } try { BufferedOutputStream buf=new BufferedOutputStream(new FileOutputStream(file));//以緩沖流的方式將圖片的數據進行寫入.. buf.flush(); buf.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } Toast.makeText(MainActivity.this, "已保存", Toast.LENGTH_SHORT).show(); camera.stopPreview(); camera.startPreview(); } }; private ShutterCallback shutter=new ShutterCallback() { @Override public void onShutter() { // TODO Auto-generated method stub //在按下快門時進行調用.. } }; @Override public void onAutoFocus(boolean success, Camera camera) { // TODO Auto-generated method stub if(success){ MainActivity.this.camera.takePicture(shutter, raw, jpeg);//takepicture()方法需要有三個參數...這三個參數就是上面定義的三個方法.. } } }); } } }
最后我們需要獲取一些權限,需要在AndroidManifest.xml文件內部進行權限設置...
<!--下面表示獲取相機權限,獲取自動聚焦權限,允許拍照權限,sd卡文件的寫入和刪除權限,配寫sd卡的權限...--> <uses-feature android:name="android.hardware.camera"/> <uses-feature android:name="android.hardware.camera.autofocus"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
這樣通過以上的方式,就實現了簡單照相機的拍照功能...
2.Android如何實現音頻與視頻的錄制...
Android的實現音頻和視頻的錄制是非常的簡單的...只需要幾個步驟就可以完成了...這里我們需要使用到android.media.MediaRecorder這個類,同過實例化對象,我們就可以對視頻和音頻進行錄制了...通過內部提供的一些方法,我們就可以自定義一些屬性...
1.首先初始化對象...MediaRecorder re=new MediaRecorder();
2.設置視頻和音頻的文件來源...re.setAudioSource(MediaRecorder.AudioSource.MIC);
3.設置輸出的文本格式...re.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
4.設置音頻的編碼..AudioEncoder.DEFAULT和AudioEncoder.AMR_NB兩種格式...re.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
5.設置文件的存儲路徑...re.setOutputFile(PATH_NAME);
6.准備錄制...re.prepare();
7.開始錄制...re.start();
8.在AndroidManifest.xml文件中進行相應的配置...這就是實現音頻錄制的過程....來一個簡單的實現代碼...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/start" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="開始錄音"/> <Button android:id="@+id/stop" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="停止錄音"/> <TextView android:id="@+id/msg" android:layout_height="wrap_content" android:layout_width="wrap_content"/> </LinearLayout>
配置xml的權限設置...
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
最后就是java內部如何進行實現...
package com.example.recoder; /* 如何實現視頻和音頻的錄制 * 音頻 * 1.首先初始化對象...MediaRecorder re=new MediaRecorder(); * 2.設置視頻和音頻的文件來源...re.setAudioSource(MediaRecorder.AudioSource.MIC); * 3.設置輸出的文本格式...re.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); * 4.設置音頻的編碼..AudioEncoder.DEFAULT和AudioEncoder.AMR_NB兩種格式...re.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); * 5.設置文件的存儲路徑...re.setOutputFile(PATH_NAME); * 6.准備錄制...re.prepare(); * 7.開始錄制...re.start(); * 8.在AndroidManifest.xml文件中進行相應的配置.. * * */ import java.io.File; import java.io.IOException; import android.os.Bundle; import android.os.Environment; import android.app.Activity; import android.view.Menu; import android.view.View; import android.widget.TextView; import android.widget.Toast; import android.media.MediaRecorder; public class MainActivity extends Activity implements View.OnClickListener { private TextView tv; private MediaRecorder mr; private File soundFile; private File MediaFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv=(TextView) findViewById(R.id.msg); findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.stop).setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()){ case R.id.start:{ tv.setText("---開始錄音---"); //判斷sd卡的狀態信息.. if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){//sd卡沒有正常掛載... Toast.makeText(MainActivity.this, "請插入sd卡", Toast.LENGTH_LONG).show();//在屏幕上顯示信息... return; } mr=new MediaRecorder(); //獲取sd卡的根目錄... MediaFile=Environment.getExternalStorageDirectory(); try { soundFile=File.createTempFile("exam_Recorder", ".arm", MediaFile);//新建一個文件,前兩個參數為前綴名和后綴名,這里自己定義,第三個參表示的是文件的目錄... mr.setAudioSource(MediaRecorder.AudioSource.MIC);//設置錄制的音頻來源為手機麥克風 mr.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//設置輸出格式,就是以什么格式進行保存... mr.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//設置音頻的編碼為什么形式... mr.setOutputFile(soundFile.getAbsolutePath());//設置錄音的輸出文件路徑...保存的位置... mr.prepare(); mr.start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; } case R.id.stop:{ tv.setText("---停止錄音---"); if(soundFile!=null){ mr.stop(); mr.release(); mr=null; } } default: break; } } @Override public void onDestroy(){ if(soundFile!=null&&soundFile.exists()&&mr!=null){ mr.stop(); mr.release(); mr=null; } super.onDestroy(); } }
3.視頻的錄制...
視頻的錄制,那么必然就涉及到攝像頭了,想到攝像頭,必然我們要使用SurfaceView類了...這是必然的,因為我們需要預覽...錄制視頻的過程就是屬於一直對畫面進行預覽,然后一幀一幀的進行記錄...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <SurfaceView android:id="@+id/videoView" android:layout_height="240px" android:layout_width="320px" android:visibility="visible"/> <RelativeLayout android:layout_height="wrap_content" android:layout_width="fill_parent"> <Button android:id="@+id/start" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text="開始錄制"/> <Button android:id="@+id/stop" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_toRightOf="@id/start" android:text="停止錄制"/> </RelativeLayout> <TextView android:id="@+id/msg" android:layout_height="wrap_content" android:layout_width="wrap_content" android:text=""/> </LinearLayout>
同樣我們需要在xml文件中配置相應的權限...設置好了權限,我們才可以進行相應的操作...
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
然后在java文件中進行實現...
package com.example.video; import java.io.File; import java.io.IOException; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.app.Activity; import android.view.Menu; import android.view.SurfaceView; import android.view.SurfaceHolder; import android.view.View; import android.widget.TextView; public class MainActivity extends Activity implements View.OnClickListener { private TextView tv; private File myvideofile; private File dir; private MediaRecorder recorder; private SurfaceView suf; private SurfaceHolder holder; @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.start).setOnClickListener(this); findViewById(R.id.stop).setOnClickListener(this); tv=(TextView) findViewById(R.id.msg); suf=(SurfaceView) findViewById(R.id.videoView);//獲取SurfaceView... holder=suf.getHolder();//獲取SurfaceHolder接口... holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//設置一個緩沖... recorder=new MediaRecorder();//定義一個MediaRecorder對象... File Dir=Environment.getExternalStorageDirectory();//獲取sd卡的根目錄... String path=Dir.getAbsolutePath()+File.separator+"MyVideo"+File.separator;//設置文件路徑...這里的File.separator表示的是文件之間的分隔符..Windows下為'\'... dir=new File(path); if(!dir.exists()){//父文件夾不存在則進行新建... dir.mkdir(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.start: tv.setText("開始錄制"); try { myvideofile=File.createTempFile("video", ".3gp", dir); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } recorder.setPreviewDisplay(holder.getSurface());//預覽時的SrufaceView recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//設置視頻的來源...來源於相機的拍攝 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);//音頻的來源於手機的麥克... recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//輸出的格式... recorder.setVideoSize(800, 480);//設置預覽時的分辨率.... recorder.setVideoFrameRate(15);//每秒的幀數... recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//設置音頻的編碼... recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);//設置視頻的編碼... recorder.setMaxDuration(1000);//設置最大的期限... recorder.setOutputFile(myvideofile.getAbsolutePath());//獲取文件的輸出路徑...進行保存... try { recorder.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } recorder.start(); break; case R.id.stop: tv.setText("停止錄制"); recorder.stop(); recorder.reset(); recorder.release(); recorder=null; } } }
這樣就實現了視頻的錄制...非常的簡單.....