1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.testingcamera2; 18 19 import java.util.ArrayList; 20 import java.util.Date; 21 import java.util.LinkedList; 22 import java.util.List; 23 import java.util.Objects; 24 import java.io.File; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.nio.ByteBuffer; 29 import java.nio.ShortBuffer; 30 import java.nio.channels.Channels; 31 import java.nio.channels.WritableByteChannel; 32 import java.text.SimpleDateFormat; 33 34 import android.content.Context; 35 import android.graphics.ImageFormat; 36 import android.graphics.Bitmap; 37 import android.graphics.BitmapFactory; 38 import android.graphics.Color; 39 import android.hardware.camera2.CameraCharacteristics; 40 import android.hardware.camera2.DngCreator; 41 import android.hardware.camera2.TotalCaptureResult; 42 import android.hardware.camera2.params.StreamConfigurationMap; 43 import android.media.Image; 44 import android.media.ImageReader; 45 import android.os.Environment; 46 import android.os.SystemClock; 47 import android.util.Size; 48 import android.util.AttributeSet; 49 import android.view.LayoutInflater; 50 import android.view.Surface; 51 import android.view.View; 52 import android.widget.AdapterView; 53 import android.widget.ArrayAdapter; 54 import android.widget.Button; 55 import android.widget.ImageView; 56 import android.widget.LinearLayout; 57 import android.widget.Spinner; 58 import android.widget.AdapterView.OnItemSelectedListener; 59 60 public class ImageReaderSubPane extends TargetSubPane { 61 62 private static final int NO_FORMAT = -1; 63 private static final int NO_SIZE = -1; 64 private static final int NO_IMAGE = -1; 65 private static final int MAX_BUFFER_COUNT = 25; 66 private static final int DEFAULT_BUFFER_COUNT = 3; 67 68 enum OutputFormat { 69 JPEG(ImageFormat.JPEG), 70 RAW16(ImageFormat.RAW_SENSOR), 71 RAW10(ImageFormat.RAW10), 72 YUV_420_888(ImageFormat.YUV_420_888); 73 74 public final int imageFormat; 75 76 OutputFormat(int imageFormat) { 77 this.imageFormat = imageFormat; 78 } 79 }; 80 81 private Surface mSurface; 82 83 private final Spinner mFormatSpinner; 84 private final List<OutputFormat> mFormats = new ArrayList<>(); 85 86 private final Spinner mSizeSpinner; 87 private Size[] mSizes; 88 private final Spinner mCountSpinner; 89 private Integer[] mCounts; 90 91 private final ImageView mImageView; 92 93 private int mCurrentCameraOrientation = 0; 94 private int mCurrentUiOrientation = 0; 95 96 private int mCurrentFormatId = NO_FORMAT; 97 private int mCurrentSizeId = NO_SIZE; 98 private CameraControlPane mCurrentCamera; 99 100 private OutputFormat mConfiguredFormat = null; 101 private Size mConfiguredSize = null; 102 private int mConfiguredCount = 0; 103 104 private ImageReader mReader = null; 105 private final LinkedList<Image> mCurrentImages = new LinkedList<>(); 106 private int mCurrentImageIdx = NO_IMAGE; 107 108 private int mRawShiftFactor = 0; 109 private int mRawShiftRow = 0; 110 private int mRawShiftCol = 0; 111 112 public ImageReaderSubPane(Context context, AttributeSet attrs) { 113 super(context, attrs); 114 115 LayoutInflater inflater = 116 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 117 118 inflater.inflate(R.layout.imagereader_target_subpane, this); 119 this.setOrientation(VERTICAL); 120 121 mFormatSpinner = 122 (Spinner) this.findViewById(R.id.target_subpane_image_reader_format_spinner); 123 mFormatSpinner.setOnItemSelectedListener(mFormatSpinnerListener); 124 125 mSizeSpinner = (Spinner) this.findViewById(R.id.target_subpane_image_reader_size_spinner); 126 mSizeSpinner.setOnItemSelectedListener(mSizeSpinnerListener); 127 128 mCountSpinner = 129 (Spinner) this.findViewById(R.id.target_subpane_image_reader_count_spinner); 130 mCounts = new Integer[MAX_BUFFER_COUNT]; 131 for (int i = 0; i < mCounts.length; i++) { 132 mCounts[i] = i + 1; 133 } 134 mCountSpinner.setAdapter(new ArrayAdapter<>(getContext(), R.layout.spinner_item, 135 mCounts)); 136 mCountSpinner.setSelection(DEFAULT_BUFFER_COUNT - 1); 137 138 mImageView = (ImageView) this.findViewById(R.id.target_subpane_image_reader_view); 139 140 Button b = (Button) this.findViewById(R.id.target_subpane_image_reader_prev_button); 141 b.setOnClickListener(mPrevButtonListener); 142 143 b = (Button) this.findViewById(R.id.target_subpane_image_reader_next_button); 144 b.setOnClickListener(mNextButtonListener); 145 146 b = (Button) this.findViewById(R.id.target_subpane_image_reader_save_button); 147 b.setOnClickListener(mSaveButtonListener); 148 } 149 150 @Override 151 public void setTargetCameraPane(CameraControlPane target) { 152 mCurrentCamera = target; 153 if (target != null) { 154 updateFormats(); 155 } else { 156 mSizeSpinner.setAdapter(null); 157 mCurrentSizeId = NO_SIZE; 158 } 159 } 160 161 @Override 162 public void setUiOrientation(int orientation) { 163 mCurrentUiOrientation = orientation; 164 } 165 166 private void updateFormats() { 167 if (mCurrentCamera == null) { 168 mFormatSpinner.setAdapter(null); 169 mCurrentFormatId = NO_FORMAT; 170 updateSizes(); 171 return; 172 } 173 174 OutputFormat oldFormat = null; 175 if (mCurrentFormatId != NO_FORMAT) { 176 oldFormat = mFormats.get(mCurrentFormatId); 177 } 178 179 CameraCharacteristics info = mCurrentCamera.getCharacteristics(); 180 StreamConfigurationMap streamConfigMap = 181 info.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 182 183 mFormats.clear(); 184 for (OutputFormat format : OutputFormat.values()) { 185 if (streamConfigMap.isOutputSupportedFor(format.imageFormat)) { 186 mFormats.add(format); 187 } 188 } 189 190 int newSelectionId = 0; 191 for (int i = 0; i < mFormats.size(); i++) { 192 if (mFormats.get(i).equals(oldFormat)) { 193 newSelectionId = i; 194 break; 195 } 196 } 197 198 String[] outputFormatItems = new String[mFormats.size()]; 199 for (int i = 0; i < outputFormatItems.length; i++) { 200 outputFormatItems[i] = mFormats.get(i).toString(); 201 } 202 203 mFormatSpinner.setAdapter(new ArrayAdapter<>(getContext(), R.layout.spinner_item, 204 outputFormatItems)); 205 mFormatSpinner.setSelection(newSelectionId); 206 mCurrentFormatId = newSelectionId; 207 208 // Map sensor orientation to Surface.ROTATE_* constants 209 final int SENSOR_ORIENTATION_TO_SURFACE_ROTATE = 90; 210 mCurrentCameraOrientation = info.get(CameraCharacteristics.SENSOR_ORIENTATION) / 211 SENSOR_ORIENTATION_TO_SURFACE_ROTATE; 212 213 // Get the max white level for raw data if any 214 Integer maxLevel = info.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL); 215 if (maxLevel != null) { 216 int l = maxLevel; 217 // Find number of bits to shift to map from 0..WHITE_LEVEL to 0..255 218 for (mRawShiftFactor = 0; l > 255; mRawShiftFactor++) l >>= 1; 219 } else { 220 mRawShiftFactor = 0; 221 } 222 223 Integer cfa = info.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT); 224 if (cfa != null) { 225 switch (cfa) { 226 case CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: 227 mRawShiftRow = 0; 228 mRawShiftCol = 0; 229 break; 230 case CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: 231 mRawShiftRow = 0; 232 mRawShiftCol = 1; 233 break; 234 case CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: 235 mRawShiftRow = 1; 236 mRawShiftCol = 0; 237 break; 238 case CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: 239 mRawShiftRow = 1; 240 mRawShiftCol = 1; 241 break; 242 case CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB: 243 mRawShiftRow = 0; 244 mRawShiftCol = 0; 245 246 break; 247 } 248 } 249 updateSizes(); 250 } 251 252 private void updateSizes() { 253 254 if (mCurrentCamera == null) { 255 mSizeSpinner.setAdapter(null); 256 mCurrentSizeId = NO_SIZE; 257 return; 258 } 259 260 Size oldSize = null; 261 if (mCurrentSizeId != NO_SIZE) { 262 oldSize = mSizes[mCurrentSizeId]; 263 } 264 265 CameraCharacteristics info = mCurrentCamera.getCharacteristics(); 266 StreamConfigurationMap streamConfigMap = 267 info.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); 268 269 mSizes = streamConfigMap.getOutputSizes(mFormats.get(mCurrentFormatId).imageFormat); 270 271 int newSelectionId = 0; 272 for (int i = 0; i < mSizes.length; i++) { 273 if (mSizes[i].equals(oldSize)) { 274 newSelectionId = i; 275 break; 276 } 277 } 278 String[] outputSizeItems = new String[mSizes.length]; 279 for (int i = 0; i < outputSizeItems.length; i++) { 280 outputSizeItems[i] = mSizes[i].toString(); 281 } 282 283 mSizeSpinner.setAdapter(new ArrayAdapter<>(getContext(), R.layout.spinner_item, 284 outputSizeItems)); 285 mSizeSpinner.setSelection(newSelectionId); 286 mCurrentSizeId = newSelectionId; 287 } 288 289 private void updateImage() { 290 if (mCurrentImageIdx == NO_IMAGE) return; 291 Image img = mCurrentImages.get(mCurrentImageIdx); 292 293 // Find rough scale factor to fit image into imageview to minimize processing overhead 294 // Want to be one factor too large 295 int SCALE_FACTOR = 2; 296 while (mConfiguredSize.getWidth() > (mImageView.getWidth() * SCALE_FACTOR << 1) ) { 297 SCALE_FACTOR <<= 1; 298 } 299 300 Bitmap imgBitmap = null; 301 switch (img.getFormat()) { 302 case ImageFormat.JPEG: { 303 ByteBuffer jpegBuffer = img.getPlanes()[0].getBuffer(); 304 jpegBuffer.rewind(); 305 byte[] jpegData = new byte[jpegBuffer.limit()]; 306 jpegBuffer.get(jpegData); 307 BitmapFactory.Options opts = new BitmapFactory.Options(); 308 opts.inSampleSize = SCALE_FACTOR; 309 imgBitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, opts); 310 break; 311 } 312 case ImageFormat.YUV_420_888: { 313 ByteBuffer yBuffer = img.getPlanes()[0].getBuffer(); 314 yBuffer.rewind(); 315 int w = mConfiguredSize.getWidth() / SCALE_FACTOR; 316 int h = mConfiguredSize.getHeight() / SCALE_FACTOR; 317 byte[] row = new byte[mConfiguredSize.getWidth()]; 318 int[] imgArray = new int[w * h]; 319 for (int y = 0, j = 0; y < h; y++) { 320 yBuffer.position(y * SCALE_FACTOR * mConfiguredSize.getWidth()); 321 yBuffer.get(row); 322 for (int x = 0, i = 0; x < w; x++) { 323 int yval = row[i] & 0xFF; 324 imgArray[j] = Color.rgb(yval, yval, yval); 325 i += SCALE_FACTOR; 326 j++; 327 } 328 } 329 imgBitmap = Bitmap.createBitmap(imgArray, w, h, Bitmap.Config.ARGB_8888); 330 break; 331 } 332 case ImageFormat.RAW_SENSOR: { 333 ShortBuffer rawBuffer = img.getPlanes()[0].getBuffer().asShortBuffer(); 334 rawBuffer.rewind(); 335 // Very rough nearest-neighbor downsample for display 336 int w = mConfiguredSize.getWidth() / SCALE_FACTOR; 337 int h = mConfiguredSize.getHeight() / SCALE_FACTOR; 338 short[] redRow = new short[mConfiguredSize.getWidth()]; 339 short[] blueRow = new short[mConfiguredSize.getWidth()]; 340 int[] imgArray = new int[w * h]; 341 for (int y = 0, j = 0; y < h; y++) { 342 // Align to start of red row in the pair to sample from 343 rawBuffer.position( 344 (y * SCALE_FACTOR + mRawShiftRow) * mConfiguredSize.getWidth()); 345 rawBuffer.get(redRow); 346 // Align to start of blue row in the pair to sample from 347 rawBuffer.position( 348 (y * SCALE_FACTOR + 1 - mRawShiftRow) * mConfiguredSize.getWidth()); 349 rawBuffer.get(blueRow); 350 for (int x = 0, i = 0; x < w; x++, i += SCALE_FACTOR, j++) { 351 int r = redRow[i + mRawShiftCol] >> mRawShiftFactor; 352 int g = redRow[i + 1 - mRawShiftCol] >> mRawShiftFactor; 353 int b = blueRow[i + 1 - mRawShiftCol] >> mRawShiftFactor; 354 imgArray[j] = Color.rgb(r,g,b); 355 } 356 } 357 imgBitmap = Bitmap.createBitmap(imgArray, w, h, Bitmap.Config.ARGB_8888); 358 break; 359 } 360 case ImageFormat.RAW10: { 361 TLog.e("RAW10 viewing not implemented"); 362 break; 363 } 364 } 365 if (imgBitmap != null) { 366 mImageView.setImageBitmap(imgBitmap); 367 } 368 } 369 370 @Override 371 public Surface getOutputSurface() { 372 if (mCurrentSizeId == NO_SIZE || 373 mCurrentFormatId == NO_FORMAT) { 374 return null; 375 } 376 Size s = mSizes[mCurrentSizeId]; 377 OutputFormat f = mFormats.get(mCurrentFormatId); 378 int c = (Integer) mCountSpinner.getSelectedItem(); 379 if (mReader == null || 380 !Objects.equals(mConfiguredSize, s) || 381 !Objects.equals(mConfiguredFormat, f) || 382 mConfiguredCount != c) { 383 384 if (mReader != null) { 385 mReader.close(); 386 mCurrentImages.clear(); 387 mCurrentImageIdx = NO_IMAGE; 388 } 389 mReader = ImageReader.newInstance(s.getWidth(), s.getHeight(), f.imageFormat, c); 390 mReader.setOnImageAvailableListener(mImageListener, null); 391 mConfiguredSize = s; 392 mConfiguredFormat = f; 393 mConfiguredCount = c; 394 } 395 return mReader.getSurface(); 396 } 397 398 private final OnItemSelectedListener mFormatSpinnerListener = new OnItemSelectedListener() { 399 @Override 400 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 401 mCurrentFormatId = pos; 402 updateSizes(); 403 }; 404 405 @Override 406 public void onNothingSelected(AdapterView<?> parent) { 407 mCurrentFormatId = NO_FORMAT; 408 }; 409 }; 410 411 private final OnItemSelectedListener mSizeSpinnerListener = new OnItemSelectedListener() { 412 @Override 413 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 414 mCurrentSizeId = pos; 415 }; 416 417 @Override 418 public void onNothingSelected(AdapterView<?> parent) { 419 mCurrentSizeId = NO_SIZE; 420 }; 421 }; 422 423 private final OnClickListener mPrevButtonListener = new OnClickListener() { 424 @Override 425 public void onClick(View v) { 426 if (mCurrentImageIdx != NO_IMAGE) { 427 int prevIdx = mCurrentImageIdx; 428 mCurrentImageIdx = (mCurrentImageIdx == 0) ? 429 (mCurrentImages.size() - 1) : (mCurrentImageIdx - 1); 430 if (prevIdx != mCurrentImageIdx) { 431 updateImage(); 432 } 433 } 434 } 435 }; 436 437 private final OnClickListener mNextButtonListener = new OnClickListener() { 438 @Override 439 public void onClick(View v) { 440 if (mCurrentImageIdx != NO_IMAGE) { 441 int prevIdx = mCurrentImageIdx; 442 mCurrentImageIdx = (mCurrentImageIdx == mCurrentImages.size() - 1) ? 443 0 : (mCurrentImageIdx + 1); 444 if (prevIdx != mCurrentImageIdx) { 445 updateImage(); 446 } 447 } 448 } 449 }; 450 451 private final OnClickListener mSaveButtonListener = new OnClickListener() { 452 @Override 453 public void onClick(View v) { 454 // TODO: Make async and coordinate with onImageAvailable 455 if (mCurrentImageIdx != NO_IMAGE) { 456 Image img = mCurrentImages.get(mCurrentImageIdx); 457 try { 458 String name = saveImage(img); 459 TLog.i("Saved image as %s", name); 460 } catch (IOException e) { 461 TLog.e("Can't save file:", e); 462 } 463 } 464 } 465 }; 466 467 private final ImageReader.OnImageAvailableListener mImageListener = 468 new ImageReader.OnImageAvailableListener() { 469 @Override 470 public void onImageAvailable(ImageReader reader) { 471 while (mCurrentImages.size() >= reader.getMaxImages()) { 472 Image oldest = mCurrentImages.remove(); 473 oldest.close(); 474 mCurrentImageIdx = Math.min(mCurrentImageIdx - 1, 0); 475 } 476 mCurrentImages.add(reader.acquireNextImage()); 477 if (mCurrentImageIdx == NO_IMAGE) { 478 mCurrentImageIdx = 0; 479 } 480 updateImage(); 481 } 482 }; 483 484 private String saveImage(Image img) throws IOException { 485 long timestamp = img.getTimestamp(); 486 File output = getOutputImageFile(img.getFormat(), timestamp); 487 try (FileOutputStream out = new FileOutputStream(output)) { 488 switch(img.getFormat()) { 489 case ImageFormat.JPEG: { 490 writeJpegImage(img, out); 491 break; 492 } 493 case ImageFormat.YUV_420_888: { 494 writeYuvImage(img, out); 495 break; 496 } 497 case ImageFormat.RAW_SENSOR: { 498 writeDngImage(img, out); 499 break; 500 } 501 case ImageFormat.RAW10: { 502 TLog.e("RAW10 saving not implemented"); 503 break; 504 } 505 } 506 } 507 return output.getName(); 508 } 509 510 private void writeDngImage(Image img, OutputStream out) throws IOException { 511 if (img.getFormat() != ImageFormat.RAW_SENSOR) { 512 throw new IOException( 513 String.format("Unexpected Image format: %d, expected ImageFormat.RAW_SENSOR", 514 img.getFormat())); 515 } 516 long timestamp = img.getTimestamp(); 517 if (mCurrentCamera == null) { 518 TLog.e("No camera availble for camera info, not saving DNG (timestamp %d)", 519 timestamp); 520 throw new IOException("No camera info available"); 521 } 522 TotalCaptureResult result = mCurrentCamera.getResultAt(timestamp); 523 if (result == null) { 524 TLog.e("No result matching raw image found, not saving DNG (timestamp %d)", 525 timestamp); 526 throw new IOException("No matching result found"); 527 } 528 CameraCharacteristics info = mCurrentCamera.getCharacteristics(); 529 try (DngCreator writer = new DngCreator(info, result)) { 530 writer.writeImage(out, img); 531 } 532 } 533 534 private void writeJpegImage(Image img, OutputStream out) throws IOException { 535 if (img.getFormat() != ImageFormat.JPEG) { 536 throw new IOException( 537 String.format("Unexpected Image format: %d, expected ImageFormat.JPEG", 538 img.getFormat())); 539 } 540 WritableByteChannel outChannel = Channels.newChannel(out); 541 ByteBuffer jpegData = img.getPlanes()[0].getBuffer(); 542 jpegData.rewind(); 543 outChannel.write(jpegData); 544 } 545 546 private void writeYuvImage(Image img, OutputStream out) 547 throws IOException { 548 if (img.getFormat() != ImageFormat.YUV_420_888) { 549 throw new IOException( 550 String.format("Unexpected Image format: %d, expected ImageFormat.YUV_420_888", 551 img.getFormat())); 552 } 553 WritableByteChannel outChannel = Channels.newChannel(out); 554 for (int plane = 0; plane < 3; plane++) { 555 Image.Plane colorPlane = img.getPlanes()[plane]; 556 ByteBuffer colorData = colorPlane.getBuffer(); 557 int subsampleFactor = (plane == 0) ? 1 : 2; 558 int colorW = img.getWidth() / subsampleFactor; 559 int colorH = img.getHeight() / subsampleFactor; 560 colorData.rewind(); 561 colorData.limit(colorData.capacity()); 562 if (colorPlane.getPixelStride() == 1) { 563 // Can write contiguous rows 564 for (int y = 0, rowStart = 0; y < colorH; 565 y++, rowStart += colorPlane.getRowStride()) { 566 colorData.limit(rowStart + colorW); 567 colorData.position(rowStart); 568 outChannel.write(colorData); 569 } 570 } else { 571 // Need to pack rows 572 byte[] row = new byte[colorW * colorPlane.getPixelStride()]; 573 byte[] packedRow = new byte[colorW]; 574 ByteBuffer packedRowBuffer = ByteBuffer.wrap(packedRow); 575 for (int y = 0, rowStart = 0; y < colorH; 576 y++, rowStart += colorPlane.getRowStride()) { 577 colorData.position(rowStart); 578 colorData.get(row); 579 for (int x = 0, i = 0; x < colorW; 580 x++, i += colorPlane.getPixelStride()) { 581 packedRow[x] = row[i]; 582 } 583 packedRowBuffer.rewind(); 584 outChannel.write(packedRowBuffer); 585 } 586 } 587 } 588 } 589 590 File getOutputImageFile(int type, long timestamp){ 591 // To be safe, you should check that the SDCard is mounted 592 // using Environment.getExternalStorageState() before doing this. 593 594 String state = Environment.getExternalStorageState(); 595 if (!Environment.MEDIA_MOUNTED.equals(state)) { 596 return null; 597 } 598 599 File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( 600 Environment.DIRECTORY_DCIM), "TestingCamera2"); 601 // This location works best if you want the created images to be shared 602 // between applications and persist after your app has been uninstalled. 603 604 // Create the storage directory if it does not exist 605 if (!mediaStorageDir.exists()){ 606 if (!mediaStorageDir.mkdirs()){ 607 TLog.e("Failed to create directory for pictures/video"); 608 return null; 609 } 610 } 611 612 // Create a media file name 613 614 // Find out time now in the Date and boottime time bases. 615 long nowMs = new Date().getTime(); 616 long nowBootTimeNs = SystemClock.elapsedRealtimeNanos(); 617 618 // Convert timestamp from boottime time base to the Date timebase 619 // Slightly approximate, but close enough 620 final long NS_PER_MS = 1000000l; 621 long timestampMs = (nowMs * NS_PER_MS - nowBootTimeNs + timestamp) / NS_PER_MS; 622 623 String timeStamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS"). 624 format(new Date(timestampMs)); 625 File mediaFile = null; 626 switch(type) { 627 case ImageFormat.JPEG: 628 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 629 "IMG_"+ timeStamp + ".jpg"); 630 break; 631 case ImageFormat.YUV_420_888: 632 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 633 "IMG_"+ timeStamp + ".yuv"); 634 break; 635 case ImageFormat.RAW_SENSOR: 636 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 637 "IMG_"+ timeStamp + ".dng"); 638 break; 639 case ImageFormat.RAW10: 640 mediaFile = new File(mediaStorageDir.getPath() + File.separator + 641 "IMG_"+ timeStamp + ".raw10"); 642 break; 643 } 644 645 return mediaFile; 646 } 647 648 } 649