Android 7.1 RK3288 A40i Camera2 拍照镜像分析
https://www.cnblogs.com/Free-Thinker/p/6394797.html Android入门——Bitmap和BitmapFactory
要求:Camera2 拍照 镜像
主要的想法: 是根据 拍照按钮来跟踪成像照片 最后镜像翻转
Rk3288 PhotoModule 模式
A40i CaptureModule 模式 ,改缩略图 改成像图
==================================================== RK3288 android7.1 ==================================================== K:\ZK-Rxxx_7.1_RK3288_Firmware\ZK_RXXX_RK3288_ANDROID7.1\packages\apps\Camera2\src\com\android\camera\PhotoModule.java 1.1.拍照按钮回调 onShutterButtonClick @Override public void onShutterButtonClick() { 1.2.找到拍照最后保存照片 saveFinalPhoto 由Matrix 创建矩阵,并水平平移 镜像 --- a/packages/apps/Camera2/src/com/android/camera/PhotoModule.java +++ b/packages/apps/Camera2/src/com/android/camera/PhotoModule.java @@ -103,7 +103,7 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Vector; - +import android.graphics.Matrix; public class PhotoModule extends CameraModule @@ -1644,6 +1644,9 @@ public class PhotoModule void saveFinalPhoto(final byte[] jpegData, NamedEntity name, final ExifInterface exif, CameraProxy camera) { + + android.util.Log.d("gatsby", "PhotoModule.saveFinalPhoto CameraCaputre"); + int orientation = Exif.getOrientation(exif); float zoomValue = 1.0f; @@ -1714,8 +1717,17 @@ public class PhotoModule exif.setTag(directionRefTag); exif.setTag(directionTag); } + Bitmap bitmap = CameraUtil.makeBitmap(jpegData, exifWidth*exifHeight); + Matrix m = new Matrix(); + m.postScale(-1, 1); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), m, true); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); + byte[] tm_jpegData; + tm_jpegData = baos.toByteArray(); + //gatsby getServices().getMediaSaver().addImage( - jpegData, title, date, mLocation, width, height, + tm_jpegData, title, date, mLocation, width, height, orientation, exif, mOnMediaSavedListener); } // Animate capture with real jpeg data instead of a preview
A40i android7.1
===================================================== 2021.01.15 ====================================================== 1.1. 拍照按钮 K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\CaptureModule.java @Override public void onShutterButtonClick() { if (mCamera == null) { return; } int countDownDuration = mSettingsManager .getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION); if (countDownDuration > 0) { // Start count down. mAppController.getCameraAppUI().transitionToCancel(); mAppController.getCameraAppUI().hideModeOptions(); mUI.setCountdownFinishedListener(this); mUI.startCountdown(countDownDuration); // Will take picture later via listener callback. } else { takePictureNow(); } } 1.2. 拍照 K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\CaptureModule.java private void takePictureNow() { if (mCamera == null) { Log.i(TAG, "Not taking picture since Camera is closed."); return; } android.util.Log.d("gatsby", "CaptureModule takePictureNow"); CaptureSession session = createAndStartCaptureSession(); int orientation = mAppController.getOrientationManager().getDeviceOrientation() .getDegrees(); // TODO: This should really not use getExternalCacheDir and instead use // the SessionStorage API. Need to sync with gcam if that's OK. PhotoCaptureParameters params = new PhotoCaptureParameters( session.getTitle(), orientation, session.getLocation(), mContext.getExternalCacheDir(), this, mPictureSaverCallback, mHeadingSensor.getCurrentHeading(), mZoomValue, 0); decorateSessionAtCaptureTime(session); //gatsby take a pircture mCamera.takePicture(params, session); } 1.3 K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\OneCamera.java /** * Call this to take a picture. * * @param params parameters for taking pictures. * @param session the capture session for this picture. */ public void takePicture(PhotoCaptureParameters params, CaptureSession session); 1.4. 定义PictureTaker接口 K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\v2\photo\PictureTaker.java public interface PictureTaker { /** * @See {@link OneCamera#takePicture} */ public void takePicture(OneCamera.PhotoCaptureParameters params, CaptureSession session); } 1.5. PictureTakerImpl类 实现 PictureTaker接口 重写了takePicture方法, K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\v2\photo\PictureTakerImpl.java class PictureTakerImpl implements PictureTaker { @Override public void takePicture(OneCamera.PhotoCaptureParameters params, final CaptureSession session) { android.util.Log.d("gatsby", "PictureTakerImpl takePicture"); OneCamera.PictureCallback pictureCallback = params.callback; // Wrap the pictureCallback with a thread-safe adapter which guarantees // that they are always invoked on the main thread. PictureCallbackAdapter pictureCallbackAdapter = new PictureCallbackAdapter(pictureCallback, mMainExecutor); final Updatable<Void> imageExposureCallback = pictureCallbackAdapter.provideQuickExposeUpdatable(); final ImageSaver imageSaver = mImageSaverBuilder.build( params.saverCallback, OrientationManager.DeviceOrientation.from(params.orientation), session); //Log future mCameraCommandExecutor.execute(new PictureTakerCommand( imageExposureCallback, imageSaver, session)); } 1.6. Executing command gatsby (13557): Executing command: PictureTakerCommand{command=com.android.camera.one.v2.photo.SimpleImageCaptureCommand@d754686} START K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\v2\commands\CameraCommandExecutor.java public Future<?> execute(CameraCommand command) { if (mClosed) { return Futures.immediateFuture(null); } synchronized (mLock) { if (mExecutor == null) { // Create a new executor, if necessary. mExecutor = mExecutorProvider.get(); } checkNotNull(mExecutor); android.util.Log.d("gatsby", "CameraCommandExecutor Future<?> checkNotNull(mExecutor)"+new CommandRunnable(command)); return mExecutor.submit(new CommandRunnable(command)); } } 1.7. 发送拍照请求 (走到这里断了,研究ing) https://blog.csdn.net/afei__/article/details/86326991 a.CaptureRequest 表示一个捕捉的请求。根据不同的场景(预览、拍照)创建不同的捕捉请求,并可以配置不同的捕捉属性,如: 预览分辨率,预览目标,对焦模式、曝光模式等等 b.CaptureRequest.Builder TEMPLATE_STILL_CAPTURE : 用于创建一个拍照请求。相机会优先保证高画质而不是高帧率。适用于所有相机设备 K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\v2\photo\SimpleImageCaptureCommand.java /** * Sends a request to take a picture and blocks until it completes. */ //发送一个拍照请求并阻塞直到它完成 @Override public void run(Updatable<Void> imageExposureUpdatable, ImageSaver imageSaver) throws InterruptedException, CameraAccessException, CameraCaptureSessionClosedException, ResourceAcquisitionFailedException { try (FrameServer.Session session = mFrameServer.createExclusiveSession(); ImageStream imageStream = mImageReader.createStream(1)) { android.util.Log.d("gatsby", "SimpleImageCaptureCommand run"); UpdatableCountDownLatch<Void> exposureLatch = new UpdatableCountDownLatch<>(1); RequestBuilder photoRequest = mBuilderFactory.create(CameraDevice .TEMPLATE_STILL_CAPTURE); photoRequest.addStream(imageStream); MetadataFuture metadataFuture = new MetadataFuture(); photoRequest.addResponseListener(metadataFuture); photoRequest.addResponseListener(forFrameExposure(imageExposureUpdatable)); photoRequest.addResponseListener(forFrameExposure(exposureLatch)); session.submitRequest(Arrays.asList(photoRequest.build()), FrameServer.RequestType.NON_REPEATING); // Release the FrameServer session (allowing subsequent camera // operations to occur) as soon as the image is exposed. exposureLatch.await(); session.close(); ImageProxy image = imageStream.getNext(); //addFullSizeImage 时间戳 拍照尺寸 imageSaver.addFullSizeImage(image, metadataFuture.getMetadata()); } catch (BufferQueue.BufferQueueClosedException e) { // If we get here, the request was submitted, but the image // never arrived. // TODO Log failure and notify the caller } finally { imageSaver.close(); } } 1.7.1 获取照片 时间戳、尺寸 01-01 20:16:33.927 3321 3882 D gatsby : MostRecentImageSaver addFullSizeImage333->MetadataImage{timestamp=994912780557, width=1280, height=960} @Override public void addFullSizeImage(ImageProxy imageProxy, ListenableFuture<TotalCaptureResultProxy> metadata) { mFullSizeImages.put(imageProxy.getTimestamp(), new MetadataImage(imageProxy, metadata)); closeOlderImages(); } 1.8.重新回到 PictureTakerImpl 这里来 ,JpegImageBackendImageSaver.java 缩略图 final ImageSaver imageSaver = mImageSaverBuilder.build( params.saverCallback, OrientationManager.DeviceOrientation.from(params.orientation), session); K:\ZK_A40X_A40i_ANDROID7.1\android\packages\apps\Camera2\src\com\android\camera\one\v2\imagesaver\JpegImageBackendImageSaver.java public class JpegImageBackendImageSaver implements ImageSaver.Builder { @Override public void onResultCompressed(TaskImageContainer.TaskInfo task, TaskImageContainer.CompressedPayload payload) { if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) { // Just start the thumbnail now, since there's no earlier event. // Downsample and convert the JPEG payload to a reasonably-sized // Bitmap BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = JPEG_DOWNSAMPLE_FOR_FAST_INDICATOR; //gatsby payload.data 获得data final Bitmap bitmap = BitmapFactory.decodeByteArray(payload.data, 0, payload.data.length, options); // If the rotation is implemented as an EXIF flag, we need to // pass this information onto the UI call, since the rotation is // NOT applied to the bitmap directly. int rotation = Exif.getOrientation(payload.data); mSession.updateCaptureIndicatorThumbnail(bitmap, rotation); // Send image to remote devices mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data); }
A40i Camera2 默认镜像 改法
一.开日志
--- a/packages/apps/Camera2/src/com/android/camera/debug/Log.java +++ b/packages/apps/Camera2/src/com/android/camera/debug/Log.java @@ -192,7 +192,10 @@ public class Log { } private static boolean isLoggable(Tag tag, int level) { - if (sSuppressForTesting) { + + return true; + + /* if (sSuppressForTesting) { return false; } try { @@ -208,7 +211,7 @@ public class Log { } catch (IllegalArgumentException ex) { e(TAG, "Tag too long:" + tag); return false; - } + }*/ }
二.A40i 缩略图
--- a/packages/apps/Camera2/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java +++ b/packages/apps/Camera2/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java @@ -48,6 +48,18 @@ import java.util.concurrent.Executors; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; +import android.graphics.Matrix; +import android.graphics.BitmapFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.DataInputStream; +import java.io.DataOutputStream; + +import android.os.SystemProperties; + /** * Wires up the ImageBackend task submission process to save JPEG images. Camera * delivers a JPEG-compressed full-size image. This class does very little work @@ -74,6 +86,7 @@ public class JpegImageBackendImageSaver implements ImageSaver.Builder { @Override public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail, ListenableFuture<TotalCaptureResultProxy> metadata) { + android.util.Log.d("gatsby", "JpegImageBackendImageSaver.saveAndCloseImage()"); // TODO: Use thumbnail to speed up RGB thumbnail creation whenever // possible. For now, just close it. if (thumbnail.isPresent()) { @@ -132,12 +145,97 @@ public class JpegImageBackendImageSaver implements ImageSaver.Builder { // pass this information onto the UI call, since the rotation is // NOT applied to the bitmap directly. int rotation = Exif.getOrientation(payload.data); - mSession.updateCaptureIndicatorThumbnail(bitmap, rotation); + + saveBimap(bitmap); + + String savePicturePath = "storage/emulated/0/gatsby.jpg"; + String savePicturePath2 = "storage/emulated/0/gatsby2.jpg"; + File savePictureFile2 = new File("/storage/emulated/0/gatsby2.jpg"); + + Bitmap rawBitmap = BitmapFactory.decodeFile(savePicturePath); + Matrix matrix = new Matrix(); + matrix.postScale(-1, 1); + rawBitmap = Bitmap.createBitmap(rawBitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + try{ + FileOutputStream fileOutputStream = new FileOutputStream(savePictureFile2); + rawBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream); + fileOutputStream.flush(); + fileOutputStream.close(); + } catch (Exception e){ + e.printStackTrace(); + } + + Bitmap rawBitmap2 = BitmapFactory.decodeFile(savePicturePath2); + + mSession.updateCaptureIndicatorThumbnail(rawBitmap2, rotation); // Send image to remote devices mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data); + RootCommand("rm -rf storage/emulated/0/gatsby.jpg"); + RootCommand("rm -rf storage/emulated/0/gatsby2.jpg"); } + } + + + public static void saveBimap(Bitmap bitmap) { + + String savePicturePath = "storage/emulated/0/gatsby.jpg"; + File file = new File(savePicturePath); + if(!file.exists()){ + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + FileOutputStream out = new FileOutputStream(file); + if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)) { + out.flush(); + out.close(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void RootCommand(String cmd) { + android.util.Log.d("gatsby", "JpegImageBackendImageSaver.RootCommand"); + Process process = null; + DataOutputStream os = null; + DataInputStream is = null; + try { + process = Runtime.getRuntime().exec("/system/xbin/su"); + os = new DataOutputStream(process.getOutputStream()); + os.writeBytes(cmd + "\n"); + os.writeBytes("exit\n"); + os.flush(); + + int aa = process.waitFor(); + is = new DataInputStream(process.getInputStream()); + + byte[] buffer = new byte[is.available()]; + is.read(buffer); + + String out = new String(buffer); + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.close(); + } + if (is != null) { + is.close(); + } + process.destroy(); + } catch (Exception e) { + } + } + } @Override public void onResultUncompressed(TaskImageContainer.TaskInfo task,
三.A40i 成像工具类 中改成像图
--- a/packages/apps/Camera2/src/com/android/camera/data/PhotoDataFactory.java +++ b/packages/apps/Camera2/src/com/android/camera/data/PhotoDataFactory.java @@ -26,6 +26,13 @@ import com.android.camera.util.Size; import java.util.Date; +import android.graphics.Matrix; +import android.graphics.BitmapFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + public class PhotoDataFactory { private static final Log.Tag TAG = new Log.Tag("PhotoDataFact"); @@ -68,6 +75,27 @@ public class PhotoDataFactory { Location location = Location.from(latitude, longitude); Uri uri = PhotoDataQuery.CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build(); + + File filePhotoDataPath = new File(filePath); + android.util.Log.d("gatsby", "PhotoDataFactory.filePhotoDataPath"); + Bitmap rawBitmap = BitmapFactory.decodeFile(filePath); + Matrix matrix = new Matrix(); + matrix.postScale(-1, 1); + rawBitmap = Bitmap.createBitmap(rawBitmap, 0, 0, rawBitmap.getWidth(), rawBitmap.getHeight(), matrix, true); + try { + FileOutputStream fileOutputStream = new FileOutputStream(filePhotoDataPath); + rawBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream); + fileOutputStream.flush(); + fileOutputStream.close(); + } catch (Exception e){ + e.printStackTrace(); + } + + try { + Thread.sleep(300); + } catch (InterruptedException e) { + e.printStackTrace(); + } return new FilmstripItemData( id,