我们了解了视频相关的基础知识,后面的文章我们要能够和音频一样可以采集我们的视频,视频是一帧一帧的图片来的,我们首先要学习预览视频,然后采集一帧图片,采集视频从简到难的来了解这个问题。首先第一个反应打开Google搜索和Android视频采集相关的东西,我们要知道如何通过API来采集,不由自主地到了Android官网的Camera API。Android有两个视频采集的API,Camera是Android 5.0以前使用的,现在已经废弃了,我们还是得学一下他的使用,Camera2是最新的视频采集API,我们重点了解它的使用。这篇文章我们掌握调用系统的拍照和录制视频API来实现拍照录像功能。
Camera
它是API21(Android5.0)以前用来对摄像头数据采集的的API,我们从开始到每个环节的关键内容记录如下。
基础知识
先来了解使用Camera有几个相关联的类。
Camera:API21以后老的API控制摄像头设备
SurfaceView:显示摄像头预览图像给用户
MediaRecorder:录制摄像头的视频
权限声明
摄像头权限:我们要使用Camera设备必须要声明一个权限
1
|
<uses-permission android:name= "android.permission.CAMERA" /> |
但是当我们使用Intent来调用系统自己的Camera设备拍照录像就不需要这个权限。
摄像头特征:应用必须声明使用摄像头特性权限(这个不知道是啥意思的要了解uses-feature这个清单文件的意义)
1
|
<uses-feature android:name= "android.hardware.camera" /> |
音频录制权限:当录制视频的时候我们还要音频就要加上这个权限。
1
|
<uses-permission android:name= "android.permission.RECORD_AUDIO" /> |
存储权限:如果我们要保存相片和视频在存储设备那么就要加上这个权限。
1
|
<uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" /> |
定位权限:如果照片的标签要GPS位置信息,我们就要如下权限
1
2
3
4
|
<uses-permission android:name= "android.permission.ACCESS_FINE_LOCATION" /> ... <!-- Needed only if your app targets Android 5.0 (API level 21 ) or higher. --> <uses-feature android:name= "android.hardware.location.gps" /> |
调用系统的摄像头app来拍照和录制视频
拍照
请求摄像头特征
1
2
3
4
5
|
<manifest ... > <uses-feature android:name= "android.hardware.camera" android:required= "true" /> ... </manifest> |
这个权限可以让GooglePlay来判断是否设备支持下载我们的应用,如果设置required为true那么一定要有摄像头硬件设备的才能下载,如果设置required为false,那么没有摄像头硬件设备的也可以下载,当然我们在程序里面就要判断一下是否有摄像头可用了。
使用默认Intent开始拍照
调用默认的开启系统拍照App的Intent
1
2
3
4
5
6
7
8
|
static final int REQUEST_IMAGE_CAPTURE = 1 ; private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null ) { startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); } } |
获取拍照图片
我们刚才通过startActivityForResult来拍照了,很自然的在onActivityResult来接受返回的数据,我们把图片显示在一个ImageView上面
1
2
3
4
5
6
7
8
|
@Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { Bundle extras = data.getExtras(); Bitmap imageBitmap = (Bitmap) extras.get( "data" ); mImageView.setImageBitmap(imageBitmap); } } |
通过这种默认的拍照我们不需要在Android6.0以上的机器声明任何权限就可以成功执行。
自定义保存相片图片路径
我们上面的操作,获取来的是一个bitmap,我们的图片信息都是在内存里面操作的,如果我们要保存拍照的图片到存储卡并且查看图片,那么我们只要声明一个写存储卡权限就OK。
1
2
3
4
|
<manifest ...> <uses-permission android:name= "android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest> |
开始重新请求拍照代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
static final int REQUEST_TAKE_PHOTO = 1 ; private void dispatchTakePictureIntent() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // Ensure that there's a camera activity to handle the intent if (takePictureIntent.resolveActivity(getPackageManager()) != null ) { // Create the File where the photo should go File photoFile = null ; try { photoFile = createImageFile(); } catch (IOException ex) { // Error occurred while creating the File ... } // Continue only if the File was successfully created if (photoFile != null ) { Uri photoURI = FileProvider.getUriForFile( this , "com.example.android.fileprovider" , photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } String mCurrentPhotoPath; private File createImageFile() throws IOException { // Create an image file name String timeStamp = new SimpleDateFormat( "yyyyMMdd_HHmmss" ).format( new Date()); String imageFileName = "JPEG_" + timeStamp + "_" ; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents mCurrentPhotoPath = image.getAbsolutePath(); return image; } |
上面的代码我们使用了FileProvider.getUriForFile方法,它返回content://URI,这个API在Android7.0以上使用不做处理会抛出FileUriExposedException。我们要在清单文件注册配置一个FileProvider
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<application> ... <provider android:name= "android.support.v4.content.FileProvider" android:authorities= "com.example.android.fileprovider" android:exported= "false" android:grantUriPermissions= "true" > <meta-data android:name= "android.support.FILE_PROVIDER_PATHS" android:resource= "@xml/file_paths" ></meta-data> </provider> ... </application> |
在res/xml/file_paths配置文件
1
2
3
4
|
<? xml version = "1.0" encoding = "utf-8" ?> < external-path name = "my_images" path = "Android/data/com.example.package.name/files/Pictures" /> </ paths > |
添加照片到相册
我们上面的照片保存位置根目录为getExternalFilesDir(Environment.DIRECTORY_PICTURES);这个目录下面多媒体扫描器是不能找到我们的照片的,因为它是我们App私有的。下面的代码可以让系统的多媒体扫描器添加我们的图片到Media Provider’s 数据库,让我们的图片对系统相册和其他应用都可以使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
private void galleryAddPic() { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); File f = new File(mCurrentPhotoPath); try { MediaStore.Images.Media.insertImage(getContentResolver(), f.getAbsolutePath(), f.getName(), null ); Log.d(TAG, "galleryAddPic: add to Media Scanner success" ); } catch (FileNotFoundException e) { e.printStackTrace(); Log.e(TAG, "galleryAddPic: add to Media Scanner failed" ); } Uri contentUri = Uri.fromFile(f); mediaScanIntent.setData(contentUri); this .sendBroadcast(mediaScanIntent); Toast.makeText( this , "Add to Gallery success" , Toast.LENGTH_SHORT).show(); } |
解码缩放图片
我们在把图片ImageView上面,没有做任何处理,如果图片较大,会导致oom的,做一个缩放处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
private void setPic() { // Get the dimensions of the View int targetW = mImageView.getWidth(); int targetH = mImageView.getHeight(); // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true ; BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false ; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true ; Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); mImageView.setImageBitmap(bitmap); } |
录像
录制视频播放的代码很简单,如果要对视频播放器进行定制,那么久要多一些东西,我们现在只简单的可以播放调用系统录制的视频。
开启视频录制Intent
1
2
3
4
5
6
7
8
|
static final int REQUEST_VIDEO_CAPTURE = 1 ; private void dispatchTakeVideoIntent() { Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); if (takeVideoIntent.resolveActivity(getPackageManager()) != null ) { startActivityForResult(takeVideoIntent, REQUEST_VIDEO_CAPTURE); } } |
在onActivityResult里面接收视频Uri来播放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Override protected void onActivityResult( int requestCode, int resultCode, Intent intent) { if (requestCode == REQUEST_VIDEO_CAPTURE && resultCode == RESULT_OK) { Uri videoUri = intent.getData(); Log.d(TAG, "onActivityResult: " + videoUri); mVideoView.setVideoURI(videoUri); mVideoView.requestFocus(); mVideoView.setOnPreparedListener( new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.setLooping( false ); //设置视频重复播放 } }); mVideoView.start(); //播放视频 MediaController mediaController = new MediaController( this ); //显示控制条 mVideoView.setMediaController(mediaController); mediaController.setMediaPlayer(mVideoView); //设置控制的对象 mediaController.show(); } } |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
转自:https://www.jb51.net/article/153005.htm