preface
In Android audio and video development, online knowledge is too fragmented, self-learning is very difficult, but audio and video cattle Jhuster put forward “Android audio and video from the entry to improve – task list”. This article is one of the Android audio and video tasks list, corresponding to the content to learn is: video capture – system API foundation
Audio and video task list
Audio and video task list: Click here to jump to view.
directory
(1) Introduction to camera system API
(1.1) Use the default Intent to take photos
Launch the camera with the default Intent
mTakePhotoByIntent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
// Make sure there is a camera activity to handle intents
if(intent.resolveActivity(getPackageManager()) ! =null) { startActivityForResult(intent, TAKE_PHOTO_BY_INTENT); }}});Copy the code
We used startActivityForResult() to start the activity, so the result will be returned to the onActivityResult() method after the photo is taken. If the photo was taken successfully, the image can be displayed in onActivityResult.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case TAKE_PHOTO_BY_INTENT:
if (resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mPicture.setImageBitmap(imageBitmap);
}
break; }}Copy the code
The built-in Camera is basically a built-in system application of every smart phone, which provides an intention filter inside:
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
Copy the code
Therefore, the system camera can be called by constructing the following intents
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE")
Copy the code
It can also be called through the constant ACTION_IMAGE_CAPTURE of the MediaStore class
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Copy the code
When running this example, the image is small, and the Camera returns a small thumbnail because of the small memory of the mobile device
(1.2) Save the image to the cache directory
After the photo is taken, the image is saved to the cache directory
mTakePhotoToCache.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Create a File object to store the image after the photo is taken
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
if(outputImage ! =null) {
if (Build.VERSION.SDK_INT < 24) {
mImageUri = Uri.fromFile(outputImage);
} else {
mImageUri = FileProvider.getUriForFile(TakePhotoActivity.this."com.lzacking.camerabasedemo.fileprovider",
outputImage);
}
// Start the camera program
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
// Make sure there is a camera activity to handle intents
if(intent.resolveActivity(getPackageManager()) ! =null) {
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);// Save the photo to the specified URIstartActivityForResult(intent, TAKE_PHOTO_TO_CACHE); }}}});Copy the code
The onActivityResult() method displays the photo taken
case TAKE_PHOTO_TO_CACHE:
if (resultCode == RESULT_OK) {
try {
// Display the photos taken
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mImageUri));
mPicture.setImageBitmap(bitmap);
} catch(Exception e) { e.printStackTrace(); }}break;
Copy the code
- (1) Create a File object to store the pictures taken by the camera, name the picture output_ image.jpg, and store it in the application associated cache directory of the SD card of the mobile phone.
What is the application associated cache directory? Is specially used to store the current application of cache data in SD card, call getExternalCacheDir () method can get the directory, the specific path is/sdcard/Android/data/cache /.
So why use the app associated cache directory to store images? Since Android6.0, read and write SD card is listed as dangerous permissions, if the picture stored in any other SD card directory, have to run time permission processing, and the use of application associated directory can skip this step.
- The fromFile() method of Uri is called to convert the File object into a Uri object that identifies the local real path of the image output_image.jpg. Otherwise, the FileProvider’s getUriForFUe() method is called to convert the File object into an encapsulated Uri object.
The getUriForFile() method takes three arguments
(1) The first parameter requires passing to the Context object. (2) The second argument can be any unique string. (3) The third argument is the File object we just created. The reason for this layer conversion is that starting with Android 7.0, using a Uri directly from the local real path is considered unsafe and throws a FileUriExposedException. FileProvider is a special content provider. It uses a similar mechanism to content providers to protect data and selectively share encapsulated URIs to the outside world, thus improving application security.
-
Http://image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media.action.image_capture = android.media. Finally, call startActivityForResult() to start the activity. Since we are using an implicit Intent, the system will find an activity to launch in response to this Intent, so the camera program will be opened and the photo taken will be output to output_image.jpg. If the photo was taken successfully, the image can be displayed in onActivityResult. Call BitmapFactory’s decodeStream() method to parse the output_image.jpg image into a Bitmap object and set it to display in the ImageView.
-
(4) Because the content provider is used, you need to register the content provider in androidmanifest.xml as follows:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".TakeVideoActivity"></activity>
<activity android:name=".TakePhotoActivity" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.lzacking.camerabasedemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
Copy the code
The value of the Android: Name attribute is fixed.
Android: authorities attribute’s value must be and just FileProvider getUriForFile () method of the second parameter. Android: Exported indicates whether other applications are supported to call the current component. Android: grantUriPermissions this property at present I have not yet fully understood, such as fully understood, after adding the provider of the label for internal use meta – data sharing of tag specifies the Uri path, and cites a @ XML/fite_paths resources.
Of course, this resource still doesn’t exist, so let’s create it. Right click res Directory 1 >New — >Directory to create an XML Directory, then right click XML Directory 1 >New — >File to create a file_paths.xml File. Then modify the contents of the file_paths.xml file as follows:
<? xml version="l.0" encoding="utf-8"? > <paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="" />
</paths>
Copy the code
External-path is used to specify the shared Uri. The value of the name attribute is optional, and the value of the path attribute represents the specific path of the shared Uri. Set this to null to share the entire SD card, or you can just share the path where we saved output_image.jpg
- Access to the SD card is manifestXML.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Copy the code
(1.3) Save the picture in a custom path
After the photo is taken, save the image to a custom path
mTakePhotoToCustomPath.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
File outputImage = null;
try {
outputImage = createImageFile();
} catch (IOException e) {
e.printStackTrace();
}
if(outputImage ! =null) {
if (Build.VERSION.SDK_INT < 24) {
mImageUri = Uri.fromFile(outputImage);
} else {
mImageUri = FileProvider.getUriForFile(TakePhotoActivity.this."com.lzacking.camerabasedemo.fileprovider",
outputImage);
}
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Make sure there is a camera activity to handle intents
if(intent.resolveActivity(getPackageManager()) ! =null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); startActivityForResult(intent, TAKE_PHOTO_TO_CUSTOMPATH); }}}});Copy the code
The difference between (1.2) saving images in the associated cache directory is this custom path, and other similar content is not explained here.
private File createImageFile(a) throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = Environment.getExternalStorageDirectory();// Get the SDCard directory
File image = File.createTempFile(imageFileName, ".jpg", storageDir);
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
Copy the code
The onActivityResult() method displays the photo taken
case TAKE_PHOTO_TO_CUSTOMPATH:
if (resultCode == RESULT_OK) {
try {
// Display the photos taken
// The URI here is obtained in a different way, so there is no need for Android7.0 judgment when tailoring it.
mPicture.setImageURI(mImageUri);
} catch(Exception e) { e.printStackTrace(); }}break;
Copy the code
This shows the photos taken using another method, which is setImageURI ()
(1.4) Save the picture to a custom path and add it to the album
After the photo is taken, the image is saved to the custom path and added to the album. This part of the code is similar to (1.3) to save the image in the custom path. Here, the code onActivityResult() method is no longer pasted to show the photo taken
case TAKE_PHOTO_TO_ALBUM:
if (resultCode == RESULT_OK) {
try {
// Display the photos taken
mPicture.setImageURI(mImageUri);
// Save the photo to an album, which recognizes the photo taken by the program
addPictureToAlbum();
} catch(Exception e) { e.printStackTrace(); }}break;
Copy the code
Add to album
private void addPictureToAlbum(a) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File file = new File(mCurrentPhotoPath);
try {
MediaStore.Images.Media.insertImage(getContentResolver(),
file.getAbsolutePath(), file.getName(), null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
Toast.makeText(this."Add to Album success", Toast.LENGTH_SHORT).show();
}
Copy the code
The multimedia scanner can’t find our photo, because it is private to our App. The following code allows the system’s multimedia scanner to add our images to the Media Provider database, making our images available to system albums and other applications.
(1.5) The picture is zoomed
In the process of taking photos, please refer to the previous photo code and put the pictures taken on ImageView without any processing. If the picture is too large, it will cause OOM, so do a zoom processing
private void scalePicture(a) {
// Gets the size of the view control
int targetW = mPictureScale.getWidth();
int targetH = mPictureScale.getHeight();
// Get the size of the image
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine the extent to which the image is zoomed out
int scaleFactor = Math.min(photoW / targetW, photoH / targetH);
// Scale the image and fill it into the view control
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
mPictureScale.setImageBitmap(bitmap);
}
Copy the code
(1.6) Select a picture from the album and display it
Open the photo album
mChooseFromAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { openAlbum(); }});Copy the code
private void openAlbum(a) {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO); // Open the album
}
Copy the code
The onActivityResult() method displays the captured photo
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK) {
// Check the phone system version
if (Build.VERSION.SDK_INT >= 19) {
// Systems 4.4 and above use this method to process images
handleImageOnKitKat(data);
} else {
// 4.4 The following systems use this method to process imageshandleImageBeforeKitKat(data); }}break;
Copy the code
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
Log.d("TAG"."handleImageOnKitKat: uri is " + uri);
if (DocumentsContract.isDocumentUri(this, uri)) {
// If it is a Uri of type document, the document ID is used
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":") [1]; // Parse out the id in numeric format
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri, null); }}else if ("content".equalsIgnoreCase(uri.getScheme())) {
// If it is a Uri of type Content, it is handled in the normal way
imagePath = getImagePath(uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
// If it is a Uri of type file, get the image path directly
imagePath = uri.getPath();
}
displayImage(imagePath); // Display the image according to the image path
}
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
displayImage(imagePath);
}
private String getImagePath(Uri uri, String selection) {
String path = null;
// Use Uri and selection to get the real image path
Cursor cursor = getContentResolver().query(uri, null, selection, null.null);
if(cursor ! =null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
private void displayImage(String imagePath) {
if(imagePath ! =null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
mPicture.setImageBitmap(bitmap);
} else {
Toast.makeText(this."failed to get image", Toast.LENGTH_SHORT).show(); }}Copy the code
Call openAlbum () method, here we first construct an Intent object, and the action specified for android. The Intent. The action. The GET_CONTENT. Then set the required parameters for the Intent object and call startActivityForResult () to open the album application and select the photo. Notice that when we call the StartActivityForResult () method, we pass the value of the second parameter to CHOOSE_PHOTO, so that when we return to the onActivityResult () method after selecting the image from the album, I’m going to enter CHOOSE_PHOTO’s case to process the image. The next logical step is more complicated. First, in order to be compatible with older and newer phones, we make a decision that if the phone is 4.4 or above, we call handlelmageOnKitKat (), otherwise we call handlelmageBeforeKitKat(). The reason for this is that starting with version 4.4, Android does not return the actual Uri of an image in an album. Instead, it returns an encapsulated Uri, so phones later than version 4.4 need to parse this Uri.
The logic in the handlelmageOnKitKat () method is basically how to parse the encapsulated Uri. There are several ways to determine the situation: if the returned Uri is of type Document, fetch the document ID and handle it; if not, handle it the normal way. In addition, if the Uri’s authority is in media format, the document ID needs to be parsed again, and the second half of the document ID needs to be extracted by string splitting to get the actual numeric ID. The extracted ID is used to construct a new Uri and condition statement, and these values are passed as arguments to the getlmagePath () method to get the actual path to the image. Once you get the path to the image, call the DisplayImage0 method to display the image on the screen.
Compared to the handlelmageOnKitKat () method, the logic in the handlelmageBeforeKitKat () method is much simpler because its Uri is unencapsulated and does not require any parsing, Pass the Uri directly to the getlmagePath () method to get the actual path of the image, and finally call the displayLmage () method to display the image on the screen.
(2) Video recording API introduction
(2.1) Record video and display
You build an Intent object, specify its action as mediastore.action_video_capture, and then call startActivityForResult () to start recording the video
mTakeVideo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if(intent.resolveActivity(getPackageManager()) ! =null) { startActivityForResult(intent, TAKE_VIDEO); }}});Copy the code
Play the recorded video
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
switch (requestCode) {
case TAKE_VIDEO:
if (resultCode == RESULT_OK) {
Uri videoUri = intent.getData();
mVideoView.setVideoURI(videoUri);
mVideoView.requestFocus();
mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.setLooping(true);// Set the video to repeat}}); mVideoView.start();// Play the video
MediaController mediaController = new MediaController(this);// Display the control bar
mVideoView.setMediaController(mediaController);
mediaController.setMediaPlayer(mVideoView);// Set the object to control
mediaController.show();
}
break;
default:
break; }}Copy the code
(3) source code address
Android audio and Video development foundation (7) : Video capture – system API foundation