preface
In the development of Android audio and video, the online knowledge points are too fragmented and it is very difficult to learn by yourself. However, Jhuster, a big audio and video player, put forward "Android audio and video from getting started to improving - task list". This article is one of the Android audio and video task lists. The corresponding content to learn is: Video Acquisition - system API foundation
Audio and video task list
Audio and video task list: Click here to jump to view.
catalogue
(1) Introduction to camera system API
(1.1) take pictures with the default Intent
Start camera with 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 the intent if (intent.resolveActivity(getPackageManager()) != null) { startActivityForResult(intent, TAKE_PHOTO_BY_INTENT); } } });
Just now, we used startActivityForResult() to start the activity, so after taking the picture, the result will be returned to the onActivityResult() method. If the photo is taken successfully, the picture 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; } }
The built-in Camera is a built-in system application that basically comes with every smartphone. It internally provides an intention filter:
<intent-filter> <action android:name="android.media.action.IMAGE_CAPTURE"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter>
Therefore, the system camera can be called by constructing the following intention
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE")
We can also use the constant action of the MediaStore class_ IMAGE_ Capture to call
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
When running this example, the image displayed is very small, because Camera returns a small thumbnail considering the small memory of the mobile device
(1.2) save pictures to cache directory
After taking a picture, save the picture to the cache directory
mTakePhotoToCache.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // Create a File object to store the pictures 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 camera program Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); // Make sure there is a camera activity to handle the intent if (intent.resolveActivity(getPackageManager()) != null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);// Save the taken photos to the specified URI startActivityForResult(intent, TAKE_PHOTO_TO_CACHE); } } } });
The onActivityResult() method displays the taken photos
case TAKE_PHOTO_TO_CACHE: if (resultCode == RESULT_OK) { try { // Display the taken photos Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mImageUri)); mPicture.setImageBitmap(bitmap); } catch (Exception e) { e.printStackTrace(); } } break;
- (1) Create a File object to store the pictures taken by the camera, and name the pictures output_ image.jpg and store it in the application associated cache directory of the mobile phone SD card.
What is called the Application Association cache directory? It refers to the location in the SD card where the current application cache data is stored. Call getExternalCacheDir() method to get this directory. The specific path is / sdcard/Android/data//cache.
So why use the application associated directory to store pictures? Because from Android 6 0 at the beginning of the system, reading and writing SD card is listed as dangerous permission. If pictures are stored in any other directory of SD card, they must be processed with runtime permission, and this step can be skipped by using application associated directory.
- (2) Then a judgment will be made if the system version of the running device is lower than Android 7 0, call the fromFile() method of Uri to convert the File object into a Uri object, which identifies output_image.jpg the local real path of this picture. Otherwise, the getUriForFUe() method of FileProvider is called to convert the File object into an encapsulated Uri object.
The getUriForFile() method receives three parameters
(1) The first parameter requires a Context object to be passed.
(2) The second parameter can be any unique string.
(3) The third parameter is the File object we just created.
The reason for this layer conversion is that starting from Android 7.0 system, it is considered unsafe to directly use the Uri of the local real path, and a FileUriExposedException will be thrown. FileProvider is a special content provider. It uses a mechanism similar to the content provider to protect data. It can selectively share the encapsulated Uri to the outside, so as to improve the security of the application.
-
(3) An intent object is constructed, and the action of intent is specified as Android media. action. IMAGE_ CAPTURE, then call the putExtra() method of Intent to specify the output address of the picture, fill in the Uri object just now, and finally call startActivityForResult() to start the activity. Since we use an implicit intent, the system will find out the activities that can respond to the intent to start, so that the camera program will be opened and the photos taken will be output to output_image.jpg. If the photo is taken successfully, the picture can be displayed in onActivityResult. Call the decodeStream() method of BitmapFactory to output_image.jpg parses this photo into a Bitmap object, and then sets it to be displayed in ImageView.
-
(4) Because the content provider is used, it should be in Android manifest Register the content provider in 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>
The value of the android:name attribute is fixed.
The value of android:authorities property must be the same as fileprovider The second parameter in the geturiforfile () method is consistent.
android:exported indicates whether other applications can call the current component.
android:grantUriPermissions I haven't fully understood this attribute yet. I'll add it later when I fully understand it
Inside the provider tag, the meta data tag is used to specify the sharing path of the Uri, and a @ XML / fit is referenced_ Paths resource.
Of course, this resource still doesn't exist. Let's create it. Right click res Directory > New - > directory to create an xml directory, then right click xml Directory > New - > file to create a file_paths.xml file. Then modify the file_ paths. The contents in the xml file are 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>
Among them, external path is used to specify Uri sharing. The value of name attribute can be filled in arbitrarily, and the value of path attribute represents the specific path of sharing. Setting a null value here means sharing the entire SD card. Of course, you can only share the output we store_ image. Jpg path to this picture
- (5) In AndroidManifestxml, declare the following permissions to access the SD card:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
(1.3) save the picture in the custom path
After taking a picture, save the picture in 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 the intent if (intent.resolveActivity(getPackageManager()) != null) { intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); startActivityForResult(intent, TAKE_PHOTO_TO_CUSTOMPATH); } } } });
The difference from (1.2) saving pictures in the associated cache directory is that this custom path is similar to other contents, which will not be explained here.
private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = Environment.getExternalStorageDirectory();// Get SDCard directory File image = File.createTempFile(imageFileName, ".jpg", storageDir); mCurrentPhotoPath = image.getAbsolutePath(); return image; }
The onActivityResult() method displays the taken photos
case TAKE_PHOTO_TO_CUSTOMPATH: if (resultCode == RESULT_OK) { try { // Display the taken photos // The URIs here are obtained in different ways, so there is no need for Android 7.0 when cutting 0 judgment mPicture.setImageURI(mImageUri); } catch (Exception e) { e.printStackTrace(); } } break;
Another method used for the photos shown here is setImageURI()
(1.4) save the picture to the custom path and add it to the album
After taking a picture, the picture is saved to a custom path and added to the album
The code for taking pictures is similar to (1.3) saving pictures in a user-defined path. No code will be posted here
The onActivityResult() method displays the taken photos
case TAKE_PHOTO_TO_ALBUM: if (resultCode == RESULT_OK) { try { // Display the taken photos mPicture.setImageURI(mImageUri); // Save the photos into the album, which can identify the photos taken by the program addPictureToAlbum(); } catch (Exception e) { e.printStackTrace(); } } break;
Add to album
private void addPictureToAlbum() { 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(); }
The multimedia scanner cannot find our photos because it is private to our App. The following code allows the system's multimedia scanner to add our pictures to the Media Provider database, so that our pictures can be used for system albums and other applications.
(1.5) zoom the picture
Please refer to the previous photo code for the photo taking process
The captured image is placed on the ImageView without any processing. If the image is large, it will cause oom. Do a zoom processing
private void scalePicture() { // Gets the size of the view control int targetW = mPictureScale.getWidth(); int targetH = mPictureScale.getHeight(); // Gets the size of the picture BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determines how much the image is reduced int scaleFactor = Math.min(photoW / targetW, photoH / targetH); // Zoom the picture 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); }
(1.6) select pictures from the album and display them
Open Album
mChooseFromAlbum.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { openAlbum(); } });
private void openAlbum() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent, CHOOSE_PHOTO); // Open Album }
The obtained photo is displayed in the onActivityResult() method
case CHOOSE_PHOTO: if (resultCode == RESULT_OK) { // Judge the mobile phone system version number if (Build.VERSION.SDK_INT >= 19) { // 4.4 and above systems use this method to process pictures handleImageOnKitKat(data); } else { // 4.4 the following systems use this method to process images handleImageBeforeKitKat(data); } } break;
@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 document type, it is processed by document id String docId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; // Resolve the id in digital 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 file type, you can directly obtain the image path imagePath = uri.getPath(); } displayImage(imagePath); // Display pictures according to picture 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; // Get the real image path through Uri and selection 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(); } }
Call the openAlbum () method. Here, we first build an intent object and specify its action as Android intent. action. GET_ CONTENT. Then set some necessary parameters for the Intent object, and then call the startActivityForResult () method to open the album program and select the photo. Note that when we call the startactivityforresult () method, the value we pass in for the second parameter becomes CHOOSE_PHOTO, so that when you return to the onActivityResult () method after selecting pictures from the album, you will enter CHOOSE_PHOTO case to process pictures. The following logic is more complicated. First, in order to be compatible with new and old versions of mobile phones, we made a judgment. If the mobile phone is a 4.4 or above system, we call the handlelmageOnKitKat method to process it. Otherwise, we call the handlelmageBeforeKitKat() method to process the picture. The reason for this is that the Android system starts from version 4.4. Selecting the pictures in the album will no longer return the real Uri of the pictures, but an encapsulated Uri. Therefore, if it is a mobile phone above version 4.4, it needs to parse this Uri.
Then the logic in the handlelmageOnKitKat () method is basically how to parse the encapsulated Uri. There are several judgment conditions. If the returned Uri is of document type, take out the document id for processing. If not, use the normal method. In addition, if the authority of Uri is in media format, the document id needs to be parsed again. The second half needs to be extracted by string segmentation to get the real digital id. The extracted id is used to build a new Uri and condition statement, and then pass these values as parameters to the getlmagePath () method to obtain the real path of the image. After getting the path of the picture, call the displayimage0 method to display the picture on the interface.
Compared with the handlelmageOnKitKat() method, the logic in the handlelmageBeforeKitKat() method is much simpler, because its Uri is not encapsulated and does not need any parsing. You can get the real path of the image by directly passing the Uri to the getlmagePath() method. Finally, you also call the displaylmage() method to display the image on the interface.
(2) Recording video API introduction
(2.1) record video and display
Build an Intent object and specify its action as mediastore ACTION_ VIDEO_ CAPTURE
Then you can start recording video by calling the startActivityForResult () method.
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); } } });
Play 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 video replay } }); mVideoView.start();// Play video MediaController mediaController = new MediaController(this);// Display control bar mVideoView.setMediaController(mediaController); mediaController.setMediaPlayer(mVideoView);// Set controlled objects mediaController.show(); } break; default: break; } }
(3) Source code address
Fundamentals of Android audio and video development (VII): Video Acquisition - system API Foundation