1 /*
2  * Copyright (C) 2019 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 #define LOG_TAG "Camera3-DepthPhotoProcessor"
18 #define ATRACE_TAG ATRACE_TAG_CAMERA
19 //#define LOG_NDEBUG 0
20 //
21 
22 #include "DepthPhotoProcessor.h"
23 
24 #include <dynamic_depth/camera.h>
25 #include <dynamic_depth/cameras.h>
26 #include <dynamic_depth/container.h>
27 #include <dynamic_depth/device.h>
28 #include <dynamic_depth/dimension.h>
29 #include <dynamic_depth/dynamic_depth.h>
30 #include <dynamic_depth/point.h>
31 #include <dynamic_depth/pose.h>
32 #include <dynamic_depth/profile.h>
33 #include <dynamic_depth/profiles.h>
34 #include <jpeglib.h>
35 #include <libexif/exif-data.h>
36 #include <libexif/exif-system.h>
37 #include <math.h>
38 #include <sstream>
39 #include <utils/Errors.h>
40 #include <utils/ExifUtils.h>
41 #include <utils/Log.h>
42 #include <xmpmeta/xmp_data.h>
43 #include <xmpmeta/xmp_writer.h>
44 
45 using dynamic_depth::Camera;
46 using dynamic_depth::Cameras;
47 using dynamic_depth::CameraParams;
48 using dynamic_depth::Container;
49 using dynamic_depth::DepthFormat;
50 using dynamic_depth::DepthMap;
51 using dynamic_depth::DepthMapParams;
52 using dynamic_depth::DepthUnits;
53 using dynamic_depth::Device;
54 using dynamic_depth::DeviceParams;
55 using dynamic_depth::Dimension;
56 using dynamic_depth::Image;
57 using dynamic_depth::ImagingModel;
58 using dynamic_depth::ImagingModelParams;
59 using dynamic_depth::Item;
60 using dynamic_depth::Pose;
61 using dynamic_depth::Profile;
62 using dynamic_depth::Profiles;
63 
64 template<>
65 struct std::default_delete<jpeg_compress_struct> {
operator ()std::default_delete66     inline void operator()(jpeg_compress_struct* cinfo) const {
67         jpeg_destroy_compress(cinfo);
68     }
69 };
70 
71 namespace android {
72 namespace camera3 {
73 
74 // Depth samples with low confidence can skew the
75 // near/far values and impact the range inverse coding.
76 static const float CONFIDENCE_THRESHOLD = .15f;
77 
getExifOrientation(const unsigned char * jpegBuffer,size_t jpegBufferSize)78 ExifOrientation getExifOrientation(const unsigned char *jpegBuffer, size_t jpegBufferSize) {
79     if ((jpegBuffer == nullptr) || (jpegBufferSize == 0)) {
80         return ExifOrientation::ORIENTATION_UNDEFINED;
81     }
82 
83     auto exifData = exif_data_new();
84     exif_data_load_data(exifData, jpegBuffer, jpegBufferSize);
85     ExifEntry *orientation = exif_content_get_entry(exifData->ifd[EXIF_IFD_0],
86             EXIF_TAG_ORIENTATION);
87     if ((orientation == nullptr) || (orientation->size != sizeof(ExifShort))) {
88         ALOGV("%s: Orientation EXIF entry invalid!", __FUNCTION__);
89         exif_data_unref(exifData);
90         return ExifOrientation::ORIENTATION_0_DEGREES;
91     }
92 
93     auto orientationValue = exif_get_short(orientation->data, exif_data_get_byte_order(exifData));
94     ExifOrientation ret;
95     switch (orientationValue) {
96         case ExifOrientation::ORIENTATION_0_DEGREES:
97         case ExifOrientation::ORIENTATION_90_DEGREES:
98         case ExifOrientation::ORIENTATION_180_DEGREES:
99         case ExifOrientation::ORIENTATION_270_DEGREES:
100             ret = static_cast<ExifOrientation> (orientationValue);
101             break;
102         default:
103             ALOGE("%s: Unexpected EXIF orientation value: %d, defaulting to 0 degrees",
104                     __FUNCTION__, orientationValue);
105             ret = ExifOrientation::ORIENTATION_0_DEGREES;
106     }
107 
108     exif_data_unref(exifData);
109 
110     return ret;
111 }
112 
encodeGrayscaleJpeg(size_t width,size_t height,uint8_t * in,void * out,const size_t maxOutSize,uint8_t jpegQuality,ExifOrientation exifOrientation,size_t & actualSize)113 status_t encodeGrayscaleJpeg(size_t width, size_t height, uint8_t *in, void *out,
114         const size_t maxOutSize, uint8_t jpegQuality, ExifOrientation exifOrientation,
115         size_t &actualSize) {
116     status_t ret;
117     // libjpeg is a C library so we use C-style "inheritance" by
118     // putting libjpeg's jpeg_destination_mgr first in our custom
119     // struct. This allows us to cast jpeg_destination_mgr* to
120     // CustomJpegDestMgr* when we get it passed to us in a callback.
121     struct CustomJpegDestMgr : public jpeg_destination_mgr {
122         JOCTET *mBuffer;
123         size_t mBufferSize;
124         size_t mEncodedSize;
125         bool mSuccess;
126     } dmgr;
127 
128     std::unique_ptr<jpeg_compress_struct> cinfo = std::make_unique<jpeg_compress_struct>();
129     jpeg_error_mgr jerr;
130 
131     // Initialize error handling with standard callbacks, but
132     // then override output_message (to print to ALOG) and
133     // error_exit to set a flag and print a message instead
134     // of killing the whole process.
135     cinfo->err = jpeg_std_error(&jerr);
136 
137     cinfo->err->output_message = [](j_common_ptr cinfo) {
138         char buffer[JMSG_LENGTH_MAX];
139 
140         /* Create the message */
141         (*cinfo->err->format_message)(cinfo, buffer);
142         ALOGE("libjpeg error: %s", buffer);
143     };
144 
145     cinfo->err->error_exit = [](j_common_ptr cinfo) {
146         (*cinfo->err->output_message)(cinfo);
147         if(cinfo->client_data) {
148             auto & dmgr = *static_cast<CustomJpegDestMgr*>(cinfo->client_data);
149             dmgr.mSuccess = false;
150         }
151     };
152 
153     // Now that we initialized some callbacks, let's create our compressor
154     jpeg_create_compress(cinfo.get());
155     dmgr.mBuffer = static_cast<JOCTET*>(out);
156     dmgr.mBufferSize = maxOutSize;
157     dmgr.mEncodedSize = 0;
158     dmgr.mSuccess = true;
159     cinfo->client_data = static_cast<void*>(&dmgr);
160 
161     // These lambdas become C-style function pointers and as per C++11 spec
162     // may not capture anything.
163     dmgr.init_destination = [](j_compress_ptr cinfo) {
164         auto & dmgr = static_cast<CustomJpegDestMgr&>(*cinfo->dest);
165         dmgr.next_output_byte = dmgr.mBuffer;
166         dmgr.free_in_buffer = dmgr.mBufferSize;
167         ALOGV("%s:%d jpeg start: %p [%zu]",
168               __FUNCTION__, __LINE__, dmgr.mBuffer, dmgr.mBufferSize);
169     };
170 
171     dmgr.empty_output_buffer = [](j_compress_ptr cinfo __unused) {
172         ALOGV("%s:%d Out of buffer", __FUNCTION__, __LINE__);
173         return 0;
174     };
175 
176     dmgr.term_destination = [](j_compress_ptr cinfo) {
177         auto & dmgr = static_cast<CustomJpegDestMgr&>(*cinfo->dest);
178         dmgr.mEncodedSize = dmgr.mBufferSize - dmgr.free_in_buffer;
179         ALOGV("%s:%d Done with jpeg: %zu", __FUNCTION__, __LINE__, dmgr.mEncodedSize);
180     };
181     cinfo->dest = static_cast<struct jpeg_destination_mgr*>(&dmgr);
182     cinfo->image_width = width;
183     cinfo->image_height = height;
184     cinfo->input_components = 1;
185     cinfo->in_color_space = JCS_GRAYSCALE;
186 
187     // Initialize defaults and then override what we want
188     jpeg_set_defaults(cinfo.get());
189 
190     jpeg_set_quality(cinfo.get(), jpegQuality, 1);
191     jpeg_set_colorspace(cinfo.get(), JCS_GRAYSCALE);
192     cinfo->raw_data_in = 0;
193     cinfo->dct_method = JDCT_IFAST;
194 
195     cinfo->comp_info[0].h_samp_factor = 1;
196     cinfo->comp_info[1].h_samp_factor = 1;
197     cinfo->comp_info[2].h_samp_factor = 1;
198     cinfo->comp_info[0].v_samp_factor = 1;
199     cinfo->comp_info[1].v_samp_factor = 1;
200     cinfo->comp_info[2].v_samp_factor = 1;
201 
202     jpeg_start_compress(cinfo.get(), TRUE);
203 
204     if (exifOrientation != ExifOrientation::ORIENTATION_UNDEFINED) {
205         std::unique_ptr<ExifUtils> utils(ExifUtils::create());
206         utils->initializeEmpty();
207         utils->setImageWidth(width);
208         utils->setImageHeight(height);
209         utils->setOrientationValue(exifOrientation);
210 
211         if (utils->generateApp1()) {
212             const uint8_t* exifBuffer = utils->getApp1Buffer();
213             size_t exifBufferSize = utils->getApp1Length();
214             jpeg_write_marker(cinfo.get(), JPEG_APP0 + 1, static_cast<const JOCTET*>(exifBuffer),
215                     exifBufferSize);
216         } else {
217             ALOGE("%s: Unable to generate App1 buffer", __FUNCTION__);
218         }
219     }
220 
221     for (size_t i = 0; i < cinfo->image_height; i++) {
222         auto currentRow  = static_cast<JSAMPROW>(in + i*width);
223         jpeg_write_scanlines(cinfo.get(), &currentRow, /*num_lines*/1);
224     }
225 
226     jpeg_finish_compress(cinfo.get());
227 
228     actualSize = dmgr.mEncodedSize;
229     if (dmgr.mSuccess) {
230         ret = NO_ERROR;
231     } else {
232         ret = UNKNOWN_ERROR;
233     }
234 
235     return ret;
236 }
237 
unpackDepth16(uint16_t value,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)238 inline void unpackDepth16(uint16_t value, std::vector<float> *points /*out*/,
239         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
240     // Android densely packed depth map. The units for the range are in
241     // millimeters and need to be scaled to meters.
242     // The confidence value is encoded in the 3 most significant bits.
243     // The confidence data needs to be additionally normalized with
244     // values 1.0f, 0.0f representing maximum and minimum confidence
245     // respectively.
246     auto point = static_cast<float>(value & 0x1FFF) / 1000.f;
247     points->push_back(point);
248 
249     auto conf = (value >> 13) & 0x7;
250     float normConfidence = (conf == 0) ? 1.f : (static_cast<float>(conf) - 1) / 7.f;
251     confidence->push_back(normConfidence);
252     if (normConfidence < CONFIDENCE_THRESHOLD) {
253         return;
254     }
255 
256     if (*near > point) {
257         *near = point;
258     }
259     if (*far < point) {
260         *far = point;
261     }
262 }
263 
264 // Trivial case, read forward from top,left corner.
rotate0AndUnpack(DepthPhotoInputFrame inputFrame,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)265 void rotate0AndUnpack(DepthPhotoInputFrame inputFrame, std::vector<float> *points /*out*/,
266         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
267     for (size_t i = 0; i < inputFrame.mDepthMapHeight; i++) {
268         for (size_t j = 0; j < inputFrame.mDepthMapWidth; j++) {
269             unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points,
270                     confidence, near, far);
271         }
272     }
273 }
274 
275 // 90 degrees CW rotation can be applied by starting to read from bottom, left corner
276 // transposing rows and columns.
rotate90AndUnpack(DepthPhotoInputFrame inputFrame,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)277 void rotate90AndUnpack(DepthPhotoInputFrame inputFrame, std::vector<float> *points /*out*/,
278         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
279     for (size_t i = 0; i < inputFrame.mDepthMapWidth; i++) {
280         for (ssize_t j = inputFrame.mDepthMapHeight-1; j >= 0; j--) {
281             unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points,
282                     confidence, near, far);
283         }
284     }
285 }
286 
287 // 180 CW degrees rotation can be applied by starting to read backwards from bottom, right corner.
rotate180AndUnpack(DepthPhotoInputFrame inputFrame,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)288 void rotate180AndUnpack(DepthPhotoInputFrame inputFrame, std::vector<float> *points /*out*/,
289         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
290     for (ssize_t i = inputFrame.mDepthMapHeight-1; i >= 0; i--) {
291         for (ssize_t j = inputFrame.mDepthMapWidth-1; j >= 0; j--) {
292             unpackDepth16(inputFrame.mDepthMapBuffer[i*inputFrame.mDepthMapStride + j], points,
293                     confidence, near, far);
294         }
295     }
296 }
297 
298 // 270 degrees CW rotation can be applied by starting to read from top, right corner
299 // transposing rows and columns.
rotate270AndUnpack(DepthPhotoInputFrame inputFrame,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)300 void rotate270AndUnpack(DepthPhotoInputFrame inputFrame, std::vector<float> *points /*out*/,
301         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
302     for (ssize_t i = inputFrame.mDepthMapWidth-1; i >= 0; i--) {
303         for (size_t j = 0; j < inputFrame.mDepthMapHeight; j++) {
304             unpackDepth16(inputFrame.mDepthMapBuffer[j*inputFrame.mDepthMapStride + i], points,
305                     confidence, near, far);
306         }
307     }
308 }
309 
rotateAndUnpack(DepthPhotoInputFrame inputFrame,std::vector<float> * points,std::vector<float> * confidence,float * near,float * far)310 bool rotateAndUnpack(DepthPhotoInputFrame inputFrame, std::vector<float> *points /*out*/,
311         std::vector<float> *confidence /*out*/, float *near /*out*/, float *far /*out*/) {
312     switch (inputFrame.mOrientation) {
313         case DepthPhotoOrientation::DEPTH_ORIENTATION_0_DEGREES:
314             rotate0AndUnpack(inputFrame, points, confidence, near, far);
315             return false;
316         case DepthPhotoOrientation::DEPTH_ORIENTATION_90_DEGREES:
317             rotate90AndUnpack(inputFrame, points, confidence, near, far);
318             return true;
319         case DepthPhotoOrientation::DEPTH_ORIENTATION_180_DEGREES:
320             rotate180AndUnpack(inputFrame, points, confidence, near, far);
321             return false;
322         case DepthPhotoOrientation::DEPTH_ORIENTATION_270_DEGREES:
323             rotate270AndUnpack(inputFrame, points, confidence, near, far);
324             return true;
325         default:
326             ALOGE("%s: Unsupported depth photo rotation: %d, default to 0", __FUNCTION__,
327                     inputFrame.mOrientation);
328             rotate0AndUnpack(inputFrame, points, confidence, near, far);
329     }
330 
331     return false;
332 }
333 
processDepthMapFrame(DepthPhotoInputFrame inputFrame,ExifOrientation exifOrientation,std::vector<std::unique_ptr<Item>> * items,bool * switchDimensions)334 std::unique_ptr<dynamic_depth::DepthMap> processDepthMapFrame(DepthPhotoInputFrame inputFrame,
335         ExifOrientation exifOrientation, std::vector<std::unique_ptr<Item>> *items /*out*/,
336         bool *switchDimensions /*out*/) {
337     if ((items == nullptr) || (switchDimensions == nullptr)) {
338         return nullptr;
339     }
340 
341     std::vector<float> points, confidence;
342 
343     size_t pointCount = inputFrame.mDepthMapWidth * inputFrame.mDepthMapHeight;
344     points.reserve(pointCount);
345     confidence.reserve(pointCount);
346     float near = UINT16_MAX;
347     float far = .0f;
348     *switchDimensions = false;
349     // Physical rotation of depth and confidence maps may be needed in case
350     // the EXIF orientation is set to 0 degrees and the depth photo orientation
351     // (source color image) has some different value.
352     if (exifOrientation == ExifOrientation::ORIENTATION_0_DEGREES) {
353         *switchDimensions = rotateAndUnpack(inputFrame, &points, &confidence, &near, &far);
354     } else {
355         rotate0AndUnpack(inputFrame, &points, &confidence, &near, &far);
356     }
357 
358     size_t width = inputFrame.mDepthMapWidth;
359     size_t height = inputFrame.mDepthMapHeight;
360     if (*switchDimensions) {
361         width = inputFrame.mDepthMapHeight;
362         height = inputFrame.mDepthMapWidth;
363     }
364 
365     if (near == far) {
366         ALOGE("%s: Near and far range values must not match!", __FUNCTION__);
367         return nullptr;
368     }
369 
370     std::vector<uint8_t> pointsQuantized, confidenceQuantized;
371     pointsQuantized.reserve(pointCount); confidenceQuantized.reserve(pointCount);
372     auto pointIt = points.begin();
373     auto confidenceIt = confidence.begin();
374     while ((pointIt != points.end()) && (confidenceIt != confidence.end())) {
375         auto point = *pointIt;
376         if ((*confidenceIt) < CONFIDENCE_THRESHOLD) {
377             point = std::clamp(point, near, far);
378         }
379         pointsQuantized.push_back(floorf(((far * (point - near)) /
380                 (point * (far - near))) * 255.0f));
381         confidenceQuantized.push_back(floorf(*confidenceIt * 255.0f));
382         confidenceIt++; pointIt++;
383     }
384 
385     DepthMapParams depthParams(DepthFormat::kRangeInverse, near, far, DepthUnits::kMeters,
386             "android/depthmap");
387     depthParams.confidence_uri = "android/confidencemap";
388     depthParams.mime = "image/jpeg";
389     depthParams.depth_image_data.resize(inputFrame.mMaxJpegSize);
390     depthParams.confidence_data.resize(inputFrame.mMaxJpegSize);
391     size_t actualJpegSize;
392     auto ret = encodeGrayscaleJpeg(width, height, pointsQuantized.data(),
393             depthParams.depth_image_data.data(), inputFrame.mMaxJpegSize,
394             inputFrame.mJpegQuality, exifOrientation, actualJpegSize);
395     if (ret != NO_ERROR) {
396         ALOGE("%s: Depth map compression failed!", __FUNCTION__);
397         return nullptr;
398     }
399     depthParams.depth_image_data.resize(actualJpegSize);
400 
401     ret = encodeGrayscaleJpeg(width, height, confidenceQuantized.data(),
402             depthParams.confidence_data.data(), inputFrame.mMaxJpegSize,
403             inputFrame.mJpegQuality, exifOrientation, actualJpegSize);
404     if (ret != NO_ERROR) {
405         ALOGE("%s: Confidence map compression failed!", __FUNCTION__);
406         return nullptr;
407     }
408     depthParams.confidence_data.resize(actualJpegSize);
409 
410     return DepthMap::FromData(depthParams, items);
411 }
412 
processDepthPhotoFrame(DepthPhotoInputFrame inputFrame,size_t depthPhotoBufferSize,void * depthPhotoBuffer,size_t * depthPhotoActualSize)413 int processDepthPhotoFrame(DepthPhotoInputFrame inputFrame, size_t depthPhotoBufferSize,
414         void* depthPhotoBuffer /*out*/, size_t* depthPhotoActualSize /*out*/) {
415     if ((inputFrame.mMainJpegBuffer == nullptr) || (inputFrame.mDepthMapBuffer == nullptr) ||
416             (depthPhotoBuffer == nullptr) || (depthPhotoActualSize == nullptr)) {
417         return BAD_VALUE;
418     }
419 
420     std::vector<std::unique_ptr<Item>> items;
421     std::vector<std::unique_ptr<Camera>> cameraList;
422     auto image = Image::FromDataForPrimaryImage("image/jpeg", &items);
423     std::unique_ptr<CameraParams> cameraParams(new CameraParams(std::move(image)));
424     if (cameraParams == nullptr) {
425         ALOGE("%s: Failed to initialize camera parameters", __FUNCTION__);
426         return BAD_VALUE;
427     }
428 
429     ExifOrientation exifOrientation = getExifOrientation(
430             reinterpret_cast<const unsigned char*> (inputFrame.mMainJpegBuffer),
431             inputFrame.mMainJpegSize);
432     bool switchDimensions;
433     cameraParams->depth_map = processDepthMapFrame(inputFrame, exifOrientation, &items,
434             &switchDimensions);
435     if (cameraParams->depth_map == nullptr) {
436         ALOGE("%s: Depth map processing failed!", __FUNCTION__);
437         return BAD_VALUE;
438     }
439 
440     // It is not possible to generate an imaging model without intrinsic calibration.
441     if (inputFrame.mIsIntrinsicCalibrationValid) {
442         // The camera intrinsic calibration layout is as follows:
443         // [focalLengthX, focalLengthY, opticalCenterX, opticalCenterY, skew]
444         const dynamic_depth::Point<double> focalLength(inputFrame.mIntrinsicCalibration[0],
445                 inputFrame.mIntrinsicCalibration[1]);
446         size_t width = inputFrame.mMainJpegWidth;
447         size_t height = inputFrame.mMainJpegHeight;
448         if (switchDimensions) {
449             width = inputFrame.mMainJpegHeight;
450             height = inputFrame.mMainJpegWidth;
451         }
452         const Dimension imageSize(width, height);
453         ImagingModelParams imagingParams(focalLength, imageSize);
454         imagingParams.principal_point.x = inputFrame.mIntrinsicCalibration[2];
455         imagingParams.principal_point.y = inputFrame.mIntrinsicCalibration[3];
456         imagingParams.skew = inputFrame.mIntrinsicCalibration[4];
457 
458         // The camera lens distortion contains the following lens correction coefficients.
459         // [kappa_1, kappa_2, kappa_3 kappa_4, kappa_5]
460         if (inputFrame.mIsLensDistortionValid) {
461             // According to specification the lens distortion coefficients should be ordered
462             // as [1, kappa_4, kappa_1, kappa_5, kappa_2, 0, kappa_3, 0]
463             float distortionData[] = {1.f, inputFrame.mLensDistortion[3],
464                     inputFrame.mLensDistortion[0], inputFrame.mLensDistortion[4],
465                     inputFrame.mLensDistortion[1], 0.f, inputFrame.mLensDistortion[2], 0.f};
466             auto distortionDataLength = sizeof(distortionData) / sizeof(distortionData[0]);
467             imagingParams.distortion.reserve(distortionDataLength);
468             imagingParams.distortion.insert(imagingParams.distortion.end(), distortionData,
469                     distortionData + distortionDataLength);
470         }
471 
472         cameraParams->imaging_model = ImagingModel::FromData(imagingParams);
473     }
474 
475     if (inputFrame.mIsLogical) {
476         cameraParams->trait = dynamic_depth::CameraTrait::LOGICAL;
477     } else {
478         cameraParams->trait = dynamic_depth::CameraTrait::PHYSICAL;
479     }
480 
481     cameraList.emplace_back(Camera::FromData(std::move(cameraParams)));
482 
483     auto deviceParams = std::make_unique<DeviceParams> (Cameras::FromCameraArray(&cameraList));
484     deviceParams->container = Container::FromItems(&items);
485     std::vector<std::unique_ptr<Profile>> profileList;
486     profileList.emplace_back(Profile::FromData("DepthPhoto", {0}));
487     deviceParams->profiles = Profiles::FromProfileArray(&profileList);
488     std::unique_ptr<Device> device = Device::FromData(std::move(deviceParams));
489     if (device == nullptr) {
490         ALOGE("%s: Failed to initialize camera device", __FUNCTION__);
491         return BAD_VALUE;
492     }
493 
494     std::istringstream inputJpegStream(
495             std::string(inputFrame.mMainJpegBuffer, inputFrame.mMainJpegSize));
496     std::ostringstream outputJpegStream;
497     if (!WriteImageAndMetadataAndContainer(&inputJpegStream, device.get(), &outputJpegStream)) {
498         ALOGE("%s: Failed writing depth output", __FUNCTION__);
499         return BAD_VALUE;
500     }
501 
502     *depthPhotoActualSize = static_cast<size_t> (outputJpegStream.tellp());
503     if (*depthPhotoActualSize > depthPhotoBufferSize) {
504         ALOGE("%s: Depth photo output buffer not sufficient, needed %zu actual %zu", __FUNCTION__,
505                 *depthPhotoActualSize, depthPhotoBufferSize);
506         return NO_MEMORY;
507     }
508 
509     memcpy(depthPhotoBuffer, outputJpegStream.str().c_str(), *depthPhotoActualSize);
510 
511     return 0;
512 }
513 
514 }; // namespace camera3
515 }; // namespace android
516