Merge "Audioflinger intercept track retry on buffer end"
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index c038314..0e969c7 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -162,6 +162,28 @@
* Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
*/
const int EVENT_NONE = 0;
- const int EVENT_USER_SWITCHED = 1;
+ const int EVENT_USER_SWITCHED = 1; // The argument is the set of new foreground user IDs.
oneway void notifySystemEvent(int eventId, in int[] args);
+
+ /**
+ * Notify the camera service of a device physical status change. May only be called from
+ * a privileged process.
+ *
+ * newState is a bitfield consisting of DEVICE_STATE_* values combined together. Valid state
+ * combinations are device-specific. At device startup, the camera service will assume the device
+ * state is NORMAL until otherwise notified.
+ *
+ * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
+ */
+ oneway void notifyDeviceStateChange(long newState);
+
+ // Bitfield constants for notifyDeviceStateChange
+ // All bits >= 32 are for custom vendor states
+ // Written as ints since AIDL does not support long constants.
+ const int DEVICE_STATE_NORMAL = 0;
+ const int DEVICE_STATE_BACK_COVERED = 1;
+ const int DEVICE_STATE_FRONT_COVERED = 2;
+ const int DEVICE_STATE_FOLDED = 4;
+ const int DEVICE_STATE_LAST_FRAMEWORK_BIT = 0x80000000; // 1 << 31;
+
}
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index b88a2c5..92b06c2 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -27,6 +27,7 @@
"libhidltransport",
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
+ "android.hardware.camera.provider@2.5",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.4",
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index 50ad7b2..de40990 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -36,6 +36,8 @@
filterDurations(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS);
filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
filterDurations(ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
+ filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS);
+ filterDurations(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS);
}
// TODO: filter request/result keys
}
@@ -174,6 +176,16 @@
filteredDurations.push_back(duration);
}
break;
+ case ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS:
+ case ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS:
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_HEIC;
+ filteredDurations.push_back(format);
+ filteredDurations.push_back(width);
+ filteredDurations.push_back(height);
+ filteredDurations.push_back(duration);
+ }
+ break;
default:
// Should not reach here
ALOGE("%s: Unkown tag 0x%x", __FUNCTION__, tag);
@@ -247,6 +259,31 @@
filteredDepthStreamConfigs.push_back(isInput);
}
mData.update(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS, filteredDepthStreamConfigs);
+
+ entry = mData.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS);
+ Vector<int32_t> filteredHeicStreamConfigs;
+ filteredHeicStreamConfigs.setCapacity(entry.count);
+
+ for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
+ int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
+ int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
+ int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
+ int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
+ if (isInput == ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT) {
+ // Hide input streams
+ continue;
+ }
+ // Translate HAL formats to NDK format
+ if (format == HAL_PIXEL_FORMAT_BLOB) {
+ format = AIMAGE_FORMAT_HEIC;
+ }
+
+ filteredHeicStreamConfigs.push_back(format);
+ filteredHeicStreamConfigs.push_back(width);
+ filteredHeicStreamConfigs.push_back(height);
+ filteredHeicStreamConfigs.push_back(isInput);
+ }
+ mData.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS, filteredHeicStreamConfigs);
}
bool
@@ -485,6 +522,8 @@
ANDROID_STATISTICS_INFO_SHARPNESS_MAP_SIZE,
ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION,
ANDROID_DEPTH_MAX_DEPTH_SAMPLES,
+ ANDROID_HEIC_INFO_SUPPORTED,
+ ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT,
});
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 69b9e7e..8c19e1d 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -71,6 +71,8 @@
ACAMERA_DEPTH,
ACAMERA_LOGICAL_MULTI_CAMERA,
ACAMERA_DISTORTION_CORRECTION,
+ ACAMERA_HEIC,
+ ACAMERA_HEIC_INFO,
ACAMERA_SECTION_COUNT,
ACAMERA_VENDOR = 0x8000
@@ -112,6 +114,8 @@
ACAMERA_DISTORTION_CORRECTION_START
= ACAMERA_DISTORTION_CORRECTION
<< 16,
+ ACAMERA_HEIC_START = ACAMERA_HEIC << 16,
+ ACAMERA_HEIC_INFO_START = ACAMERA_HEIC_INFO << 16,
ACAMERA_VENDOR_START = ACAMERA_VENDOR << 16
} acamera_metadata_section_start_t;
@@ -1912,6 +1916,7 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
+ * <p>This tag is also used for HEIC image capture.</p>
*/
ACAMERA_JPEG_GPS_COORDINATES = // double[3]
ACAMERA_JPEG_START,
@@ -1927,6 +1932,7 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
+ * <p>This tag is also used for HEIC image capture.</p>
*/
ACAMERA_JPEG_GPS_PROCESSING_METHOD = // byte
ACAMERA_JPEG_START + 1,
@@ -1942,6 +1948,7 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
+ * <p>This tag is also used for HEIC image capture.</p>
*/
ACAMERA_JPEG_GPS_TIMESTAMP = // int64
ACAMERA_JPEG_START + 2,
@@ -1986,6 +1993,10 @@
* </code></pre>
* <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will
* also be set to EXTERNAL. The above code is not relevant in such case.</p>
+ * <p>This tag is also used to describe the orientation of the HEIC image capture, in which
+ * case the rotation is reflected by
+ * <a href="https://developer.android.com/reference/android/media/ExifInterface.html#TAG_ORIENTATION">EXIF orientation flag</a>, and not by
+ * rotating the image data itself.</p>
*
* @see ACAMERA_SENSOR_ORIENTATION
*/
@@ -2003,7 +2014,8 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
- * <p>85-95 is typical usage range.</p>
+ * <p>85-95 is typical usage range. This tag is also used to describe the quality
+ * of the HEIC image capture.</p>
*/
ACAMERA_JPEG_QUALITY = // byte
ACAMERA_JPEG_START + 4,
@@ -2019,6 +2031,7 @@
* <li>ACaptureRequest</li>
* </ul></p>
*
+ * <p>This tag is also used to describe the quality of the HEIC image capture.</p>
*/
ACAMERA_JPEG_THUMBNAIL_QUALITY = // byte
ACAMERA_JPEG_START + 5,
@@ -2055,6 +2068,10 @@
* orientation is requested. LEGACY device will always report unrotated thumbnail
* size.</li>
* </ul>
+ * <p>The tag is also used as thumbnail size for HEIC image format capture, in which case the
+ * the thumbnail rotation is reflected by
+ * <a href="https://developer.android.com/reference/android/media/ExifInterface.html#TAG_ORIENTATION">EXIF orientation flag</a>, and not by
+ * rotating the thumbnail data itself.</p>
*
* @see ACAMERA_JPEG_ORIENTATION
*/
@@ -2088,6 +2105,7 @@
* and vice versa.</li>
* <li>All non-<code>(0, 0)</code> sizes will have non-zero widths and heights.</li>
* </ul>
+ * <p>This list is also used as supported thumbnail sizes for HEIC image format capture.</p>
*
* @see ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS
*/
@@ -5757,6 +5775,80 @@
ACAMERA_DISTORTION_CORRECTION_START + 1,
ACAMERA_DISTORTION_CORRECTION_END,
+ /**
+ * <p>The available HEIC (ISO/IEC 23008-12) stream
+ * configurations that this camera device supports
+ * (i.e. format, width, height, output/input stream).</p>
+ *
+ * <p>Type: int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>The configurations are listed as <code>(format, width, height, input?)</code> tuples.</p>
+ * <p>If the camera device supports HEIC image format, it will support identical set of stream
+ * combinations involving HEIC image format, compared to the combinations involving JPEG
+ * image format as required by the device's hardware level and capabilities.</p>
+ * <p>All the static, control, and dynamic metadata tags related to JPEG apply to HEIC formats.
+ * Configuring JPEG and HEIC streams at the same time is not supported.</p>
+ * <p>All the configuration tuples <code>(format, width, height, input?)</code> will contain
+ * AIMAGE_FORMAT_HEIC format as OUTPUT only.</p>
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS = // int32[n*4] (acamera_metadata_enum_android_heic_available_heic_stream_configurations_t)
+ ACAMERA_HEIC_START,
+ /**
+ * <p>This lists the minimum frame duration for each
+ * format/size combination for HEIC output formats.</p>
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>This should correspond to the frame duration when only that
+ * stream is active, with all processing (typically in android.*.mode)
+ * set to either OFF or FAST.</p>
+ * <p>When multiple streams are used in a request, the minimum frame
+ * duration will be max(individual stream min durations).</p>
+ * <p>See ACAMERA_SENSOR_FRAME_DURATION and
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for more details about
+ * calculating the max frame rate.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS
+ * @see ACAMERA_SENSOR_FRAME_DURATION
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS = // int64[4*n]
+ ACAMERA_HEIC_START + 1,
+ /**
+ * <p>This lists the maximum stall duration for each
+ * output format/size combination for HEIC streams.</p>
+ *
+ * <p>Type: int64[4*n]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ * <p>A stall duration is how much extra time would get added
+ * to the normal minimum frame duration for a repeating request
+ * that has streams with non-zero stall.</p>
+ * <p>This functions similarly to
+ * ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS for HEIC
+ * streams.</p>
+ * <p>All HEIC output stream formats may have a nonzero stall
+ * duration.</p>
+ *
+ * @see ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS
+ */
+ ACAMERA_HEIC_AVAILABLE_HEIC_STALL_DURATIONS = // int64[4*n]
+ ACAMERA_HEIC_START + 2,
+ ACAMERA_HEIC_END,
+
} acamera_metadata_tag_t;
/**
@@ -8373,6 +8465,16 @@
} acamera_metadata_enum_android_distortion_correction_mode_t;
+// ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS
+typedef enum acamera_metadata_enum_acamera_heic_available_heic_stream_configurations {
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_OUTPUT = 0,
+
+ ACAMERA_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_INPUT = 1,
+
+} acamera_metadata_enum_android_heic_available_heic_stream_configurations_t;
+
+
+
#endif /* __ANDROID_API__ >= 24 */
__END_DECLS
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 0357115..fb6edb6 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -597,6 +597,9 @@
LEVEL_AVC_5, ///< AVC (H.264) Level 5
LEVEL_AVC_5_1, ///< AVC (H.264) Level 5.1
LEVEL_AVC_5_2, ///< AVC (H.264) Level 5.2
+ LEVEL_AVC_6, ///< AVC (H.264) Level 6
+ LEVEL_AVC_6_1, ///< AVC (H.264) Level 6.1
+ LEVEL_AVC_6_2, ///< AVC (H.264) Level 6.2
// HEVC (H.265) tiers and levels
LEVEL_HEVC_MAIN_1 = _C2_PL_HEVC_BASE, ///< HEVC (H.265) Main Tier Level 1
diff --git a/media/codec2/hidl/1.0/utils/Configurable.cpp b/media/codec2/hidl/1.0/utils/Configurable.cpp
index a35b74c..ec9c170 100644
--- a/media/codec2/hidl/1.0/utils/Configurable.cpp
+++ b/media/codec2/hidl/1.0/utils/Configurable.cpp
@@ -171,17 +171,15 @@
c2fields,
mayBlock ? C2_MAY_BLOCK : C2_DONT_BLOCK);
hidl_vec<FieldSupportedValuesQueryResult> outFields(inFields.size());
- {
- size_t ix = 0;
- for (const C2FieldSupportedValuesQuery &result : c2fields) {
- if (!objcpy(&outFields[ix], result)) {
- ++ix;
- } else {
- outFields.resize(ix);
- c2res = C2_CORRUPTED;
- LOG(WARNING) << "querySupportedValues -- invalid output params.";
- break;
- }
+ size_t dstIx = 0;
+ for (const C2FieldSupportedValuesQuery &result : c2fields) {
+ if (objcpy(&outFields[dstIx], result)) {
+ ++dstIx;
+ } else {
+ outFields.resize(dstIx);
+ c2res = C2_CORRUPTED;
+ LOG(WARNING) << "querySupportedValues -- invalid output params.";
+ break;
}
}
_hidl_cb((Status)c2res, outFields);
diff --git a/media/codec2/sfplugin/SkipCutBuffer.cpp b/media/codec2/sfplugin/SkipCutBuffer.cpp
index 5762440..8d1de65 100644
--- a/media/codec2/sfplugin/SkipCutBuffer.cpp
+++ b/media/codec2/sfplugin/SkipCutBuffer.cpp
@@ -20,7 +20,7 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/SkipCutBuffer.h>
+#include "SkipCutBuffer.h"
namespace android {
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index c369e16..0a6a717 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -65,7 +65,9 @@
{ C2Config::LEVEL_AVC_5, AVCLevel5 },
{ C2Config::LEVEL_AVC_5_1, AVCLevel51 },
{ C2Config::LEVEL_AVC_5_2, AVCLevel52 },
-
+ { C2Config::LEVEL_AVC_6, AVCLevel6 },
+ { C2Config::LEVEL_AVC_6_1, AVCLevel61 },
+ { C2Config::LEVEL_AVC_6_2, AVCLevel62 },
};
ALookup<C2Config::profile_t, int32_t> sAvcProfiles = {
diff --git a/media/codec2/vndk/C2Config.cpp b/media/codec2/vndk/C2Config.cpp
index 8a27088..34680a7 100644
--- a/media/codec2/vndk/C2Config.cpp
+++ b/media/codec2/vndk/C2Config.cpp
@@ -186,6 +186,9 @@
{ "avc-5", C2Config::LEVEL_AVC_5 },
{ "avc-5.1", C2Config::LEVEL_AVC_5_1 },
{ "avc-5.2", C2Config::LEVEL_AVC_5_2 },
+ { "avc-6", C2Config::LEVEL_AVC_6 },
+ { "avc-6.1", C2Config::LEVEL_AVC_6_1 },
+ { "avc-6.2", C2Config::LEVEL_AVC_6_2 },
{ "hevc-main-1", C2Config::LEVEL_HEVC_MAIN_1 },
{ "hevc-main-2", C2Config::LEVEL_HEVC_MAIN_2 },
{ "hevc-main-2.1", C2Config::LEVEL_HEVC_MAIN_2_1 },
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index ac54116..0243c03 100755
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -6214,6 +6214,7 @@
static const char *extensions[] = {
"3g2",
+ "3ga",
"3gp",
"3gpp",
"3gpp2",
@@ -6222,6 +6223,7 @@
"m4v",
"mov",
"mp4",
+ "qt",
NULL
};
diff --git a/media/libaaudio/examples/loopback/src/loopback.cpp b/media/libaaudio/examples/loopback/src/loopback.cpp
index 75d425f..6578156 100644
--- a/media/libaaudio/examples/loopback/src/loopback.cpp
+++ b/media/libaaudio/examples/loopback/src/loopback.cpp
@@ -36,8 +36,11 @@
#include "LoopbackAnalyzer.h"
#include "../../utils/AAudioExampleUtils.h"
-// V0.4.00 = rectify and low-pass filter the echos, use auto-correlation on entire echo
-#define APP_VERSION "0.4.00"
+// V0.4.00 = rectify and low-pass filter the echos, auto-correlate entire echo
+// V0.4.01 = add -h hang option
+// fix -n option to set output buffer for -tm
+// plot first glitch
+#define APP_VERSION "0.4.01"
// Tag for machine readable results as property = value pairs
#define RESULT_TAG "RESULT: "
@@ -396,7 +399,7 @@
int32_t requestedInputCapacity = AAUDIO_UNSPECIFIED;
aaudio_performance_mode_t inputPerformanceLevel = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
- int32_t outputFramesPerBurst = 0;
+ int32_t outputFramesPerBurst = 0;
aaudio_format_t actualOutputFormat = AAUDIO_FORMAT_INVALID;
int32_t actualSampleRate = 0;
@@ -476,6 +479,8 @@
int32_t timeMillis = 0;
int32_t recordingDuration = std::min(60 * 5, requestedDuration);
+ int32_t requestedOutputBursts = argParser.getNumberOfBursts();
+
switch(testMode) {
case TEST_SINE_MAGNITUDE:
loopbackData.loopbackProcessor = &loopbackData.sineAnalyzer;
@@ -541,20 +546,19 @@
}
inputStream = loopbackData.inputStream = recorder.getStream();
- argParser.compareWithStream(inputStream);
-
{
int32_t actualCapacity = AAudioStream_getBufferCapacityInFrames(inputStream);
(void) AAudioStream_setBufferSizeInFrames(inputStream, actualCapacity);
- if (testMode == TEST_SINE_MAGNITUDE) {
+ if (testMode == TEST_SINE_MAGNITUDE
+ && requestedOutputBursts == AAUDIO_UNSPECIFIED) {
result = AAudioStream_setBufferSizeInFrames(outputStream, actualCapacity);
if (result < 0) {
fprintf(stderr, "ERROR - AAudioStream_setBufferSizeInFrames(output) returned %d\n",
result);
goto finish;
} else {
- printf("Output buffer size set to match input capacity = %d frames.\n", result);
+ printf("Output buffer size set to match input capacity = %d frames!\n", result);
}
}
@@ -565,6 +569,8 @@
}
}
+ argParser.compareWithStream(inputStream);
+
// ------- Setup loopbackData -----------------------------
loopbackData.actualInputFormat = AAudioStream_getFormat(inputStream);
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index 1645986..4373fa9 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -32,8 +32,6 @@
// Arbitrary period for glitches
#define FORCED_UNDERRUN_PERIOD_FRAMES (2 * 48000)
-// How long to sleep in a callback to cause an intentional glitch. For testing.
-#define FORCED_UNDERRUN_SLEEP_MICROS (10 * 1000)
#define MAX_TIMESTAMPS 16
@@ -275,7 +273,7 @@
int scheduler = 0;
bool schedulerChecked = false;
- bool forceUnderruns = false;
+ int32_t hangTimeMSec = 0;
AAudioSimplePlayer simplePlayer;
int32_t callbackCount = 0;
@@ -327,10 +325,12 @@
sineData->setupSineSweeps();
}
- if (sineData->forceUnderruns) {
+ if (sineData->hangTimeMSec > 0) {
if (sineData->framesTotal > sineData->nextFrameToGlitch) {
- usleep(FORCED_UNDERRUN_SLEEP_MICROS);
- printf("Simulate glitch at %lld\n", (long long) sineData->framesTotal);
+ usleep(sineData->hangTimeMSec * 1000);
+ printf("Hang callback at %lld frames for %d msec\n",
+ (long long) sineData->framesTotal,
+ sineData->hangTimeMSec);
sineData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
}
}
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 7a48153..2b05f10 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -26,11 +26,14 @@
#include <string.h>
#include <time.h>
#include <aaudio/AAudio.h>
+
#include "AAudioExampleUtils.h"
#include "AAudioSimplePlayer.h"
#include "AAudioArgsParser.h"
-#define APP_VERSION "0.1.5"
+#define APP_VERSION "0.1.6"
+
+constexpr int32_t kDefaultHangTimeMSec = 10;
/**
* Open stream, play some sine waves, then close the stream.
@@ -41,7 +44,7 @@
static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser,
int32_t loopCount,
int32_t prefixToneMsec,
- bool forceUnderruns)
+ int32_t hangTimeMSec)
{
SineThreadedData_t myData;
AAudioSimplePlayer &player = myData.simplePlayer;
@@ -53,10 +56,12 @@
printf("----------------------- run complete test --------------------------\n");
myData.schedulerChecked = false;
myData.callbackCount = 0;
- myData.forceUnderruns = forceUnderruns; // test AAudioStream_getXRunCount()
+ myData.hangTimeMSec = hangTimeMSec; // test AAudioStream_getXRunCount()
result = player.open(argParser,
- SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
+ SimplePlayerDataCallbackProc,
+ SimplePlayerErrorCallbackProc,
+ &myData);
if (result != AAUDIO_OK) {
fprintf(stderr, "ERROR - player.open() returned %s\n",
AAudio_convertResultToText(result));
@@ -115,12 +120,17 @@
int64_t millis =
(getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
result = myData.waker.get();
+ const int32_t framesWritten = (int32_t) AAudioStream_getFramesWritten(player.getStream());
+ const int32_t framesRead = (int32_t) AAudioStream_getFramesRead(player.getStream());
+ const int32_t xruns = AAudioStream_getXRunCount(player.getStream());
printf(" waker result = %d, at %6d millis"
- ", second = %3d, framesWritten = %8d, underruns = %d\n",
+ ", second = %3d, frames written %8d - read %8d = %8d, underruns = %d\n",
result, (int) millis,
second,
- (int) AAudioStream_getFramesWritten(player.getStream()),
- (int) AAudioStream_getXRunCount(player.getStream()));
+ framesWritten,
+ framesRead,
+ framesWritten - framesRead,
+ xruns);
if (result != AAUDIO_OK) {
disconnected = (result == AAUDIO_ERROR_DISCONNECTED);
bailOut = true;
@@ -210,7 +220,9 @@
AAudioArgsParser::usage();
printf(" -l{count} loopCount start/stop, every other one is silent\n");
printf(" -t{msec} play a high pitched tone at the beginning\n");
- printf(" -z force periodic underruns by sleeping in callback\n");
+ printf(" -h{msec} force periodic underruns by hanging in callback\n");
+ printf(" If no value specified then %d used.\n",
+ kDefaultHangTimeMSec);
}
int main(int argc, const char **argv)
@@ -219,13 +231,14 @@
aaudio_result_t result;
int32_t loopCount = 1;
int32_t prefixToneMsec = 0;
- bool forceUnderruns = false;
+ int32_t hangTimeMSec = 0;
// Make printf print immediately so that debug info is not stuck
// in a buffer if we hang or crash.
setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
- printf("%s - Play a sine sweep using an AAudio callback V%s\n", argv[0], APP_VERSION);
+ printf("%s - Play a sine sweep using an AAudio callback V%s\n",
+ argv[0], APP_VERSION);
for (int i = 1; i < argc; i++) {
const char *arg = argv[i];
@@ -240,8 +253,10 @@
case 't':
prefixToneMsec = atoi(&arg[2]);
break;
- case 'z':
- forceUnderruns = true; // Zzzzzzz
+ case 'h':
+ hangTimeMSec = (arg[2]) // value specified?
+ ? atoi(&arg[2])
+ : kDefaultHangTimeMSec;
break;
default:
usage();
@@ -257,7 +272,8 @@
}
// Keep looping until we can complete the test without disconnecting.
- while((result = testOpenPlayClose(argParser, loopCount, prefixToneMsec, forceUnderruns))
+ while((result = testOpenPlayClose(argParser, loopCount,
+ prefixToneMsec, hangTimeMSec))
== AAUDIO_ERROR_DISCONNECTED);
return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 8afb1cc..baa1469 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1361,14 +1361,12 @@
ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
return BAD_VALUE;
}
- {
- AutoMutex lock(mLock);
- if (mDeviceCallback.unsafe_get() != callback.get()) {
- ALOGW("%s(%d): removing different callback!", __func__, mPortId);
- return INVALID_OPERATION;
- }
- mDeviceCallback.clear();
+ AutoMutex lock(mLock);
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
+ ALOGW("%s(%d): removing different callback!", __func__, mPortId);
+ return INVALID_OPERATION;
}
+ mDeviceCallback.clear();
if (mInput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mInput);
}
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 52ad5a6..01d9b3d 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -522,11 +522,12 @@
if (ioDesc == 0 || ioDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) return;
audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
- AudioDeviceCallbacks callbacks;
- bool deviceValidOrChanged = false;
- Mutex::Autolock _l(mCallbacksLock);
+ Vector<sp<AudioDeviceCallback>> callbacksToCall;
{
Mutex::Autolock _l(mLock);
+ bool deviceValidOrChanged = false;
+ bool sendCallbacks = false;
+ ssize_t ioIndex = -1;
switch (event) {
case AUDIO_OUTPUT_OPENED:
@@ -544,17 +545,16 @@
if (ioDesc->getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
deviceId = ioDesc->getDeviceId();
if (event == AUDIO_OUTPUT_OPENED || event == AUDIO_INPUT_OPENED) {
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
+ ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
if (ioIndex >= 0) {
- callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ sendCallbacks = true;
deviceValidOrChanged = true;
}
}
if (event == AUDIO_OUTPUT_REGISTERED || event == AUDIO_INPUT_REGISTERED) {
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
- if ((ioIndex >= 0) && !mAudioDeviceCallbacks.valueAt(ioIndex).notifiedOnce()) {
- callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
- }
+ ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
+ sendCallbacks = (ioIndex >= 0)
+ && !mAudioDeviceCallbackProxies.valueAt(ioIndex).notifiedOnce();
}
}
ALOGV("ioConfigChanged() new %s %s %d samplingRate %u, format %#x channel mask %#x "
@@ -577,7 +577,7 @@
event == AUDIO_OUTPUT_CLOSED ? "output" : "input", ioDesc->mIoHandle);
mIoDescriptors.removeItem(ioDesc->mIoHandle);
- mAudioDeviceCallbacks.removeItem(ioDesc->mIoHandle);
+ mAudioDeviceCallbackProxies.removeItem(ioDesc->mIoHandle);
} break;
case AUDIO_OUTPUT_CONFIG_CHANGED:
@@ -594,10 +594,8 @@
if (deviceId != ioDesc->getDeviceId()) {
deviceValidOrChanged = true;
deviceId = ioDesc->getDeviceId();
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(ioDesc->mIoHandle);
- if (ioIndex >= 0) {
- callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
- }
+ ioIndex = mAudioDeviceCallbackProxies.indexOfKey(ioDesc->mIoHandle);
+ sendCallbacks = ioIndex >= 0;
}
ALOGV("ioConfigChanged() new config for %s %d samplingRate %u, format %#x "
"channel mask %#x frameCount %zu frameCountHAL %zu deviceId %d",
@@ -608,30 +606,34 @@
} break;
}
- }
- // callbacks.size() != 0 => ioDesc->mIoHandle and deviceId are valid
- if (callbacks.size() != 0) {
- for (size_t i = 0; i < callbacks.size(); ) {
- sp<AudioDeviceCallback> callback = callbacks[i].promote();
- if (callback.get() != nullptr) {
- // Call the callback only if the device actually changed, the input or output was
- // opened or closed or the client was newly registered and the callback was never
- // called
- if (!callback->notifiedOnce() || deviceValidOrChanged) {
- // Must be called without mLock held. May lead to dead lock if calling for
- // example getRoutedDevice that updates the device and tries to acquire mLock.
- callback->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
- callback->setNotifiedOnce();
+
+ // sendCallbacks true => ioDesc->mIoHandle and deviceId are valid
+ if (sendCallbacks) {
+ AudioDeviceCallbackProxies &callbackProxies =
+ mAudioDeviceCallbackProxies.editValueAt(ioIndex);
+ for (size_t i = 0; i < callbackProxies.size(); ) {
+ sp<AudioDeviceCallback> callback = callbackProxies[i]->callback();
+ if (callback.get() != nullptr) {
+ // Call the callback only if the device actually changed, the input or output
+ // was opened or closed or the client was newly registered and the callback
+ // was never called
+ if (!callbackProxies[i]->notifiedOnce() || deviceValidOrChanged) {
+ callbacksToCall.add(callback);
+ callbackProxies[i]->setNotifiedOnce();
+ }
+ i++;
+ } else {
+ callbackProxies.removeAt(i);
}
- i++;
- } else {
- callbacks.removeAt(i);
}
+ callbackProxies.setNotifiedOnce();
}
- callbacks.setNotifiedOnce();
- // clean up callback list while we are here if some clients have disappeared without
- // unregistering their callback, or if cb was served for the first time since registered
- mAudioDeviceCallbacks.replaceValueFor(ioDesc->mIoHandle, callbacks);
+ }
+
+ // Callbacks must be called without mLock held. May lead to dead lock if calling for
+ // example getRoutedDevice that updates the device and tries to acquire mLock.
+ for (size_t i = 0; i < callbacksToCall.size(); i++) {
+ callbacksToCall[i]->onAudioDeviceUpdate(ioDesc->mIoHandle, deviceId);
}
}
@@ -686,48 +688,49 @@
status_t AudioSystem::AudioFlingerClient::addAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
- Mutex::Autolock _l(mCallbacksLock);
- AudioDeviceCallbacks callbacks;
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+ Mutex::Autolock _l(mLock);
+ AudioDeviceCallbackProxies callbackProxies;
+ ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo);
if (ioIndex >= 0) {
- callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
+ callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex);
}
- for (size_t cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
- if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) {
+ for (size_t cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) {
+ sp<AudioDeviceCallback> cbk = callbackProxies[cbIndex]->callback();
+ if (cbk.get() == callback.unsafe_get()) {
return INVALID_OPERATION;
}
}
- callbacks.add(callback);
- callbacks.resetNotifiedOnce();
- mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+ callbackProxies.add(new AudioDeviceCallbackProxy(callback));
+ callbackProxies.resetNotifiedOnce();
+ mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies);
return NO_ERROR;
}
status_t AudioSystem::AudioFlingerClient::removeAudioDeviceCallback(
const wp<AudioDeviceCallback>& callback, audio_io_handle_t audioIo)
{
- Mutex::Autolock _l(mCallbacksLock);
- ssize_t ioIndex = mAudioDeviceCallbacks.indexOfKey(audioIo);
+ Mutex::Autolock _l(mLock);
+ ssize_t ioIndex = mAudioDeviceCallbackProxies.indexOfKey(audioIo);
if (ioIndex < 0) {
return INVALID_OPERATION;
}
- AudioDeviceCallbacks callbacks = mAudioDeviceCallbacks.valueAt(ioIndex);
-
+ AudioDeviceCallbackProxies callbackProxies = mAudioDeviceCallbackProxies.valueAt(ioIndex);
size_t cbIndex;
- for (cbIndex = 0; cbIndex < callbacks.size(); cbIndex++) {
- if (callbacks[cbIndex].unsafe_get() == callback.unsafe_get()) {
+ for (cbIndex = 0; cbIndex < callbackProxies.size(); cbIndex++) {
+ sp<AudioDeviceCallback> cbk = callbackProxies[cbIndex]->callback();
+ if (cbk.get() == callback.unsafe_get()) {
break;
}
}
- if (cbIndex == callbacks.size()) {
+ if (cbIndex == callbackProxies.size()) {
return INVALID_OPERATION;
}
- callbacks.removeAt(cbIndex);
- if (callbacks.size() != 0) {
- mAudioDeviceCallbacks.replaceValueFor(audioIo, callbacks);
+ callbackProxies.removeAt(cbIndex);
+ if (callbackProxies.size() != 0) {
+ mAudioDeviceCallbackProxies.replaceValueFor(audioIo, callbackProxies);
} else {
- mAudioDeviceCallbacks.removeItem(audioIo);
+ mAudioDeviceCallbackProxies.removeItem(audioIo);
}
return NO_ERROR;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index b5a7ebe..7881bb8 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -2959,14 +2959,12 @@
ALOGW("%s(%d): removing NULL callback!", __func__, mPortId);
return BAD_VALUE;
}
- {
- AutoMutex lock(mLock);
- if (mDeviceCallback.unsafe_get() != callback.get()) {
- ALOGW("%s removing different callback!", __FUNCTION__);
- return INVALID_OPERATION;
- }
- mDeviceCallback.clear();
+ AutoMutex lock(mLock);
+ if (mDeviceCallback.unsafe_get() != callback.get()) {
+ ALOGW("%s removing different callback!", __FUNCTION__);
+ return INVALID_OPERATION;
}
+ mDeviceCallback.clear();
if (mOutput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mOutput);
}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 3df49e6..b9ee24a 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -399,15 +399,28 @@
virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo,
audio_port_handle_t deviceId) = 0;
- bool notifiedOnce() const { return mNotifiedOnce; }
- void setNotifiedOnce() { mNotifiedOnce = true; }
+ };
+
+ class AudioDeviceCallbackProxy : public RefBase
+ {
+ public:
+
+ AudioDeviceCallbackProxy(wp<AudioDeviceCallback> callback)
+ : mCallback(callback) {}
+ ~AudioDeviceCallbackProxy() override {}
+
+ sp<AudioDeviceCallback> callback() const { return mCallback.promote(); };
+
+ bool notifiedOnce() const { return mNotifiedOnce; }
+ void setNotifiedOnce() { mNotifiedOnce = true; }
private:
- /**
- * @brief mNotifiedOnce it forces the callback to be called at least once when
- * registered with a VALID AudioDevice, and allows not to flood other listeners
- * on this iohandle that already know the valid device.
- */
- bool mNotifiedOnce = false;
+ /**
+ * @brief mNotifiedOnce it forces the callback to be called at least once when
+ * registered with a VALID AudioDevice, and allows not to flood other listeners
+ * on this iohandle that already know the valid device.
+ */
+ bool mNotifiedOnce = false;
+ wp<AudioDeviceCallback> mCallback;
};
static status_t addAudioDeviceCallback(const wp<AudioDeviceCallback>& callback,
@@ -454,7 +467,7 @@
Mutex mLock;
DefaultKeyedVector<audio_io_handle_t, sp<AudioIoDescriptor> > mIoDescriptors;
- class AudioDeviceCallbacks : public Vector<wp<AudioDeviceCallback>>
+ class AudioDeviceCallbackProxies : public Vector<sp<AudioDeviceCallbackProxy>>
{
public:
/**
@@ -472,8 +485,8 @@
*/
bool mNotifiedOnce = false;
};
- Mutex mCallbacksLock; // prevents race on Callbacks
- DefaultKeyedVector<audio_io_handle_t, AudioDeviceCallbacks> mAudioDeviceCallbacks;
+ DefaultKeyedVector<audio_io_handle_t, AudioDeviceCallbackProxies>
+ mAudioDeviceCallbackProxies;
// cached values for recording getInputBufferSize() queries
size_t mInBuffSize; // zero indicates cache is invalid
uint32_t mInSamplingRate;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 9a1ac53..5853e4b 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -21,6 +21,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: ["AudioParameter.cpp", "TypeConverter.cpp"],
cflags: [
"-Werror",
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 6d2329f..52cb5fa 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -4298,24 +4298,27 @@
int maxDimension = max(width, height);
static const int limits[][5] = {
- /* MBps MB dim bitrate level */
- { 1485, 99, 28, 64, OMX_VIDEO_AVCLevel1 },
- { 1485, 99, 28, 128, OMX_VIDEO_AVCLevel1b },
- { 3000, 396, 56, 192, OMX_VIDEO_AVCLevel11 },
- { 6000, 396, 56, 384, OMX_VIDEO_AVCLevel12 },
- { 11880, 396, 56, 768, OMX_VIDEO_AVCLevel13 },
- { 11880, 396, 56, 2000, OMX_VIDEO_AVCLevel2 },
- { 19800, 792, 79, 4000, OMX_VIDEO_AVCLevel21 },
- { 20250, 1620, 113, 4000, OMX_VIDEO_AVCLevel22 },
- { 40500, 1620, 113, 10000, OMX_VIDEO_AVCLevel3 },
- { 108000, 3600, 169, 14000, OMX_VIDEO_AVCLevel31 },
- { 216000, 5120, 202, 20000, OMX_VIDEO_AVCLevel32 },
- { 245760, 8192, 256, 20000, OMX_VIDEO_AVCLevel4 },
- { 245760, 8192, 256, 50000, OMX_VIDEO_AVCLevel41 },
- { 522240, 8704, 263, 50000, OMX_VIDEO_AVCLevel42 },
- { 589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5 },
- { 983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 },
- { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 },
+ /* MBps MB dim bitrate level */
+ { 1485, 99, 28, 64, OMX_VIDEO_AVCLevel1 },
+ { 1485, 99, 28, 128, OMX_VIDEO_AVCLevel1b },
+ { 3000, 396, 56, 192, OMX_VIDEO_AVCLevel11 },
+ { 6000, 396, 56, 384, OMX_VIDEO_AVCLevel12 },
+ { 11880, 396, 56, 768, OMX_VIDEO_AVCLevel13 },
+ { 11880, 396, 56, 2000, OMX_VIDEO_AVCLevel2 },
+ { 19800, 792, 79, 4000, OMX_VIDEO_AVCLevel21 },
+ { 20250, 1620, 113, 4000, OMX_VIDEO_AVCLevel22 },
+ { 40500, 1620, 113, 10000, OMX_VIDEO_AVCLevel3 },
+ { 108000, 3600, 169, 14000, OMX_VIDEO_AVCLevel31 },
+ { 216000, 5120, 202, 20000, OMX_VIDEO_AVCLevel32 },
+ { 245760, 8192, 256, 20000, OMX_VIDEO_AVCLevel4 },
+ { 245760, 8192, 256, 50000, OMX_VIDEO_AVCLevel41 },
+ { 522240, 8704, 263, 50000, OMX_VIDEO_AVCLevel42 },
+ { 589824, 22080, 420, 135000, OMX_VIDEO_AVCLevel5 },
+ { 983040, 36864, 543, 240000, OMX_VIDEO_AVCLevel51 },
+ { 2073600, 36864, 543, 240000, OMX_VIDEO_AVCLevel52 },
+ { 4177920, 139264, 1055, 240000, OMX_VIDEO_AVCLevel6 },
+ { 8355840, 139264, 1055, 480000, OMX_VIDEO_AVCLevel61 },
+ { 16711680, 139264, 1055, 800000, OMX_VIDEO_AVCLevel62 },
};
for (size_t i = 0; i < ARRAY_SIZE(limits); i++) {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 2e7da01..82f7026 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -192,6 +192,9 @@
{ 50, OMX_VIDEO_AVCLevel5 },
{ 51, OMX_VIDEO_AVCLevel51 },
{ 52, OMX_VIDEO_AVCLevel52 },
+ { 60, OMX_VIDEO_AVCLevel6 },
+ { 61, OMX_VIDEO_AVCLevel61 },
+ { 62, OMX_VIDEO_AVCLevel62 },
};
const static ALookup<uint8_t, OMX_VIDEO_AVCPROFILETYPE> profiles {
{ 66, OMX_VIDEO_AVCProfileBaseline },
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index 362b7f5..4383004 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -87,6 +87,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: ["OMXUtils.cpp"],
export_include_dirs: [
"include",
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index bebfb3b..819058c 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -10,6 +10,7 @@
vndk: {
enabled: true,
},
+ double_loadable: true,
srcs: [
"MediaCodecsXmlParser.cpp",
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 010c1aa..c3eb437 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -69,6 +69,7 @@
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
case AIMAGE_FORMAT_Y8:
+ case AIMAGE_FORMAT_HEIC:
return true;
case AIMAGE_FORMAT_PRIVATE:
// For private format, cpu usage is prohibited.
@@ -96,6 +97,7 @@
case AIMAGE_FORMAT_DEPTH16:
case AIMAGE_FORMAT_DEPTH_POINT_CLOUD:
case AIMAGE_FORMAT_Y8:
+ case AIMAGE_FORMAT_HEIC:
return 1;
case AIMAGE_FORMAT_PRIVATE:
return 0;
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index 15b340c..14d88cb 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -526,7 +526,15 @@
* (in bytes) between adjacent rows.</p>
*
*/
- AIMAGE_FORMAT_Y8 = 0x20203859
+ AIMAGE_FORMAT_Y8 = 0x20203859,
+
+ /**
+ * Compressed HEIC format.
+ *
+ * <p>This format defines the HEIC brand of High Efficiency Image File
+ * Format as described in ISO/IEC 23008-12.</p>
+ */
+ AIMAGE_FORMAT_HEIC = 0x48454946,
};
/**
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7733071..2d80bd8 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3277,9 +3277,13 @@
}
// look for the thread where the specified audio session is present
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) {
+ uint32_t sessionType = mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId);
+ if (sessionType != 0) {
io = mPlaybackThreads.keyAt(i);
- break;
+ // thread with same effect session is preferable
+ if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) {
+ break;
+ }
}
}
if (io == AUDIO_IO_HANDLE_NONE) {
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index e78c98b..c5b9953 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -139,6 +139,75 @@
}
}
+void FastMixer::updateMixerTrack(int index, Reason reason) {
+ const FastMixerState * const current = (const FastMixerState *) mCurrent;
+ const FastTrack * const fastTrack = ¤t->mFastTracks[index];
+
+ // check and update generation
+ if (reason == REASON_MODIFY && mGenerations[index] == fastTrack->mGeneration) {
+ return; // no change on an already configured track.
+ }
+ mGenerations[index] = fastTrack->mGeneration;
+
+ // mMixer == nullptr on configuration failure (check done after generation update).
+ if (mMixer == nullptr) {
+ return;
+ }
+
+ switch (reason) {
+ case REASON_REMOVE:
+ mMixer->destroy(index);
+ break;
+ case REASON_ADD: {
+ const status_t status = mMixer->create(
+ index, fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
+ LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+ "%s: cannot create fast track index"
+ " %d, mask %#x, format %#x in AudioMixer",
+ __func__, index, fastTrack->mChannelMask, fastTrack->mFormat);
+ }
+ [[fallthrough]]; // now fallthrough to update the newly created track.
+ case REASON_MODIFY:
+ mMixer->setBufferProvider(index, fastTrack->mBufferProvider);
+
+ float vlf, vrf;
+ if (fastTrack->mVolumeProvider != nullptr) {
+ const gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+ vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
+ vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
+ } else {
+ vlf = vrf = AudioMixer::UNITY_GAIN_FLOAT;
+ }
+
+ // set volume to avoid ramp whenever the track is updated (or created).
+ // Note: this does not distinguish from starting fresh or
+ // resuming from a paused state.
+ mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
+ mMixer->setParameter(index, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+
+ mMixer->setParameter(index, AudioMixer::RESAMPLE, AudioMixer::REMOVE, nullptr);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+ (void *)mMixerBuffer);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_FORMAT,
+ (void *)(uintptr_t)mMixerBufferFormat);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::FORMAT,
+ (void *)(uintptr_t)fastTrack->mFormat);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
+ (void *)(uintptr_t)fastTrack->mChannelMask);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
+ (void *)(uintptr_t)mSinkChannelMask);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
+ (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
+ mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
+ (void *)(uintptr_t)fastTrack->mHapticIntensity);
+
+ mMixer->enable(index);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("%s: invalid update reason %d", __func__, reason);
+ }
+}
+
void FastMixer::onStateChange()
{
const FastMixerState * const current = (const FastMixerState *) mCurrent;
@@ -240,21 +309,16 @@
// check for change in active track set
const unsigned currentTrackMask = current->mTrackMask;
dumpState->mTrackMask = currentTrackMask;
+ dumpState->mNumTracks = popcount(currentTrackMask);
if (current->mFastTracksGen != mFastTracksGen) {
- ALOG_ASSERT(mMixerBuffer != NULL);
// process removed tracks first to avoid running out of track names
unsigned removedTracks = previousTrackMask & ~currentTrackMask;
while (removedTracks != 0) {
int i = __builtin_ctz(removedTracks);
removedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- ALOG_ASSERT(fastTrack->mBufferProvider == NULL);
- if (mMixer != NULL) {
- mMixer->destroy(i);
- }
+ updateMixerTrack(i, REASON_REMOVE);
// don't reset track dump state, since other side is ignoring it
- mGenerations[i] = fastTrack->mGeneration;
}
// now process added tracks
@@ -262,40 +326,7 @@
while (addedTracks != 0) {
int i = __builtin_ctz(addedTracks);
addedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
- if (mMixer != NULL) {
- const int name = i; // for clarity, choose name as fast track index.
- status_t status = mMixer->create(
- name,
- fastTrack->mChannelMask,
- fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
- "%s: cannot create track name"
- " %d, mask %#x, format %#x, sessionId %d in AudioMixer",
- __func__, name,
- fastTrack->mChannelMask, fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX);
- mMixer->setBufferProvider(name, bufferProvider);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
- (void *)mMixerBuffer);
- // newly allocated track names default to full scale volume
- mMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
- (void *)(uintptr_t)fastTrack->mFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *)(uintptr_t)fastTrack->mChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
- (void *)(uintptr_t)mSinkChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
- (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
- (void *)(uintptr_t)fastTrack->mHapticIntensity);
- mMixer->enable(name);
- }
- mGenerations[i] = fastTrack->mGeneration;
+ updateMixerTrack(i, REASON_ADD);
}
// finally process (potentially) modified tracks; these use the same slot
@@ -304,44 +335,10 @@
while (modifiedTracks != 0) {
int i = __builtin_ctz(modifiedTracks);
modifiedTracks &= ~(1 << i);
- const FastTrack* fastTrack = ¤t->mFastTracks[i];
- if (fastTrack->mGeneration != mGenerations[i]) {
- // this track was actually modified
- AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
- ALOG_ASSERT(bufferProvider != NULL);
- if (mMixer != NULL) {
- const int name = i;
- mMixer->setBufferProvider(name, bufferProvider);
- if (fastTrack->mVolumeProvider == NULL) {
- float f = AudioMixer::UNITY_GAIN_FLOAT;
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f);
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f);
- }
- mMixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::REMOVE, NULL);
- mMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
- (void *)(uintptr_t)fastTrack->mFormat);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *)(uintptr_t)fastTrack->mChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK,
- (void *)(uintptr_t)mSinkChannelMask);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
- (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
- mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
- (void *)(uintptr_t)fastTrack->mHapticIntensity);
- // already enabled
- }
- mGenerations[i] = fastTrack->mGeneration;
- }
+ updateMixerTrack(i, REASON_MODIFY);
}
mFastTracksGen = current->mFastTracksGen;
-
- dumpState->mNumTracks = popcount(currentTrackMask);
}
}
@@ -408,8 +405,8 @@
float vlf = float_from_gain(gain_minifloat_unpack_left(vlr));
float vrf = float_from_gain(gain_minifloat_unpack_right(vlr));
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf);
- mMixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf);
+ mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &vlf);
+ mMixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &vrf);
}
// FIXME The current implementation of framesReady() for fast tracks
// takes a tryLock, which can block
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index c31d476..97ab635 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -59,6 +59,14 @@
virtual void onStateChange();
virtual void onWork();
+ enum Reason {
+ REASON_REMOVE,
+ REASON_ADD,
+ REASON_MODIFY,
+ };
+ // called when a fast track of index has been removed, added, or modified
+ void updateMixerTrack(int index, Reason reason);
+
// FIXME these former local variables need comments
static const FastMixerState sInitial;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index dd1eabf..6dd9cab 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4784,7 +4784,10 @@
track->mFillingUpStatus = Track::FS_ACTIVE;
if (track->mState == TrackBase::RESUMING) {
track->mState = TrackBase::ACTIVE;
- param = AudioMixer::RAMP_VOLUME;
+ // If a new track is paused immediately after start, do not ramp on resume.
+ if (cblk->mServer != 0) {
+ param = AudioMixer::RAMP_VOLUME;
+ }
}
mAudioMixer->setParameter(trackId, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
mLeftVolFloat = -1.0;
@@ -5462,6 +5465,11 @@
mFlushPending = true;
}
}
+ } else if (previousTrack == 0) {
+ // there could be an old track added back during track transition for direct
+ // output, so always issues flush to flush data of the previous track if it
+ // was already destroyed with HAL paused, then flush can resume the playback
+ mFlushPending = true;
}
PlaybackThread::onAddNewTrack_l();
}
@@ -5500,7 +5508,6 @@
doHwPause = true;
mHwPaused = true;
}
- tracksToRemove->add(track);
} else if (track->isFlushPending()) {
track->flushAck();
if (last) {
@@ -5597,7 +5604,8 @@
int64_t framesWritten = mBytesWritten / mFrameSize;
if (mStandby || !last ||
- track->presentationComplete(framesWritten, audioHALFrames)) {
+ track->presentationComplete(framesWritten, audioHALFrames) ||
+ track->isPaused()) {
if (track->isStopping_2()) {
track->mState = TrackBase::STOPPED;
}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index d4cfd1e..803cfac 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -143,6 +143,16 @@
void trackEffectEnabled(const sp<EffectDescriptor> &effect, bool enabled);
+ /**
+ * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
+ * been chosen as the preferred device by any client, the policy manager shall
+ * prevent from using this device any more by clearing all the session routes involving this
+ * device.
+ * In other words, the preferred device port id of these clients will be resetted to NONE.
+ * @param disconnectedDevice device to be disconnected
+ */
+ void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);
+
void dump(String8 *dst) const;
};
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 6132bb4..c84636e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -380,6 +380,16 @@
uint32_t inPastMs = 0, nsecs_t sysTime = 0) const;
/**
+ * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
+ * been chosen as the preferred device by any client, the policy manager shall
+ * prevent from using this device any more by clearing all the session routes involving this
+ * device.
+ * In other words, the preferred device port id of these clients will be resetted to NONE.
+ * @param disconnectedDevice device to be disconnected
+ */
+ void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);
+
+ /**
* returns the A2DP output handle if it is open or 0 otherwise
*/
audio_io_handle_t getA2dpOutput() const;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 1abce6f..d6f24b2 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -43,12 +43,12 @@
android::AudioMix *getMix();
- void setMix(AudioMix &mix);
+ void setMix(const AudioMix &mix);
void dump(String8 *dst, int spaces, int index) const;
private:
- AudioMix mMix; // Audio policy mix descriptor
+ AudioMix mMix; // Audio policy mix descriptor
sp<SwAudioOutputDescriptor> mOutput; // Corresponding output stream
};
@@ -71,7 +71,7 @@
* @param[out] desc to return if an primary output could be found.
* @param[out] secondaryDesc other desc that the audio should be routed to.
*/
- status_t getOutputForAttr(audio_attributes_t attributes, uid_t uid,
+ status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid,
sp<SwAudioOutputDescriptor> &primaryDesc,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
@@ -102,7 +102,7 @@
private:
enum class MixMatchStatus { MATCH, NO_MATCH, INVALID_MIX };
MixMatchStatus mixMatch(const AudioMix* mix, size_t mixIndex,
- audio_attributes_t attributes, uid_t uid);
+ const audio_attributes_t& attributes, uid_t uid);
};
} // namespace android
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index c880e67..1fa1123 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -511,6 +511,19 @@
}
}
+void AudioInputCollection::clearSessionRoutesForDevice(
+ const sp<DeviceDescriptor> &disconnectedDevice)
+{
+ for (size_t i = 0; i < size(); i++) {
+ sp<AudioInputDescriptor> inputDesc = valueAt(i);
+ for (const auto& client : inputDesc->getClientIterable()) {
+ if (client->preferredDeviceId() == disconnectedDevice->getId()) {
+ client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ }
+ }
+ }
+}
+
void AudioInputCollection::dump(String8 *dst) const
{
dst->append("\nInputs dump:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 1dfd88a..77e7add 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -802,6 +802,19 @@
return 0;
}
+void SwAudioOutputCollection::clearSessionRoutesForDevice(
+ const sp<DeviceDescriptor> &disconnectedDevice)
+{
+ for (size_t i = 0; i < size(); i++) {
+ sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+ for (const auto& client : outputDesc->getClientIterable()) {
+ if (client->preferredDeviceId() == disconnectedDevice->getId()) {
+ client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+ }
+ }
+ }
+}
+
void SwAudioOutputCollection::dump(String8 *dst) const
{
dst->append("\nOutputs dump:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 6b6d9d2..446a1e6 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -42,7 +42,7 @@
mOutput.clear();
}
-void AudioPolicyMix::setMix(AudioMix &mix)
+void AudioPolicyMix::setMix(const AudioMix &mix)
{
mMix = mix;
}
@@ -157,7 +157,7 @@
}
status_t AudioPolicyMixCollection::getOutputForAttr(
- audio_attributes_t attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
+ const audio_attributes_t& attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
{
ALOGV("getOutputForAttr() querying %zu mixes:", size());
@@ -203,7 +203,7 @@
}
AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
- const AudioMix* mix, size_t mixIndex, audio_attributes_t attributes, uid_t uid) {
+ const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) {
if (mix->mMixType == MIX_TYPE_PLAYERS) {
// TODO if adding more player rules (currently only 2), make rule handling "generic"
@@ -378,7 +378,7 @@
ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string());
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = valueAt(i);
- AudioMix *mix = policyMix->getMix();
+ const AudioMix *mix = policyMix->getMix();
ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string());
}
#endif
@@ -435,7 +435,7 @@
// for each player mix: remove existing rules that match or exclude this uid
for (size_t i = 0; i < size(); i++) {
bool foundUidRule = false;
- AudioMix *mix = valueAt(i)->getMix();
+ const AudioMix *mix = valueAt(i)->getMix();
if (mix->mMixType != MIX_TYPE_PLAYERS) {
continue;
}
@@ -463,7 +463,7 @@
// for each player mix: find rules that don't exclude this uid, and add the device to the list
for (size_t i = 0; i < size(); i++) {
bool ruleAllowsUid = true;
- AudioMix *mix = valueAt(i)->getMix();
+ const AudioMix *mix = valueAt(i)->getMix();
if (mix->mMixType != MIX_TYPE_PLAYERS) {
continue;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ccec93f..5c22727 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -193,6 +193,8 @@
// remove device from available output devices
mAvailableOutputDevices.remove(device);
+ mOutputs.clearSessionRoutesForDevice(device);
+
checkOutputsForDevice(device, state, outputs);
// Reset active device codec
@@ -929,8 +931,6 @@
const sp<DeviceDescriptor> requestedDevice =
mAvailableOutputDevices.getDeviceFromId(requestedPortId);
-
-
status_t status = getAudioAttributes(resultAttr, attr, *stream);
if (status != NO_ERROR) {
return status;
@@ -953,8 +953,8 @@
// FIXME: in case of RENDER policy, the output capabilities should be checked
if ((usePrimaryOutputFromPolicyMixes || !secondaryDescs->empty())
- && !audio_has_proportional_frames(config->format)) {
- ALOGW("%s: audio loopback only supports proportional frames", __func__);
+ && !audio_is_linear_pcm(config->format)) {
+ ALOGD("%s: rejecting request as dynamic audio policy only support pcm", __func__);
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
@@ -1045,6 +1045,14 @@
audio_attributes_t resultAttr;
bool isRequestedDeviceForExclusiveUse = false;
std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
+ const sp<DeviceDescriptor> requestedDevice =
+ mAvailableOutputDevices.getDeviceFromId(requestedPortId);
+
+ // Prevent from storing invalid requested device id in clients
+ const audio_port_handle_t sanitizedRequestedPortId =
+ requestedDevice != nullptr ? requestedPortId : AUDIO_PORT_HANDLE_NONE;
+ *selectedDeviceId = sanitizedRequestedPortId;
+
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
&secondaryOutputDescs);
@@ -1064,15 +1072,15 @@
sp<TrackClientDescriptor> clientDesc =
new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
- requestedPortId, *stream,
+ sanitizedRequestedPortId, *stream,
mEngine->getProductStrategyForAttributes(resultAttr),
*flags, isRequestedDeviceForExclusiveUse,
std::move(weakSecondaryOutputDescs));
sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
outputDesc->addClient(clientDesc);
- ALOGV("%s() returns output %d selectedDeviceId %d for port ID %d", __func__,
- *output, *selectedDeviceId, *portId);
+ ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
+ *output, requestedPortId, *selectedDeviceId, *portId);
return NO_ERROR;
}
@@ -1953,6 +1961,8 @@
if (explicitRoutingDevice != nullptr) {
device = explicitRoutingDevice;
} else {
+ // Prevent from storing invalid requested device id in clients
+ requestedDeviceId = AUDIO_PORT_HANDLE_NONE;
device = mEngine->getInputDeviceForAttributes(attributes, &policyMix);
}
if (device == nullptr) {
@@ -5295,16 +5305,17 @@
// filter devices according to output selected
DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
+ DeviceVector prevDevices = outputDesc->devices();
// no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
- // output profile
- if (!devices.isEmpty() && filteredDevices.isEmpty()) {
+ // output profile or if new device is not supported AND previous device(s) is(are) still
+ // available (otherwise reset device must be done on the output)
+ if (!devices.isEmpty() && filteredDevices.isEmpty() &&
+ !mAvailableOutputDevices.filter(prevDevices).empty()) {
ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
return 0;
}
- DeviceVector prevDevices = outputDesc->devices();
-
ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
if (!filteredDevices.isEmpty()) {
@@ -5819,6 +5830,8 @@
}
}
+ mInputs.clearSessionRoutesForDevice(deviceDesc);
+
mHwModules.cleanUpForDevice(deviceDesc);
}
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index a090479..2ca8356 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -41,6 +41,8 @@
"api2/CameraDeviceClient.cpp",
"api2/CompositeStream.cpp",
"api2/DepthCompositeStream.cpp",
+ "api2/HeicEncoderInfoManager.cpp",
+ "api2/HeicCompositeStream.cpp",
"device1/CameraHardwareInterface.cpp",
"device3/Camera3Device.cpp",
"device3/Camera3Stream.cpp",
@@ -62,12 +64,14 @@
"hidl/HidlCameraService.cpp",
"utils/CameraTraces.cpp",
"utils/AutoConditionLock.cpp",
+ "utils/ExifUtils.cpp",
"utils/TagMonitor.cpp",
"utils/LatencyHistogram.cpp",
],
shared_libs: [
"libdl",
+ "libexif",
"libui",
"liblog",
"libutilscallstack",
@@ -85,14 +89,17 @@
"libhidlbase",
"libhidltransport",
"libjpeg",
+ "libmedia_omx",
"libmemunreachable",
"libsensorprivacy",
+ "libstagefright",
"libstagefright_foundation",
"android.frameworks.cameraservice.common@2.0",
"android.frameworks.cameraservice.service@2.0",
"android.frameworks.cameraservice.device@2.0",
"android.hardware.camera.common@1.0",
"android.hardware.camera.provider@2.4",
+ "android.hardware.camera.provider@2.5",
"android.hardware.camera.device@1.0",
"android.hardware.camera.device@3.2",
"android.hardware.camera.device@3.3",
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index ee8d7e1..e06897f 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1654,6 +1654,49 @@
return Status::ok();
}
+Status CameraService::notifyDeviceStateChange(int64_t newState) {
+ const int pid = CameraThreadState::getCallingPid();
+ const int selfPid = getpid();
+
+ // Permission checks
+ if (pid != selfPid) {
+ // Ensure we're being called by system_server, or similar process with
+ // permissions to notify the camera service about system events
+ if (!checkCallingPermission(
+ String16("android.permission.CAMERA_SEND_SYSTEM_EVENTS"))) {
+ const int uid = CameraThreadState::getCallingUid();
+ ALOGE("Permission Denial: cannot send updates to camera service about device"
+ " state changes from pid=%d, uid=%d", pid, uid);
+ return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED,
+ "No permission to send updates to camera service about device state"
+ " changes from pid=%d, uid=%d", pid, uid);
+ }
+ }
+
+ ATRACE_CALL();
+
+ using hardware::camera::provider::V2_5::DeviceState;
+ hardware::hidl_bitfield<DeviceState> newDeviceState{};
+ if (newState & ICameraService::DEVICE_STATE_BACK_COVERED) {
+ newDeviceState |= DeviceState::BACK_COVERED;
+ }
+ if (newState & ICameraService::DEVICE_STATE_FRONT_COVERED) {
+ newDeviceState |= DeviceState::FRONT_COVERED;
+ }
+ if (newState & ICameraService::DEVICE_STATE_FOLDED) {
+ newDeviceState |= DeviceState::FOLDED;
+ }
+ // Only map vendor bits directly
+ uint64_t vendorBits = static_cast<uint64_t>(newState) & 0xFFFFFFFF00000000l;
+ newDeviceState |= vendorBits;
+
+ ALOGV("%s: New device state 0x%" PRIx64, __FUNCTION__, newDeviceState);
+ Mutex::Autolock l(mServiceLock);
+ mCameraProviderManager->notifyDeviceStateChange(newDeviceState);
+
+ return Status::ok();
+}
+
Status CameraService::addListener(const sp<ICameraServiceListener>& listener,
/*out*/
std::vector<hardware::CameraStatus> *cameraStatuses) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 3af52fa..cf0cef8 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -154,6 +154,8 @@
virtual binder::Status notifySystemEvent(int32_t eventId,
const std::vector<int32_t>& args);
+ virtual binder::Status notifyDeviceStateChange(int64_t newState);
+
// OK = supports api of that version, -EOPNOTSUPP = does not support
virtual binder::Status supportsCameraApi(
const String16& cameraId, int32_t apiVersion,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index e6f75f4..36395f3 100755
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -62,7 +62,8 @@
}
}
-void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/, int /*streamId*/) {
+void JpegProcessor::onBufferRequestForFrameNumber(uint64_t /*frameNumber*/,
+ int /*streamId*/, const CameraMetadata& /*settings*/) {
// Intentionally left empty
}
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.h b/services/camera/libcameraservice/api1/client2/JpegProcessor.h
index 2ee930e..53e6836 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.h
@@ -54,7 +54,8 @@
// Camera3StreamBufferListener implementation
void onBufferAcquired(const BufferInfo& bufferInfo) override;
void onBufferReleased(const BufferInfo& bufferInfo) override;
- void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override;
+ void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& settings) override;
status_t updateStream(const Parameters ¶ms);
status_t deleteStream();
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 9e203da..b512f2b 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -34,6 +34,7 @@
#include <camera_metadata_hidden.h>
#include "DepthCompositeStream.h"
+#include "HeicCompositeStream.h"
// Convenience methods for constructing binder::Status objects for error returns
@@ -711,21 +712,35 @@
return res;
if (!isStreamInfoValid) {
- if (camera3::DepthCompositeStream::isDepthCompositeStream(surface)) {
+ bool isDepthCompositeStream =
+ camera3::DepthCompositeStream::isDepthCompositeStream(surface);
+ bool isHeicCompositeStream =
+ camera3::HeicCompositeStream::isHeicCompositeStream(surface);
+ if (isDepthCompositeStream || isHeicCompositeStream) {
// We need to take in to account that composite streams can have
// additional internal camera streams.
std::vector<OutputStreamInfo> compositeStreams;
- ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
+ if (isDepthCompositeStream) {
+ ret = camera3::DepthCompositeStream::getCompositeStreamInfo(streamInfo,
+ mDevice->info(), &compositeStreams);
+ } else {
+ ret = camera3::HeicCompositeStream::getCompositeStreamInfo(streamInfo,
mDevice->info(), &compositeStreams);
+ }
if (ret != OK) {
String8 msg = String8::format(
- "Camera %s: Failed adding depth composite streams: %s (%d)",
+ "Camera %s: Failed adding composite streams: %s (%d)",
mCameraIdStr.string(), strerror(-ret), ret);
ALOGE("%s: %s", __FUNCTION__, msg.string());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- if (compositeStreams.size() > 1) {
+ if (compositeStreams.size() == 0) {
+ // No internal streams means composite stream not
+ // supported.
+ *status = false;
+ return binder::Status::ok();
+ } else if (compositeStreams.size() > 1) {
streamCount += compositeStreams.size() - 1;
streamConfiguration.streams.resize(streamCount);
}
@@ -937,15 +952,16 @@
int streamId = camera3::CAMERA3_STREAM_ID_INVALID;
std::vector<int> surfaceIds;
- if (!camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0])) {
- err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
- streamInfo.height, streamInfo.format, streamInfo.dataSpace,
- static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
- &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
- isShared);
- } else {
- sp<CompositeStream> compositeStream = new camera3::DepthCompositeStream(mDevice,
- getRemoteCallback());
+ bool isDepthCompositeStream = camera3::DepthCompositeStream::isDepthCompositeStream(surfaces[0]);
+ bool isHeicCompisiteStream = camera3::HeicCompositeStream::isHeicCompositeStream(surfaces[0]);
+ if (isDepthCompositeStream || isHeicCompisiteStream) {
+ sp<CompositeStream> compositeStream;
+ if (isDepthCompositeStream) {
+ compositeStream = new camera3::DepthCompositeStream(mDevice, getRemoteCallback());
+ } else {
+ compositeStream = new camera3::HeicCompositeStream(mDevice, getRemoteCallback());
+ }
+
err = compositeStream->createStream(surfaces, deferredConsumer, streamInfo.width,
streamInfo.height, streamInfo.format,
static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
@@ -955,6 +971,12 @@
mCompositeStreamMap.add(IInterface::asBinder(surfaces[0]->getIGraphicBufferProducer()),
compositeStream);
}
+ } else {
+ err = mDevice->createStream(surfaces, deferredConsumer, streamInfo.width,
+ streamInfo.height, streamInfo.format, streamInfo.dataSpace,
+ static_cast<camera3_stream_rotation_t>(outputConfiguration.getRotation()),
+ &streamId, physicalCameraId, &surfaceIds, outputConfiguration.getSurfaceSetID(),
+ isShared);
}
if (err != OK) {
@@ -1437,6 +1459,8 @@
camera_metadata_ro_entry streamConfigs =
(dataSpace == HAL_DATASPACE_DEPTH) ?
info.find(ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS) :
+ (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_HEIF)) ?
+ info.find(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS) :
info.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
int32_t bestWidth = -1;
@@ -1930,6 +1954,10 @@
remoteCb->onCaptureStarted(resultExtras, timestamp);
}
Camera2ClientBase::notifyShutter(resultExtras, timestamp);
+
+ for (size_t i = 0; i < mCompositeStreamMap.size(); i++) {
+ mCompositeStreamMap.valueAt(i)->onShutter(resultExtras, timestamp);
+ }
}
void CameraDeviceClient::notifyPrepared(int streamId) {
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index 796bf42..354eaf9 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -82,7 +82,8 @@
return deleteInternalStreams();
}
-void CompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) {
+void CompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& /*settings*/) {
Mutex::Autolock l(mMutex);
if (!mErrorState && (streamId == getStreamId())) {
mPendingCaptureResults.emplace(frameNumber, CameraMetadata());
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index 5837745..a401a82 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -23,6 +23,7 @@
#include <android/hardware/camera2/ICameraDeviceCallbacks.h>
#include <camera/CameraMetadata.h>
#include <camera/camera2/OutputConfiguration.h>
+#include <gui/IProducerListener.h>
#include "common/CameraDeviceBase.h"
#include "device3/Camera3StreamInterface.h"
@@ -66,15 +67,24 @@
// Return composite stream id.
virtual int getStreamId() = 0;
+ // Notify when shutter notify is triggered
+ virtual void onShutter(const CaptureResultExtras& /*resultExtras*/, nsecs_t /*timestamp*/) {}
+
void onResultAvailable(const CaptureResult& result);
bool onError(int32_t errorCode, const CaptureResultExtras& resultExtras);
// Camera3StreamBufferListener implementation
void onBufferAcquired(const BufferInfo& /*bufferInfo*/) override { /*Empty for now */ }
void onBufferReleased(const BufferInfo& bufferInfo) override;
- void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) override;
+ void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& settings) override;
protected:
+ struct ProducerListener : public BnProducerListener {
+ // ProducerListener impementation
+ void onBufferReleased() override { /*No impl. for now*/ };
+ };
+
status_t registerCompositeStreamListener(int32_t streamId);
void eraseResult(int64_t frameNumber);
void flagAnErrorFrameNumber(int64_t frameNumber);
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index f627b25..2eec0f7 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -634,6 +634,11 @@
mDepthStreamId = -1;
}
+ if (mOutputSurface != nullptr) {
+ mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA);
+ mOutputSurface.clear();
+ }
+
return ret;
}
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index e8fe517..1bf31f4 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -21,7 +21,6 @@
#include <dynamic_depth/imaging_model.h>
#include <dynamic_depth/depth_map.h>
-#include <gui/IProducerListener.h>
#include <gui/CpuConsumer.h>
#include "CompositeStream.h"
@@ -116,11 +115,6 @@
static const auto kDepthMapDataSpace = HAL_DATASPACE_DEPTH;
static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF;
- struct ProducerListener : public BnProducerListener {
- // ProducerListener implementation
- void onBufferReleased() override { /*No impl. for now*/ };
- };
-
int mBlobStreamId, mBlobSurfaceId, mDepthStreamId, mDepthSurfaceId;
size_t mBlobWidth, mBlobHeight;
sp<CpuConsumer> mBlobConsumer, mDepthConsumer;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
new file mode 100644
index 0000000..8e9c39e
--- /dev/null
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -0,0 +1,1610 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-HeicCompositeStream"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <linux/memfd.h>
+#include <pthread.h>
+#include <sys/syscall.h>
+
+#include <android/hardware/camera/device/3.5/types.h>
+#include <gui/Surface.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <media/ICrypto.h>
+#include <media/MediaCodecBuffer.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include "common/CameraDeviceBase.h"
+#include "utils/ExifUtils.h"
+#include "HeicEncoderInfoManager.h"
+#include "HeicCompositeStream.h"
+
+using android::hardware::camera::device::V3_5::CameraBlob;
+using android::hardware::camera::device::V3_5::CameraBlobId;
+
+namespace android {
+namespace camera3 {
+
+HeicCompositeStream::HeicCompositeStream(wp<CameraDeviceBase> device,
+ wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
+ CompositeStream(device, cb),
+ mUseHeic(false),
+ mNumOutputTiles(1),
+ mOutputWidth(0),
+ mOutputHeight(0),
+ mMaxHeicBufferSize(0),
+ mGridWidth(HeicEncoderInfoManager::kGridWidth),
+ mGridHeight(HeicEncoderInfoManager::kGridHeight),
+ mGridRows(1),
+ mGridCols(1),
+ mUseGrid(false),
+ mAppSegmentStreamId(-1),
+ mAppSegmentSurfaceId(-1),
+ mAppSegmentBufferAcquired(false),
+ mMainImageStreamId(-1),
+ mMainImageSurfaceId(-1),
+ mYuvBufferAcquired(false),
+ mProducerListener(new ProducerListener()),
+ mOutputBufferCounter(0),
+ mGridTimestampUs(0) {
+}
+
+HeicCompositeStream::~HeicCompositeStream() {
+ // Call deinitCodec in case stream hasn't been deleted yet to avoid any
+ // memory/resource leak.
+ deinitCodec();
+
+ mInputAppSegmentBuffers.clear();
+ mCodecOutputBuffers.clear();
+
+ mAppSegmentStreamId = -1;
+ mAppSegmentSurfaceId = -1;
+ mAppSegmentConsumer.clear();
+ mAppSegmentSurface.clear();
+
+ mMainImageStreamId = -1;
+ mMainImageSurfaceId = -1;
+ mMainImageConsumer.clear();
+ mMainImageSurface.clear();
+}
+
+bool HeicCompositeStream::isHeicCompositeStream(const sp<Surface> &surface) {
+ ANativeWindow *anw = surface.get();
+ status_t err;
+ int format;
+ if ((err = anw->query(anw, NATIVE_WINDOW_FORMAT, &format)) != OK) {
+ String8 msg = String8::format("Failed to query Surface format: %s (%d)", strerror(-err),
+ err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return false;
+ }
+
+ int dataspace;
+ if ((err = anw->query(anw, NATIVE_WINDOW_DEFAULT_DATASPACE, &dataspace)) != OK) {
+ String8 msg = String8::format("Failed to query Surface dataspace: %s (%d)", strerror(-err),
+ err);
+ ALOGE("%s: %s", __FUNCTION__, msg.string());
+ return false;
+ }
+
+ return ((format == HAL_PIXEL_FORMAT_BLOB) && (dataspace == HAL_DATASPACE_HEIF));
+}
+
+status_t HeicCompositeStream::createInternalStreams(const std::vector<sp<Surface>>& consumers,
+ bool /*hasDeferredConsumer*/, uint32_t width, uint32_t height, int format,
+ camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ std::vector<int> *surfaceIds, int /*streamSetId*/, bool /*isShared*/) {
+
+ sp<CameraDeviceBase> device = mDevice.promote();
+ if (!device.get()) {
+ ALOGE("%s: Invalid camera device!", __FUNCTION__);
+ return NO_INIT;
+ }
+
+ status_t res = initializeCodec(width, height, device);
+ if (res != OK) {
+ ALOGE("%s: Failed to initialize HEIC/HEVC codec: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return NO_INIT;
+ }
+
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ mAppSegmentConsumer = new CpuConsumer(consumer, 1);
+ mAppSegmentConsumer->setFrameAvailableListener(this);
+ mAppSegmentConsumer->setName(String8("Camera3-HeicComposite-AppSegmentStream"));
+ mAppSegmentSurface = new Surface(producer);
+
+ res = device->createStream(mAppSegmentSurface, mAppSegmentMaxSize, 1, format,
+ kAppSegmentDataSpace, rotation, &mAppSegmentStreamId, physicalCameraId, surfaceIds);
+ if (res == OK) {
+ mAppSegmentSurfaceId = (*surfaceIds)[0];
+ } else {
+ ALOGE("%s: Failed to create JPEG App segment stream: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ if (!mUseGrid) {
+ res = mCodec->createInputSurface(&producer);
+ if (res != OK) {
+ ALOGE("%s: Failed to create input surface for Heic codec: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ } else {
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ mMainImageConsumer = new CpuConsumer(consumer, 1);
+ mMainImageConsumer->setFrameAvailableListener(this);
+ mMainImageConsumer->setName(String8("Camera3-HeicComposite-HevcInputYUVStream"));
+ }
+ mMainImageSurface = new Surface(producer);
+
+ res = mCodec->start();
+ if (res != OK) {
+ ALOGE("%s: Failed to start codec: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ std::vector<int> sourceSurfaceId;
+ //Use YUV_888 format if framework tiling is needed.
+ int srcStreamFmt = mUseGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 :
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ res = device->createStream(mMainImageSurface, width, height, srcStreamFmt, kHeifDataSpace,
+ rotation, id, physicalCameraId, &sourceSurfaceId);
+ if (res == OK) {
+ mMainImageSurfaceId = sourceSurfaceId[0];
+ mMainImageStreamId = *id;
+ } else {
+ ALOGE("%s: Failed to create main image stream: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ mOutputSurface = consumers[0];
+ res = registerCompositeStreamListener(getStreamId());
+ if (res != OK) {
+ ALOGE("%s: Failed to register HAL main image stream", __FUNCTION__);
+ return res;
+ }
+
+ return res;
+}
+
+status_t HeicCompositeStream::deleteInternalStreams() {
+ requestExit();
+ auto res = join();
+ if (res != OK) {
+ ALOGE("%s: Failed to join with the main processing thread: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ }
+
+ deinitCodec();
+
+ if (mAppSegmentStreamId >= 0) {
+ sp<CameraDeviceBase> device = mDevice.promote();
+ if (!device.get()) {
+ ALOGE("%s: Invalid camera device!", __FUNCTION__);
+ return NO_INIT;
+ }
+
+ res = device->deleteStream(mAppSegmentStreamId);
+ mAppSegmentStreamId = -1;
+ }
+
+ if (mOutputSurface != nullptr) {
+ mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA);
+ mOutputSurface.clear();
+ }
+ return res;
+}
+
+void HeicCompositeStream::onBufferReleased(const BufferInfo& bufferInfo) {
+ Mutex::Autolock l(mMutex);
+
+ if (bufferInfo.mError) return;
+
+ mCodecOutputBufferTimestamps.push(bufferInfo.mTimestamp);
+}
+
+// We need to get the settings early to handle the case where the codec output
+// arrives earlier than result metadata.
+void HeicCompositeStream::onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& settings) {
+ ATRACE_ASYNC_BEGIN("HEIC capture", frameNumber);
+
+ Mutex::Autolock l(mMutex);
+ if (mErrorState || (streamId != getStreamId())) {
+ return;
+ }
+
+ mPendingCaptureResults.emplace(frameNumber, CameraMetadata());
+
+ camera_metadata_ro_entry entry;
+
+ int32_t orientation = 0;
+ entry = settings.find(ANDROID_JPEG_ORIENTATION);
+ if (entry.count == 1) {
+ orientation = entry.data.i32[0];
+ }
+
+ int32_t quality = kDefaultJpegQuality;
+ entry = settings.find(ANDROID_JPEG_QUALITY);
+ if (entry.count == 1) {
+ quality = entry.data.i32[0];
+ }
+
+ mSettingsByFrameNumber[frameNumber] = std::make_pair(orientation, quality);
+}
+
+void HeicCompositeStream::onFrameAvailable(const BufferItem& item) {
+ if (item.mDataSpace == static_cast<android_dataspace>(kAppSegmentDataSpace)) {
+ ALOGV("%s: JPEG APP segments buffer with ts: %" PRIu64 " ms. arrived!",
+ __func__, ns2ms(item.mTimestamp));
+
+ Mutex::Autolock l(mMutex);
+ if (!mErrorState) {
+ mInputAppSegmentBuffers.push_back(item.mTimestamp);
+ mInputReadyCondition.signal();
+ }
+ } else if (item.mDataSpace == kHeifDataSpace) {
+ ALOGV("%s: YUV_888 buffer with ts: %" PRIu64 " ms. arrived!",
+ __func__, ns2ms(item.mTimestamp));
+
+ Mutex::Autolock l(mMutex);
+ if (!mUseGrid) {
+ ALOGE("%s: YUV_888 internal stream is only supported for HEVC tiling",
+ __FUNCTION__);
+ return;
+ }
+ if (!mErrorState) {
+ mInputYuvBuffers.push_back(item.mTimestamp);
+ mInputReadyCondition.signal();
+ }
+ } else {
+ ALOGE("%s: Unexpected data space: 0x%x", __FUNCTION__, item.mDataSpace);
+ }
+}
+
+status_t HeicCompositeStream::getCompositeStreamInfo(const OutputStreamInfo &streamInfo,
+ const CameraMetadata& ch, std::vector<OutputStreamInfo>* compositeOutput /*out*/) {
+ if (compositeOutput == nullptr) {
+ return BAD_VALUE;
+ }
+
+ compositeOutput->clear();
+
+ bool useGrid, useHeic;
+ bool isSizeSupported = isSizeSupportedByHeifEncoder(
+ streamInfo.width, streamInfo.height, &useHeic, &useGrid, nullptr);
+ if (!isSizeSupported) {
+ // Size is not supported by either encoder.
+ return OK;
+ }
+
+ compositeOutput->insert(compositeOutput->end(), 2, streamInfo);
+
+ // JPEG APPS segments Blob stream info
+ (*compositeOutput)[0].width = calcAppSegmentMaxSize(ch);
+ (*compositeOutput)[0].height = 1;
+ (*compositeOutput)[0].format = HAL_PIXEL_FORMAT_BLOB;
+ (*compositeOutput)[0].dataSpace = kAppSegmentDataSpace;
+ (*compositeOutput)[0].consumerUsage = GRALLOC_USAGE_SW_READ_OFTEN;
+
+ // YUV/IMPLEMENTATION_DEFINED stream info
+ (*compositeOutput)[1].width = streamInfo.width;
+ (*compositeOutput)[1].height = streamInfo.height;
+ (*compositeOutput)[1].format = useGrid ? HAL_PIXEL_FORMAT_YCbCr_420_888 :
+ HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+ (*compositeOutput)[1].dataSpace = kHeifDataSpace;
+ (*compositeOutput)[1].consumerUsage = useHeic ? GRALLOC_USAGE_HW_IMAGE_ENCODER :
+ useGrid ? GRALLOC_USAGE_SW_READ_OFTEN : GRALLOC_USAGE_HW_VIDEO_ENCODER;
+
+ return NO_ERROR;
+}
+
+bool HeicCompositeStream::isSizeSupportedByHeifEncoder(int32_t width, int32_t height,
+ bool* useHeic, bool* useGrid, int64_t* stall) {
+ static HeicEncoderInfoManager& heicManager = HeicEncoderInfoManager::getInstance();
+ return heicManager.isSizeSupported(width, height, useHeic, useGrid, stall);
+}
+
+bool HeicCompositeStream::isInMemoryTempFileSupported() {
+ int memfd = syscall(__NR_memfd_create, "HEIF-try-memfd", MFD_CLOEXEC);
+ if (memfd == -1) {
+ if (errno != ENOSYS) {
+ ALOGE("%s: Failed to create tmpfs file. errno %d", __FUNCTION__, errno);
+ }
+ return false;
+ }
+ close(memfd);
+ return true;
+}
+
+void HeicCompositeStream::onHeicOutputFrameAvailable(
+ const CodecOutputBufferInfo& outputBufferInfo) {
+ Mutex::Autolock l(mMutex);
+
+ ALOGV("%s: index %d, offset %d, size %d, time %" PRId64 ", flags 0x%x",
+ __FUNCTION__, outputBufferInfo.index, outputBufferInfo.offset,
+ outputBufferInfo.size, outputBufferInfo.timeUs, outputBufferInfo.flags);
+
+ if (!mErrorState) {
+ if ((outputBufferInfo.size > 0) &&
+ ((outputBufferInfo.flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) == 0)) {
+ mCodecOutputBuffers.push_back(outputBufferInfo);
+ mInputReadyCondition.signal();
+ } else {
+ mCodec->releaseOutputBuffer(outputBufferInfo.index);
+ }
+ } else {
+ mCodec->releaseOutputBuffer(outputBufferInfo.index);
+ }
+}
+
+void HeicCompositeStream::onHeicInputFrameAvailable(int32_t index) {
+ Mutex::Autolock l(mMutex);
+
+ if (!mUseGrid) {
+ ALOGE("%s: Codec YUV input mode must only be used for Hevc tiling mode", __FUNCTION__);
+ return;
+ }
+
+ mCodecInputBuffers.push_back(index);
+ mInputReadyCondition.signal();
+}
+
+void HeicCompositeStream::onHeicFormatChanged(sp<AMessage>& newFormat) {
+ if (newFormat == nullptr) {
+ ALOGE("%s: newFormat must not be null!", __FUNCTION__);
+ return;
+ }
+
+ Mutex::Autolock l(mMutex);
+
+ AString mime;
+ AString mimeHeic(MIMETYPE_IMAGE_ANDROID_HEIC);
+ newFormat->findString(KEY_MIME, &mime);
+ if (mime != mimeHeic) {
+ // For HEVC codec, below keys need to be filled out or overwritten so that the
+ // muxer can handle them as HEIC output image.
+ newFormat->setString(KEY_MIME, mimeHeic);
+ newFormat->setInt32(KEY_WIDTH, mOutputWidth);
+ newFormat->setInt32(KEY_HEIGHT, mOutputHeight);
+ if (mUseGrid) {
+ newFormat->setInt32(KEY_TILE_WIDTH, mGridWidth);
+ newFormat->setInt32(KEY_TILE_HEIGHT, mGridHeight);
+ newFormat->setInt32(KEY_GRID_ROWS, mGridRows);
+ newFormat->setInt32(KEY_GRID_COLUMNS, mGridCols);
+ }
+ }
+ newFormat->setInt32(KEY_IS_DEFAULT, 1 /*isPrimary*/);
+
+ int32_t gridRows, gridCols;
+ if (newFormat->findInt32(KEY_GRID_ROWS, &gridRows) &&
+ newFormat->findInt32(KEY_GRID_COLUMNS, &gridCols)) {
+ mNumOutputTiles = gridRows * gridCols;
+ } else {
+ mNumOutputTiles = 1;
+ }
+
+ ALOGV("%s: mNumOutputTiles is %zu", __FUNCTION__, mNumOutputTiles);
+ mFormat = newFormat;
+}
+
+void HeicCompositeStream::onHeicCodecError() {
+ Mutex::Autolock l(mMutex);
+ mErrorState = true;
+}
+
+status_t HeicCompositeStream::configureStream() {
+ if (isRunning()) {
+ // Processing thread is already running, nothing more to do.
+ return NO_ERROR;
+ }
+
+ if (mOutputSurface.get() == nullptr) {
+ ALOGE("%s: No valid output surface set!", __FUNCTION__);
+ return NO_INIT;
+ }
+
+ auto res = mOutputSurface->connect(NATIVE_WINDOW_API_CAMERA, mProducerListener);
+ if (res != OK) {
+ ALOGE("%s: Unable to connect to native window for stream %d",
+ __FUNCTION__, mMainImageStreamId);
+ return res;
+ }
+
+ if ((res = native_window_set_buffers_format(mOutputSurface.get(), HAL_PIXEL_FORMAT_BLOB))
+ != OK) {
+ ALOGE("%s: Unable to configure stream buffer format for stream %d", __FUNCTION__,
+ mMainImageStreamId);
+ return res;
+ }
+
+ ANativeWindow *anwConsumer = mOutputSurface.get();
+ int maxConsumerBuffers;
+ if ((res = anwConsumer->query(anwConsumer, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &maxConsumerBuffers)) != OK) {
+ ALOGE("%s: Unable to query consumer undequeued"
+ " buffer count for stream %d", __FUNCTION__, mMainImageStreamId);
+ return res;
+ }
+
+ // Cannot use SourceSurface buffer count since it could be codec's 512*512 tile
+ // buffer count.
+ int maxProducerBuffers = 1;
+ if ((res = native_window_set_buffer_count(
+ anwConsumer, maxProducerBuffers + maxConsumerBuffers)) != OK) {
+ ALOGE("%s: Unable to set buffer count for stream %d", __FUNCTION__, mMainImageStreamId);
+ return res;
+ }
+
+ if ((res = native_window_set_buffers_dimensions(anwConsumer, mMaxHeicBufferSize, 1)) != OK) {
+ ALOGE("%s: Unable to set buffer dimension %zu x 1 for stream %d: %s (%d)",
+ __FUNCTION__, mMaxHeicBufferSize, mMainImageStreamId, strerror(-res), res);
+ return res;
+ }
+
+ run("HeicCompositeStreamProc");
+
+ return NO_ERROR;
+}
+
+status_t HeicCompositeStream::insertGbp(SurfaceMap* /*out*/outSurfaceMap,
+ Vector<int32_t>* /*out*/outputStreamIds, int32_t* /*out*/currentStreamId) {
+ if (outSurfaceMap->find(mAppSegmentStreamId) == outSurfaceMap->end()) {
+ (*outSurfaceMap)[mAppSegmentStreamId] = std::vector<size_t>();
+ outputStreamIds->push_back(mAppSegmentStreamId);
+ }
+ (*outSurfaceMap)[mAppSegmentStreamId].push_back(mAppSegmentSurfaceId);
+
+ if (outSurfaceMap->find(mMainImageStreamId) == outSurfaceMap->end()) {
+ (*outSurfaceMap)[mMainImageStreamId] = std::vector<size_t>();
+ outputStreamIds->push_back(mMainImageStreamId);
+ }
+ (*outSurfaceMap)[mMainImageStreamId].push_back(mMainImageSurfaceId);
+
+ if (currentStreamId != nullptr) {
+ *currentStreamId = mMainImageStreamId;
+ }
+
+ return NO_ERROR;
+}
+
+void HeicCompositeStream::onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) {
+ Mutex::Autolock l(mMutex);
+ if (mErrorState) {
+ return;
+ }
+
+ if (mSettingsByFrameNumber.find(resultExtras.frameNumber) != mSettingsByFrameNumber.end()) {
+ mFrameNumberMap.emplace(resultExtras.frameNumber, timestamp);
+ mSettingsByTimestamp[timestamp] = mSettingsByFrameNumber[resultExtras.frameNumber];
+ mSettingsByFrameNumber.erase(resultExtras.frameNumber);
+ mInputReadyCondition.signal();
+ }
+}
+
+void HeicCompositeStream::compilePendingInputLocked() {
+ while (!mSettingsByTimestamp.empty()) {
+ auto it = mSettingsByTimestamp.begin();
+ mPendingInputFrames[it->first].orientation = it->second.first;
+ mPendingInputFrames[it->first].quality = it->second.second;
+ mSettingsByTimestamp.erase(it);
+ }
+
+ while (!mInputAppSegmentBuffers.empty() && !mAppSegmentBufferAcquired) {
+ CpuConsumer::LockedBuffer imgBuffer;
+ auto it = mInputAppSegmentBuffers.begin();
+ auto res = mAppSegmentConsumer->lockNextBuffer(&imgBuffer);
+ if (res == NOT_ENOUGH_DATA) {
+ // Canot not lock any more buffers.
+ break;
+ } else if ((res != OK) || (*it != imgBuffer.timestamp)) {
+ if (res != OK) {
+ ALOGE("%s: Error locking JPEG_APP_SEGMENTS image buffer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ } else {
+ ALOGE("%s: Expecting JPEG_APP_SEGMENTS buffer with time stamp: %" PRId64
+ " received buffer with time stamp: %" PRId64, __FUNCTION__,
+ *it, imgBuffer.timestamp);
+ }
+ mPendingInputFrames[*it].error = true;
+ mInputAppSegmentBuffers.erase(it);
+ continue;
+ }
+
+ if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
+ (mPendingInputFrames[imgBuffer.timestamp].error)) {
+ mAppSegmentConsumer->unlockBuffer(imgBuffer);
+ } else {
+ mPendingInputFrames[imgBuffer.timestamp].appSegmentBuffer = imgBuffer;
+ mAppSegmentBufferAcquired = true;
+ }
+ mInputAppSegmentBuffers.erase(it);
+ }
+
+ while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired) {
+ CpuConsumer::LockedBuffer imgBuffer;
+ auto it = mInputYuvBuffers.begin();
+ auto res = mMainImageConsumer->lockNextBuffer(&imgBuffer);
+ if (res == NOT_ENOUGH_DATA) {
+ // Canot not lock any more buffers.
+ break;
+ } else if (res != OK) {
+ ALOGE("%s: Error locking YUV_888 image buffer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ mPendingInputFrames[*it].error = true;
+ mInputYuvBuffers.erase(it);
+ continue;
+ } else if (*it != imgBuffer.timestamp) {
+ ALOGW("%s: Expecting YUV_888 buffer with time stamp: %" PRId64 " received buffer with "
+ "time stamp: %" PRId64, __FUNCTION__, *it, imgBuffer.timestamp);
+ mPendingInputFrames[*it].error = true;
+ mInputYuvBuffers.erase(it);
+ continue;
+ }
+
+ if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
+ (mPendingInputFrames[imgBuffer.timestamp].error)) {
+ mMainImageConsumer->unlockBuffer(imgBuffer);
+ } else {
+ mPendingInputFrames[imgBuffer.timestamp].yuvBuffer = imgBuffer;
+ mYuvBufferAcquired = true;
+ }
+ mInputYuvBuffers.erase(it);
+ }
+
+ while (!mCodecOutputBuffers.empty()) {
+ auto it = mCodecOutputBuffers.begin();
+ // Bitstream buffer timestamp doesn't necessarily directly correlate with input
+ // buffer timestamp. Assume encoder input to output is FIFO, use a queue
+ // to look up timestamp.
+ int64_t bufferTime = -1;
+ if (mCodecOutputBufferTimestamps.empty()) {
+ ALOGE("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__);
+ } else {
+ // Direct mapping between camera timestamp (in ns) and codec timestamp (in us).
+ bufferTime = mCodecOutputBufferTimestamps.front();
+ mOutputBufferCounter++;
+ if (mOutputBufferCounter == mNumOutputTiles) {
+ mCodecOutputBufferTimestamps.pop();
+ mOutputBufferCounter = 0;
+ }
+
+ mPendingInputFrames[bufferTime].codecOutputBuffers.push_back(*it);
+ }
+ mCodecOutputBuffers.erase(it);
+ }
+
+ while (!mFrameNumberMap.empty()) {
+ auto it = mFrameNumberMap.begin();
+ mPendingInputFrames[it->second].frameNumber = it->first;
+ mFrameNumberMap.erase(it);
+ }
+
+ // Heic composition doesn't depend on capture result, so no need to check
+ // mErrorFrameNumbers. Just remove them.
+ mErrorFrameNumbers.clear();
+
+ // Distribute codec input buffers to be filled out from YUV output
+ for (auto it = mPendingInputFrames.begin();
+ it != mPendingInputFrames.end() && mCodecInputBuffers.size() > 0; it++) {
+ InputFrame& inputFrame(it->second);
+ if (inputFrame.codecInputCounter < mGridRows * mGridCols) {
+ // Available input tiles that are required for the current input
+ // image.
+ size_t newInputTiles = std::min(mCodecInputBuffers.size(),
+ mGridRows * mGridCols - inputFrame.codecInputCounter);
+ for (size_t i = 0; i < newInputTiles; i++) {
+ CodecInputBufferInfo inputInfo =
+ { mCodecInputBuffers[0], mGridTimestampUs++, inputFrame.codecInputCounter };
+ inputFrame.codecInputBuffers.push_back(inputInfo);
+
+ mCodecInputBuffers.erase(mCodecInputBuffers.begin());
+ inputFrame.codecInputCounter++;
+ }
+ break;
+ }
+ }
+}
+
+bool HeicCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*out*/) {
+ if (currentTs == nullptr) {
+ return false;
+ }
+
+ bool newInputAvailable = false;
+ for (const auto& it : mPendingInputFrames) {
+ bool appSegmentBufferReady = (it.second.appSegmentBuffer.data != nullptr) &&
+ !it.second.appSegmentWritten;
+ bool codecOutputReady = !it.second.codecOutputBuffers.empty();
+ bool codecInputReady = (it.second.yuvBuffer.data != nullptr) &&
+ (!it.second.codecInputBuffers.empty());
+ if ((!it.second.error) &&
+ (it.first < *currentTs) &&
+ (appSegmentBufferReady || codecOutputReady || codecInputReady)) {
+ *currentTs = it.first;
+ newInputAvailable = true;
+ break;
+ }
+ }
+
+ return newInputAvailable;
+}
+
+int64_t HeicCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*out*/) {
+ int64_t res = -1;
+ if (currentTs == nullptr) {
+ return res;
+ }
+
+ for (const auto& it : mPendingInputFrames) {
+ if (it.second.error && !it.second.errorNotified && (it.first < *currentTs)) {
+ *currentTs = it.first;
+ res = it.second.frameNumber;
+ break;
+ }
+ }
+
+ return res;
+}
+
+status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp,
+ InputFrame &inputFrame) {
+ ATRACE_CALL();
+ status_t res = OK;
+
+ bool appSegmentBufferReady = inputFrame.appSegmentBuffer.data != nullptr &&
+ !inputFrame.appSegmentWritten;
+ bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0;
+ bool codecInputReady = inputFrame.yuvBuffer.data != nullptr &&
+ !inputFrame.codecInputBuffers.empty();
+
+ if (!appSegmentBufferReady && !codecOutputReady && !codecInputReady) {
+ ALOGW("%s: No valid appSegmentBuffer/codec input/outputBuffer available!", __FUNCTION__);
+ return OK;
+ }
+
+ // Handle inputs for Hevc tiling
+ if (codecInputReady) {
+ res = processCodecInputFrame(inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to process codec input frame: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Initialize and start muxer if not yet done so
+ if (inputFrame.muxer == nullptr) {
+ res = startMuxerForInputFrame(timestamp, inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to create and start muxer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Write JPEG APP segments data to the muxer.
+ if (appSegmentBufferReady && inputFrame.muxer != nullptr) {
+ res = processAppSegment(timestamp, inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ // Write media codec bitstream buffers to muxer.
+ while (!inputFrame.codecOutputBuffers.empty()) {
+ res = processOneCodecOutputFrame(timestamp, inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to process codec output frame: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ if (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0) {
+ res = processCompletedInputFrame(timestamp, inputFrame);
+ if (res != OK) {
+ ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ }
+
+ return res;
+}
+
+status_t HeicCompositeStream::startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame) {
+ sp<ANativeWindow> outputANW = mOutputSurface;
+ if (inputFrame.codecOutputBuffers.size() == 0) {
+ // No single codec output buffer has been generated. Continue to
+ // wait.
+ return OK;
+ }
+
+ auto res = outputANW->dequeueBuffer(mOutputSurface.get(), &inputFrame.anb, &inputFrame.fenceFd);
+ if (res != OK) {
+ ALOGE("%s: Error retrieving output buffer: %s (%d)", __FUNCTION__, strerror(-res),
+ res);
+ return res;
+ }
+
+ // Combine current thread id, stream id and timestamp to uniquely identify image.
+ std::ostringstream tempOutputFile;
+ tempOutputFile << "HEIF-" << pthread_self() << "-"
+ << getStreamId() << "-" << timestamp;
+ inputFrame.fileFd = syscall(__NR_memfd_create, tempOutputFile.str().c_str(), MFD_CLOEXEC);
+ if (inputFrame.fileFd < 0) {
+ ALOGE("%s: Failed to create file %s. Error no is %d", __FUNCTION__,
+ tempOutputFile.str().c_str(), errno);
+ return NO_INIT;
+ }
+ inputFrame.muxer = new MediaMuxer(inputFrame.fileFd, MediaMuxer::OUTPUT_FORMAT_HEIF);
+ if (inputFrame.muxer == nullptr) {
+ ALOGE("%s: Failed to create MediaMuxer for file fd %d",
+ __FUNCTION__, inputFrame.fileFd);
+ return NO_INIT;
+ }
+
+ res = inputFrame.muxer->setOrientationHint(inputFrame.orientation);
+ if (res != OK) {
+ ALOGE("%s: Failed to setOrientationHint: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ // Set encoder quality
+ {
+ sp<AMessage> qualityParams = new AMessage;
+ qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, inputFrame.quality);
+ res = mCodec->setParameters(qualityParams);
+ if (res != OK) {
+ ALOGE("%s: Failed to set codec quality: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ ssize_t trackId = inputFrame.muxer->addTrack(mFormat);
+ if (trackId < 0) {
+ ALOGE("%s: Failed to addTrack to the muxer: %zd", __FUNCTION__, trackId);
+ return NO_INIT;
+ }
+
+ inputFrame.trackIndex = trackId;
+ inputFrame.pendingOutputTiles = mNumOutputTiles;
+
+ res = inputFrame.muxer->start();
+ if (res != OK) {
+ ALOGE("%s: Failed to start MediaMuxer: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t HeicCompositeStream::processAppSegment(nsecs_t timestamp, InputFrame &inputFrame) {
+ size_t app1Size = 0;
+ auto appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
+ inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
+ &app1Size);
+ ALOGV("%s: appSegmentSize is %zu, width %d, height %d, app1Size %zu", __FUNCTION__,
+ appSegmentSize, inputFrame.appSegmentBuffer.width,
+ inputFrame.appSegmentBuffer.height, app1Size);
+ if (appSegmentSize == 0) {
+ ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
+ return NO_INIT;
+ }
+
+ std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
+ auto exifRes = exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size);
+ if (!exifRes) {
+ ALOGE("%s: Failed to initialize ExifUtils object!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ //TODO: Use capture result metadata and static metadata to fill out the
+ //rest.
+ CameraMetadata dummyMeta;
+ exifRes = exifUtils->setFromMetadata(dummyMeta, mOutputWidth, mOutputHeight);
+ if (!exifRes) {
+ ALOGE("%s: Failed to set Exif tags using metadata and main image sizes", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ exifRes = exifUtils->setOrientation(inputFrame.orientation);
+ if (!exifRes) {
+ ALOGE("%s: ExifUtils failed to set orientation", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ exifRes = exifUtils->generateApp1();
+ if (!exifRes) {
+ ALOGE("%s: ExifUtils failed to generate APP1 segment", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ unsigned int newApp1Length = exifUtils->getApp1Length();
+ const uint8_t *newApp1Segment = exifUtils->getApp1Buffer();
+
+ //Assemble the APP1 marker buffer required by MediaCodec
+ uint8_t kExifApp1Marker[] = {'E', 'x', 'i', 'f', 0xFF, 0xE1, 0x00, 0x00};
+ kExifApp1Marker[6] = static_cast<uint8_t>(newApp1Length >> 8);
+ kExifApp1Marker[7] = static_cast<uint8_t>(newApp1Length & 0xFF);
+ size_t appSegmentBufferSize = sizeof(kExifApp1Marker) +
+ appSegmentSize - app1Size + newApp1Length;
+ uint8_t* appSegmentBuffer = new uint8_t[appSegmentBufferSize];
+ memcpy(appSegmentBuffer, kExifApp1Marker, sizeof(kExifApp1Marker));
+ memcpy(appSegmentBuffer + sizeof(kExifApp1Marker), newApp1Segment, newApp1Length);
+ if (appSegmentSize - app1Size > 0) {
+ memcpy(appSegmentBuffer + sizeof(kExifApp1Marker) + newApp1Length,
+ inputFrame.appSegmentBuffer.data + app1Size, appSegmentSize - app1Size);
+ }
+
+ sp<ABuffer> aBuffer = new ABuffer(appSegmentBuffer, appSegmentBufferSize);
+ auto res = inputFrame.muxer->writeSampleData(aBuffer, inputFrame.trackIndex,
+ timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA);
+ delete[] appSegmentBuffer;
+
+ if (res != OK) {
+ ALOGE("%s: Failed to write JPEG APP segments to muxer: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ inputFrame.appSegmentWritten = true;
+
+ return OK;
+}
+
+status_t HeicCompositeStream::processCodecInputFrame(InputFrame &inputFrame) {
+ for (auto& inputBuffer : inputFrame.codecInputBuffers) {
+ sp<MediaCodecBuffer> buffer;
+ auto res = mCodec->getInputBuffer(inputBuffer.index, &buffer);
+ if (res != OK) {
+ ALOGE("%s: Error getting codec input buffer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ // Copy one tile from source to destination.
+ size_t tileX = inputBuffer.tileIndex % mGridCols;
+ size_t tileY = inputBuffer.tileIndex / mGridCols;
+ size_t top = mGridHeight * tileY;
+ size_t left = mGridWidth * tileX;
+ size_t width = (tileX == static_cast<size_t>(mGridCols) - 1) ?
+ mOutputWidth - tileX * mGridWidth : mGridWidth;
+ size_t height = (tileY == static_cast<size_t>(mGridRows) - 1) ?
+ mOutputHeight - tileY * mGridHeight : mGridHeight;
+ ALOGV("%s: inputBuffer tileIndex [%zu, %zu], top %zu, left %zu, width %zu, height %zu",
+ __FUNCTION__, tileX, tileY, top, left, width, height);
+
+ res = copyOneYuvTile(buffer, inputFrame.yuvBuffer, top, left, width, height);
+ if (res != OK) {
+ ALOGE("%s: Failed to copy YUV tile %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ res = mCodec->queueInputBuffer(inputBuffer.index, 0, buffer->capacity(),
+ inputBuffer.timeUs, 0, nullptr /*errorDetailMsg*/);
+ if (res != OK) {
+ ALOGE("%s: Failed to queueInputBuffer to Codec: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ inputFrame.codecInputBuffers.clear();
+ return OK;
+}
+
+status_t HeicCompositeStream::processOneCodecOutputFrame(nsecs_t timestamp,
+ InputFrame &inputFrame) {
+ auto it = inputFrame.codecOutputBuffers.begin();
+ sp<MediaCodecBuffer> buffer;
+ status_t res = mCodec->getOutputBuffer(it->index, &buffer);
+ if (res != OK) {
+ ALOGE("%s: Error getting Heic codec output buffer at index %d: %s (%d)",
+ __FUNCTION__, it->index, strerror(-res), res);
+ return res;
+ }
+ if (buffer == nullptr) {
+ ALOGE("%s: Invalid Heic codec output buffer at index %d",
+ __FUNCTION__, it->index);
+ return BAD_VALUE;
+ }
+
+ sp<ABuffer> aBuffer = new ABuffer(buffer->data(), buffer->size());
+ res = inputFrame.muxer->writeSampleData(
+ aBuffer, inputFrame.trackIndex, timestamp, 0 /*flags*/);
+ if (res != OK) {
+ ALOGE("%s: Failed to write buffer index %d to muxer: %s (%d)",
+ __FUNCTION__, it->index, strerror(-res), res);
+ return res;
+ }
+
+ mCodec->releaseOutputBuffer(it->index);
+ if (inputFrame.pendingOutputTiles == 0) {
+ ALOGW("%s: Codec generated more tiles than expected!", __FUNCTION__);
+ } else {
+ inputFrame.pendingOutputTiles--;
+ }
+
+ inputFrame.codecOutputBuffers.erase(inputFrame.codecOutputBuffers.begin());
+ return OK;
+}
+
+status_t HeicCompositeStream::processCompletedInputFrame(nsecs_t timestamp,
+ InputFrame &inputFrame) {
+ sp<ANativeWindow> outputANW = mOutputSurface;
+ inputFrame.muxer->stop();
+
+ // Copy the content of the file to memory.
+ sp<GraphicBuffer> gb = GraphicBuffer::from(inputFrame.anb);
+ void* dstBuffer;
+ auto res = gb->lockAsync(GRALLOC_USAGE_SW_WRITE_OFTEN, &dstBuffer, inputFrame.fenceFd);
+ if (res != OK) {
+ ALOGE("%s: Error trying to lock output buffer fence: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ off_t fSize = lseek(inputFrame.fileFd, 0, SEEK_END);
+ if (static_cast<size_t>(fSize) > mMaxHeicBufferSize - sizeof(CameraBlob)) {
+ ALOGE("%s: Error: MediaMuxer output size %ld is larger than buffer sizer %zu",
+ __FUNCTION__, fSize, mMaxHeicBufferSize - sizeof(CameraBlob));
+ return BAD_VALUE;
+ }
+
+ lseek(inputFrame.fileFd, 0, SEEK_SET);
+ ssize_t bytesRead = read(inputFrame.fileFd, dstBuffer, fSize);
+ if (bytesRead < fSize) {
+ ALOGE("%s: Only %zd of %ld bytes read", __FUNCTION__, bytesRead, fSize);
+ return BAD_VALUE;
+ }
+
+ close(inputFrame.fileFd);
+ inputFrame.fileFd = -1;
+
+ // Fill in HEIC header
+ uint8_t *header = static_cast<uint8_t*>(dstBuffer) + mMaxHeicBufferSize - sizeof(CameraBlob);
+ struct CameraBlob *blobHeader = (struct CameraBlob *)header;
+ // Must be in sync with CAMERA3_HEIC_BLOB_ID in android_media_Utils.cpp
+ blobHeader->blobId = static_cast<CameraBlobId>(0x00FE);
+ blobHeader->blobSize = fSize;
+
+ res = native_window_set_buffers_timestamp(mOutputSurface.get(), timestamp);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
+ __FUNCTION__, getStreamId(), strerror(-res), res);
+ return res;
+ }
+
+ res = outputANW->queueBuffer(mOutputSurface.get(), inputFrame.anb, /*fence*/ -1);
+ if (res != OK) {
+ ALOGE("%s: Failed to queueBuffer to Heic stream: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+ inputFrame.anb = nullptr;
+
+ return OK;
+}
+
+
+void HeicCompositeStream::releaseInputFrameLocked(InputFrame *inputFrame /*out*/) {
+ if (inputFrame == nullptr) {
+ return;
+ }
+
+ if (inputFrame->appSegmentBuffer.data != nullptr) {
+ mAppSegmentConsumer->unlockBuffer(inputFrame->appSegmentBuffer);
+ inputFrame->appSegmentBuffer.data = nullptr;
+ mAppSegmentBufferAcquired = false;
+ }
+
+ while (!inputFrame->codecOutputBuffers.empty()) {
+ auto it = inputFrame->codecOutputBuffers.begin();
+ ALOGV("%s: releaseOutputBuffer index %d", __FUNCTION__, it->index);
+ mCodec->releaseOutputBuffer(it->index);
+ inputFrame->codecOutputBuffers.erase(it);
+ }
+
+ if (inputFrame->yuvBuffer.data != nullptr) {
+ mMainImageConsumer->unlockBuffer(inputFrame->yuvBuffer);
+ inputFrame->yuvBuffer.data = nullptr;
+ mYuvBufferAcquired = false;
+ }
+
+ while (!inputFrame->codecInputBuffers.empty()) {
+ auto it = inputFrame->codecInputBuffers.begin();
+ inputFrame->codecInputBuffers.erase(it);
+ }
+
+ if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) {
+ notifyError(inputFrame->frameNumber);
+ inputFrame->errorNotified = true;
+ }
+
+ if (inputFrame->fileFd >= 0) {
+ close(inputFrame->fileFd);
+ inputFrame->fileFd = -1;
+ }
+
+ if (inputFrame->anb != nullptr) {
+ sp<ANativeWindow> outputANW = mOutputSurface;
+ outputANW->cancelBuffer(mOutputSurface.get(), inputFrame->anb, /*fence*/ -1);
+ inputFrame->anb = nullptr;
+ }
+}
+
+void HeicCompositeStream::releaseInputFramesLocked(int64_t currentTs) {
+ auto it = mPendingInputFrames.begin();
+ while (it != mPendingInputFrames.end()) {
+ if (it->first <= currentTs) {
+ releaseInputFrameLocked(&it->second);
+ it = mPendingInputFrames.erase(it);
+ } else {
+ it++;
+ }
+ }
+}
+
+status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height,
+ const sp<CameraDeviceBase>& cameraDevice) {
+ ALOGV("%s", __FUNCTION__);
+
+ bool useGrid = false;
+ bool isSizeSupported = isSizeSupportedByHeifEncoder(width, height,
+ &mUseHeic, &useGrid, nullptr);
+ if (!isSizeSupported) {
+ ALOGE("%s: Encoder doesnt' support size %u x %u!",
+ __FUNCTION__, width, height);
+ return BAD_VALUE;
+ }
+
+ // Create Looper for MediaCodec.
+ auto desiredMime = mUseHeic ? MIMETYPE_IMAGE_ANDROID_HEIC : MIMETYPE_VIDEO_HEVC;
+ mCodecLooper = new ALooper;
+ mCodecLooper->setName("Camera3-HeicComposite-MediaCodecLooper");
+ status_t res = mCodecLooper->start(
+ false, // runOnCallingThread
+ false, // canCallJava
+ PRIORITY_AUDIO);
+ if (res != OK) {
+ ALOGE("%s: Failed to start codec looper: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return NO_INIT;
+ }
+
+ // Create HEIC/HEVC codec.
+ mCodec = MediaCodec::CreateByType(mCodecLooper, desiredMime, true /*encoder*/);
+ if (mCodec == nullptr) {
+ ALOGE("%s: Failed to create codec for %s", __FUNCTION__, desiredMime);
+ return NO_INIT;
+ }
+
+ // Create Looper and handler for Codec callback.
+ mCodecCallbackHandler = new CodecCallbackHandler(this);
+ if (mCodecCallbackHandler == nullptr) {
+ ALOGE("%s: Failed to create codec callback handler", __FUNCTION__);
+ return NO_MEMORY;
+ }
+ mCallbackLooper = new ALooper;
+ mCallbackLooper->setName("Camera3-HeicComposite-MediaCodecCallbackLooper");
+ res = mCallbackLooper->start(
+ false, // runOnCallingThread
+ false, // canCallJava
+ PRIORITY_AUDIO);
+ if (res != OK) {
+ ALOGE("%s: Failed to start media callback looper: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return NO_INIT;
+ }
+ mCallbackLooper->registerHandler(mCodecCallbackHandler);
+
+ mAsyncNotify = new AMessage(kWhatCallbackNotify, mCodecCallbackHandler);
+ res = mCodec->setCallback(mAsyncNotify);
+ if (res != OK) {
+ ALOGE("%s: Failed to set MediaCodec callback: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ // Create output format and configure the Codec.
+ sp<AMessage> outputFormat = new AMessage();
+ outputFormat->setString(KEY_MIME, desiredMime);
+ outputFormat->setInt32(KEY_BITRATE_MODE, BITRATE_MODE_CQ);
+ outputFormat->setInt32(KEY_QUALITY, kDefaultJpegQuality);
+ // Ask codec to skip timestamp check and encode all frames.
+ outputFormat->setInt64("max-pts-gap-to-encoder", kNoFrameDropMaxPtsGap);
+
+ int32_t gridWidth, gridHeight, gridRows, gridCols;
+ if (useGrid || mUseHeic) {
+ gridWidth = HeicEncoderInfoManager::kGridWidth;
+ gridHeight = HeicEncoderInfoManager::kGridHeight;
+ gridRows = (height + gridHeight - 1)/gridHeight;
+ gridCols = (width + gridWidth - 1)/gridWidth;
+
+ if (mUseHeic) {
+ outputFormat->setInt32(KEY_TILE_WIDTH, gridWidth);
+ outputFormat->setInt32(KEY_TILE_HEIGHT, gridHeight);
+ outputFormat->setInt32(KEY_GRID_COLUMNS, gridCols);
+ outputFormat->setInt32(KEY_GRID_ROWS, gridRows);
+ }
+
+ } else {
+ gridWidth = width;
+ gridHeight = height;
+ gridRows = 1;
+ gridCols = 1;
+ }
+
+ outputFormat->setInt32(KEY_WIDTH, !useGrid ? width : gridWidth);
+ outputFormat->setInt32(KEY_HEIGHT, !useGrid ? height : gridHeight);
+ outputFormat->setInt32(KEY_I_FRAME_INTERVAL, 0);
+ outputFormat->setInt32(KEY_COLOR_FORMAT,
+ useGrid ? COLOR_FormatYUV420Flexible : COLOR_FormatSurface);
+ outputFormat->setInt32(KEY_FRAME_RATE, gridRows * gridCols);
+ // This only serves as a hint to encoder when encoding is not real-time.
+ outputFormat->setInt32(KEY_OPERATING_RATE, useGrid ? kGridOpRate : kNoGridOpRate);
+
+ res = mCodec->configure(outputFormat, nullptr /*nativeWindow*/,
+ nullptr /*crypto*/, CONFIGURE_FLAG_ENCODE);
+ if (res != OK) {
+ ALOGE("%s: Failed to configure codec: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ mGridWidth = gridWidth;
+ mGridHeight = gridHeight;
+ mGridRows = gridRows;
+ mGridCols = gridCols;
+ mUseGrid = useGrid;
+ mOutputWidth = width;
+ mOutputHeight = height;
+ mAppSegmentMaxSize = calcAppSegmentMaxSize(cameraDevice->info());
+ mMaxHeicBufferSize = mOutputWidth * mOutputHeight * 3 / 2 + mAppSegmentMaxSize;
+
+ return OK;
+}
+
+void HeicCompositeStream::deinitCodec() {
+ ALOGV("%s", __FUNCTION__);
+ if (mCodec != nullptr) {
+ mCodec->stop();
+ mCodec->release();
+ mCodec.clear();
+ }
+
+ if (mCodecLooper != nullptr) {
+ mCodecLooper->stop();
+ mCodecLooper.clear();
+ }
+
+ if (mCallbackLooper != nullptr) {
+ mCallbackLooper->stop();
+ mCallbackLooper.clear();
+ }
+
+ mAsyncNotify.clear();
+ mFormat.clear();
+}
+
+// Return the size of the complete list of app segment, 0 indicates failure
+size_t HeicCompositeStream::findAppSegmentsSize(const uint8_t* appSegmentBuffer,
+ size_t maxSize, size_t *app1SegmentSize) {
+ if (appSegmentBuffer == nullptr || app1SegmentSize == nullptr) {
+ ALOGE("%s: Invalid input appSegmentBuffer %p, app1SegmentSize %p",
+ __FUNCTION__, appSegmentBuffer, app1SegmentSize);
+ return 0;
+ }
+
+ size_t expectedSize = 0;
+ // First check for EXIF transport header at the end of the buffer
+ const uint8_t *header = appSegmentBuffer + (maxSize - sizeof(struct CameraBlob));
+ const struct CameraBlob *blob = (const struct CameraBlob*)(header);
+ if (blob->blobId != CameraBlobId::JPEG_APP_SEGMENTS) {
+ ALOGE("%s: Invalid EXIF blobId %hu", __FUNCTION__, blob->blobId);
+ return 0;
+ }
+
+ expectedSize = blob->blobSize;
+ if (expectedSize == 0 || expectedSize > maxSize - sizeof(struct CameraBlob)) {
+ ALOGE("%s: Invalid blobSize %zu.", __FUNCTION__, expectedSize);
+ return 0;
+ }
+
+ uint32_t totalSize = 0;
+
+ // Verify APP1 marker (mandatory)
+ uint8_t app1Marker[] = {0xFF, 0xE1};
+ if (memcmp(appSegmentBuffer, app1Marker, sizeof(app1Marker))) {
+ ALOGE("%s: Invalid APP1 marker: %x, %x", __FUNCTION__,
+ appSegmentBuffer[0], appSegmentBuffer[1]);
+ return 0;
+ }
+ totalSize += sizeof(app1Marker);
+
+ uint16_t app1Size = (static_cast<uint16_t>(appSegmentBuffer[totalSize]) << 8) +
+ appSegmentBuffer[totalSize+1];
+ totalSize += app1Size;
+
+ ALOGV("%s: Expected APP segments size %zu, APP1 segment size %u",
+ __FUNCTION__, expectedSize, app1Size);
+ while (totalSize < expectedSize) {
+ if (appSegmentBuffer[totalSize] != 0xFF ||
+ appSegmentBuffer[totalSize+1] <= 0xE1 ||
+ appSegmentBuffer[totalSize+1] > 0xEF) {
+ // Invalid APPn marker
+ ALOGE("%s: Invalid APPn marker: %x, %x", __FUNCTION__,
+ appSegmentBuffer[totalSize], appSegmentBuffer[totalSize+1]);
+ return 0;
+ }
+ totalSize += 2;
+
+ uint16_t appnSize = (static_cast<uint16_t>(appSegmentBuffer[totalSize]) << 8) +
+ appSegmentBuffer[totalSize+1];
+ totalSize += appnSize;
+ }
+
+ if (totalSize != expectedSize) {
+ ALOGE("%s: Invalid JPEG APP segments: totalSize %u vs expected size %zu",
+ __FUNCTION__, totalSize, expectedSize);
+ return 0;
+ }
+
+ *app1SegmentSize = app1Size + sizeof(app1Marker);
+ return expectedSize;
+}
+
+int64_t HeicCompositeStream::findTimestampInNsLocked(int64_t timeInUs) {
+ for (const auto& fn : mFrameNumberMap) {
+ if (timeInUs == ns2us(fn.second)) {
+ return fn.second;
+ }
+ }
+ for (const auto& inputFrame : mPendingInputFrames) {
+ if (timeInUs == ns2us(inputFrame.first)) {
+ return inputFrame.first;
+ }
+ }
+ return -1;
+}
+
+status_t HeicCompositeStream::copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
+ const CpuConsumer::LockedBuffer& yuvBuffer,
+ size_t top, size_t left, size_t width, size_t height) {
+ ATRACE_CALL();
+
+ // Get stride information for codecBuffer
+ sp<ABuffer> imageData;
+ if (!codecBuffer->meta()->findBuffer("image-data", &imageData)) {
+ ALOGE("%s: Codec input buffer is not for image data!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (imageData->size() != sizeof(MediaImage2)) {
+ ALOGE("%s: Invalid codec input image size %zu, expected %zu",
+ __FUNCTION__, imageData->size(), sizeof(MediaImage2));
+ return BAD_VALUE;
+ }
+ MediaImage2* imageInfo = reinterpret_cast<MediaImage2*>(imageData->data());
+ if (imageInfo->mType != MediaImage2::MEDIA_IMAGE_TYPE_YUV ||
+ imageInfo->mBitDepth != 8 ||
+ imageInfo->mBitDepthAllocated != 8 ||
+ imageInfo->mNumPlanes != 3) {
+ ALOGE("%s: Invalid codec input image info: mType %d, mBitDepth %d, "
+ "mBitDepthAllocated %d, mNumPlanes %d!", __FUNCTION__,
+ imageInfo->mType, imageInfo->mBitDepth,
+ imageInfo->mBitDepthAllocated, imageInfo->mNumPlanes);
+ return BAD_VALUE;
+ }
+
+ ALOGV("%s: yuvBuffer chromaStep %d, chromaStride %d",
+ __FUNCTION__, yuvBuffer.chromaStep, yuvBuffer.chromaStride);
+ ALOGV("%s: U offset %u, V offset %u, U rowInc %d, V rowInc %d, U colInc %d, V colInc %d",
+ __FUNCTION__, imageInfo->mPlane[MediaImage2::U].mOffset,
+ imageInfo->mPlane[MediaImage2::V].mOffset,
+ imageInfo->mPlane[MediaImage2::U].mRowInc,
+ imageInfo->mPlane[MediaImage2::V].mRowInc,
+ imageInfo->mPlane[MediaImage2::U].mColInc,
+ imageInfo->mPlane[MediaImage2::V].mColInc);
+
+ // Y
+ for (auto row = top; row < top+height; row++) {
+ uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::Y].mOffset +
+ imageInfo->mPlane[MediaImage2::Y].mRowInc * (row - top);
+ memcpy(dst, yuvBuffer.data+row*yuvBuffer.stride+left, width);
+ }
+
+ // U is Cb, V is Cr
+ bool codecUPlaneFirst = imageInfo->mPlane[MediaImage2::V].mOffset >
+ imageInfo->mPlane[MediaImage2::U].mOffset;
+ uint32_t codecUvOffsetDiff = codecUPlaneFirst ?
+ imageInfo->mPlane[MediaImage2::V].mOffset - imageInfo->mPlane[MediaImage2::U].mOffset :
+ imageInfo->mPlane[MediaImage2::U].mOffset - imageInfo->mPlane[MediaImage2::V].mOffset;
+ bool isCodecUvSemiplannar = (codecUvOffsetDiff == 1) &&
+ (imageInfo->mPlane[MediaImage2::U].mRowInc ==
+ imageInfo->mPlane[MediaImage2::V].mRowInc) &&
+ (imageInfo->mPlane[MediaImage2::U].mColInc == 2) &&
+ (imageInfo->mPlane[MediaImage2::V].mColInc == 2);
+ bool isCodecUvPlannar =
+ ((codecUPlaneFirst && codecUvOffsetDiff >=
+ imageInfo->mPlane[MediaImage2::U].mRowInc * imageInfo->mHeight/2) ||
+ ((!codecUPlaneFirst && codecUvOffsetDiff >=
+ imageInfo->mPlane[MediaImage2::V].mRowInc * imageInfo->mHeight/2))) &&
+ imageInfo->mPlane[MediaImage2::U].mColInc == 1 &&
+ imageInfo->mPlane[MediaImage2::V].mColInc == 1;
+ bool cameraUPlaneFirst = yuvBuffer.dataCr > yuvBuffer.dataCb;
+
+ if (isCodecUvSemiplannar && yuvBuffer.chromaStep == 2 &&
+ (codecUPlaneFirst == cameraUPlaneFirst)) {
+ // UV semiplannar
+ // The chrome plane could be either Cb first, or Cr first. Take the
+ // smaller address.
+ uint8_t *src = std::min(yuvBuffer.dataCb, yuvBuffer.dataCr);
+ MediaImage2::PlaneIndex dstPlane = codecUvOffsetDiff > 0 ? MediaImage2::U : MediaImage2::V;
+ for (auto row = top/2; row < (top+height)/2; row++) {
+ uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[dstPlane].mOffset +
+ imageInfo->mPlane[dstPlane].mRowInc * (row - top/2);
+ memcpy(dst, src+row*yuvBuffer.chromaStride+left, width);
+ }
+ } else if (isCodecUvPlannar && yuvBuffer.chromaStep == 1) {
+ // U plane
+ for (auto row = top/2; row < (top+height)/2; row++) {
+ uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::U].mOffset +
+ imageInfo->mPlane[MediaImage2::U].mRowInc * (row - top/2);
+ memcpy(dst, yuvBuffer.dataCb+row*yuvBuffer.chromaStride+left/2, width/2);
+ }
+
+ // V plane
+ for (auto row = top/2; row < (top+height)/2; row++) {
+ uint8_t *dst = codecBuffer->data() + imageInfo->mPlane[MediaImage2::V].mOffset +
+ imageInfo->mPlane[MediaImage2::V].mRowInc * (row - top/2);
+ memcpy(dst, yuvBuffer.dataCr+row*yuvBuffer.chromaStride+left/2, width/2);
+ }
+ } else {
+ // Convert between semiplannar and plannar
+ uint8_t *dst = codecBuffer->data();
+ for (auto row = top/2; row < (top+height)/2; row++) {
+ for (auto col = left/2; col < (left+width)/2; col++) {
+ // U/Cb
+ int32_t dstIndex = imageInfo->mPlane[MediaImage2::U].mOffset +
+ imageInfo->mPlane[MediaImage2::U].mRowInc * (row - top/2) +
+ imageInfo->mPlane[MediaImage2::U].mColInc * (col - left/2);
+ int32_t srcIndex = row * yuvBuffer.chromaStride + yuvBuffer.chromaStep * col;
+ dst[dstIndex] = yuvBuffer.dataCb[srcIndex];
+
+ // V/Cr
+ dstIndex = imageInfo->mPlane[MediaImage2::V].mOffset +
+ imageInfo->mPlane[MediaImage2::V].mRowInc * (row - top/2) +
+ imageInfo->mPlane[MediaImage2::V].mColInc * (col - left/2);
+ srcIndex = row * yuvBuffer.chromaStride + yuvBuffer.chromaStep * col;
+ dst[dstIndex] = yuvBuffer.dataCr[srcIndex];
+ }
+ }
+ }
+ return OK;
+}
+
+size_t HeicCompositeStream::calcAppSegmentMaxSize(const CameraMetadata& info) {
+ camera_metadata_ro_entry_t entry = info.find(ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT);
+ size_t maxAppsSegment = 1;
+ if (entry.count > 0) {
+ maxAppsSegment = entry.data.u8[0] < 1 ? 1 :
+ entry.data.u8[0] > 16 ? 16 : entry.data.u8[0];
+ }
+ return maxAppsSegment * (2 + 0xFFFF) + sizeof(struct CameraBlob);
+}
+
+bool HeicCompositeStream::threadLoop() {
+ int64_t currentTs = INT64_MAX;
+ bool newInputAvailable = false;
+
+ {
+ Mutex::Autolock l(mMutex);
+ if (mErrorState) {
+ // In case we landed in error state, return any pending buffers and
+ // halt all further processing.
+ compilePendingInputLocked();
+ releaseInputFramesLocked(currentTs);
+ return false;
+ }
+
+
+ while (!newInputAvailable) {
+ compilePendingInputLocked();
+ newInputAvailable = getNextReadyInputLocked(¤tTs);
+
+ if (!newInputAvailable) {
+ auto failingFrameNumber = getNextFailingInputLocked(¤tTs);
+ if (failingFrameNumber >= 0) {
+ // We cannot erase 'mPendingInputFrames[currentTs]' at this point because it is
+ // possible for two internal stream buffers to fail. In such scenario the
+ // composite stream should notify the client about a stream buffer error only
+ // once and this information is kept within 'errorNotified'.
+ // Any present failed input frames will be removed on a subsequent call to
+ // 'releaseInputFramesLocked()'.
+ releaseInputFrameLocked(&mPendingInputFrames[currentTs]);
+ currentTs = INT64_MAX;
+ }
+
+ auto ret = mInputReadyCondition.waitRelative(mMutex, kWaitDuration);
+ if (ret == TIMED_OUT) {
+ return true;
+ } else if (ret != OK) {
+ ALOGE("%s: Timed wait on condition failed: %s (%d)", __FUNCTION__,
+ strerror(-ret), ret);
+ return false;
+ }
+ }
+ }
+ }
+
+ auto res = processInputFrame(currentTs, mPendingInputFrames[currentTs]);
+ Mutex::Autolock l(mMutex);
+ if (res != OK) {
+ ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)",
+ __FUNCTION__, currentTs, strerror(-res), res);
+ mPendingInputFrames[currentTs].error = true;
+ }
+
+ if (mPendingInputFrames[currentTs].error ||
+ (mPendingInputFrames[currentTs].appSegmentWritten &&
+ mPendingInputFrames[currentTs].pendingOutputTiles == 0)) {
+ releaseInputFramesLocked(currentTs);
+ }
+
+ return true;
+}
+
+bool HeicCompositeStream::onStreamBufferError(const CaptureResultExtras& resultExtras) {
+ bool res = false;
+ // Buffer errors concerning internal composite streams should not be directly visible to
+ // camera clients. They must only receive a single buffer error with the public composite
+ // stream id.
+ if ((resultExtras.errorStreamId == mAppSegmentStreamId) ||
+ (resultExtras.errorStreamId == mMainImageStreamId)) {
+ flagAnErrorFrameNumber(resultExtras.frameNumber);
+ res = true;
+ }
+
+ return res;
+}
+
+void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
+ sp<HeicCompositeStream> parent = mParent.promote();
+ if (parent == nullptr) return;
+
+ switch (msg->what()) {
+ case kWhatCallbackNotify: {
+ int32_t cbID;
+ if (!msg->findInt32("callbackID", &cbID)) {
+ ALOGE("kWhatCallbackNotify: callbackID is expected.");
+ break;
+ }
+
+ ALOGV("kWhatCallbackNotify: cbID = %d", cbID);
+
+ switch (cbID) {
+ case MediaCodec::CB_INPUT_AVAILABLE: {
+ int32_t index;
+ if (!msg->findInt32("index", &index)) {
+ ALOGE("CB_INPUT_AVAILABLE: index is expected.");
+ break;
+ }
+ parent->onHeicInputFrameAvailable(index);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_AVAILABLE: {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+
+ if (!msg->findInt32("index", &index)) {
+ ALOGE("CB_OUTPUT_AVAILABLE: index is expected.");
+ break;
+ }
+ if (!msg->findSize("offset", &offset)) {
+ ALOGE("CB_OUTPUT_AVAILABLE: offset is expected.");
+ break;
+ }
+ if (!msg->findSize("size", &size)) {
+ ALOGE("CB_OUTPUT_AVAILABLE: size is expected.");
+ break;
+ }
+ if (!msg->findInt64("timeUs", &timeUs)) {
+ ALOGE("CB_OUTPUT_AVAILABLE: timeUs is expected.");
+ break;
+ }
+ if (!msg->findInt32("flags", &flags)) {
+ ALOGE("CB_OUTPUT_AVAILABLE: flags is expected.");
+ break;
+ }
+
+ CodecOutputBufferInfo bufferInfo = {
+ index,
+ (int32_t)offset,
+ (int32_t)size,
+ timeUs,
+ (uint32_t)flags};
+
+ parent->onHeicOutputFrameAvailable(bufferInfo);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_FORMAT_CHANGED: {
+ sp<AMessage> format;
+ if (!msg->findMessage("format", &format)) {
+ ALOGE("CB_OUTPUT_FORMAT_CHANGED: format is expected.");
+ break;
+ }
+
+ parent->onHeicFormatChanged(format);
+ break;
+ }
+
+ case MediaCodec::CB_ERROR: {
+ status_t err;
+ int32_t actionCode;
+ AString detail;
+ if (!msg->findInt32("err", &err)) {
+ ALOGE("CB_ERROR: err is expected.");
+ break;
+ }
+ if (!msg->findInt32("action", &actionCode)) {
+ ALOGE("CB_ERROR: action is expected.");
+ break;
+ }
+ msg->findString("detail", &detail);
+ ALOGE("Codec reported error(0x%x), actionCode(%d), detail(%s)",
+ err, actionCode, detail.c_str());
+
+ parent->onHeicCodecError();
+ break;
+ }
+
+ default: {
+ ALOGE("kWhatCallbackNotify: callbackID(%d) is unexpected.", cbID);
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ ALOGE("shouldn't be here");
+ break;
+ }
+}
+
+}; // namespace camera3
+}; // namespace android
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
new file mode 100644
index 0000000..0a76256
--- /dev/null
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H
+#define ANDROID_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H
+
+#include <queue>
+
+#include <gui/IProducerListener.h>
+#include <gui/CpuConsumer.h>
+
+#include <media/hardware/VideoAPI.h>
+#include <media/MediaCodecBuffer.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/MediaMuxer.h>
+
+#include "CompositeStream.h"
+
+namespace android {
+namespace camera3 {
+
+class HeicCompositeStream : public CompositeStream, public Thread,
+ public CpuConsumer::FrameAvailableListener {
+public:
+ HeicCompositeStream(wp<CameraDeviceBase> device,
+ wp<hardware::camera2::ICameraDeviceCallbacks> cb);
+ ~HeicCompositeStream() override;
+
+ static bool isHeicCompositeStream(const sp<Surface> &surface);
+
+ status_t createInternalStreams(const std::vector<sp<Surface>>& consumers,
+ bool hasDeferredConsumer, uint32_t width, uint32_t height, int format,
+ camera3_stream_rotation_t rotation, int *id, const String8& physicalCameraId,
+ std::vector<int> *surfaceIds, int streamSetId, bool isShared) override;
+
+ status_t deleteInternalStreams() override;
+
+ status_t configureStream() override;
+
+ status_t insertGbp(SurfaceMap* /*out*/outSurfaceMap, Vector<int32_t>* /*out*/outputStreamIds,
+ int32_t* /*out*/currentStreamId) override;
+
+ void onShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) override;
+
+ int getStreamId() override { return mMainImageStreamId; }
+
+ // Use onShutter to keep track of frame number <-> timestamp mapping.
+ void onBufferReleased(const BufferInfo& bufferInfo) override;
+ void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& settings) override;
+
+ // CpuConsumer listener implementation
+ void onFrameAvailable(const BufferItem& item) override;
+
+ // Return stream information about the internal camera streams
+ static status_t getCompositeStreamInfo(const OutputStreamInfo &streamInfo,
+ const CameraMetadata& ch, std::vector<OutputStreamInfo>* compositeOutput /*out*/);
+
+ static bool isSizeSupportedByHeifEncoder(int32_t width, int32_t height,
+ bool* useHeic, bool* useGrid, int64_t* stall);
+ static bool isInMemoryTempFileSupported();
+protected:
+
+ bool threadLoop() override;
+ bool onStreamBufferError(const CaptureResultExtras& resultExtras) override;
+ void onResultError(const CaptureResultExtras& /*resultExtras*/) override {}
+
+private:
+ //
+ // HEIC/HEVC Codec related structures, utility functions, and callbacks
+ //
+ struct CodecOutputBufferInfo {
+ int32_t index;
+ int32_t offset;
+ int32_t size;
+ int64_t timeUs;
+ uint32_t flags;
+ };
+
+ struct CodecInputBufferInfo {
+ int32_t index;
+ int64_t timeUs;
+ size_t tileIndex;
+ };
+
+ class CodecCallbackHandler : public AHandler {
+ public:
+ explicit CodecCallbackHandler(wp<HeicCompositeStream> parent) {
+ mParent = parent;
+ }
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+ private:
+ wp<HeicCompositeStream> mParent;
+ };
+
+ enum {
+ kWhatCallbackNotify,
+ };
+
+ bool mUseHeic;
+ sp<MediaCodec> mCodec;
+ sp<ALooper> mCodecLooper, mCallbackLooper;
+ sp<CodecCallbackHandler> mCodecCallbackHandler;
+ sp<AMessage> mAsyncNotify;
+ sp<AMessage> mFormat;
+ size_t mNumOutputTiles;
+
+ int32_t mOutputWidth, mOutputHeight;
+ size_t mMaxHeicBufferSize;
+ int32_t mGridWidth, mGridHeight;
+ size_t mGridRows, mGridCols;
+ bool mUseGrid; // Whether to use framework YUV frame tiling.
+
+ static const int64_t kNoFrameDropMaxPtsGap = -1000000;
+ static const int32_t kNoGridOpRate = 30;
+ static const int32_t kGridOpRate = 120;
+
+ void onHeicOutputFrameAvailable(const CodecOutputBufferInfo& bufferInfo);
+ void onHeicInputFrameAvailable(int32_t index); // Only called for YUV input mode.
+ void onHeicFormatChanged(sp<AMessage>& newFormat);
+ void onHeicCodecError();
+
+ status_t initializeCodec(uint32_t width, uint32_t height,
+ const sp<CameraDeviceBase>& cameraDevice);
+ void deinitCodec();
+
+ //
+ // Composite stream related structures, utility functions and callbacks.
+ //
+ struct InputFrame {
+ int32_t orientation;
+ int32_t quality;
+
+ CpuConsumer::LockedBuffer appSegmentBuffer;
+ std::vector<CodecOutputBufferInfo> codecOutputBuffers;
+
+ // Fields that are only applicable to HEVC tiling.
+ CpuConsumer::LockedBuffer yuvBuffer;
+ std::vector<CodecInputBufferInfo> codecInputBuffers;
+
+ bool error;
+ bool errorNotified;
+ int64_t frameNumber;
+
+ sp<MediaMuxer> muxer;
+ int fenceFd;
+ int fileFd;
+ ssize_t trackIndex;
+ ANativeWindowBuffer *anb;
+
+ bool appSegmentWritten;
+ size_t pendingOutputTiles;
+ size_t codecInputCounter;
+
+ InputFrame() : orientation(0), quality(kDefaultJpegQuality), error(false),
+ errorNotified(false), frameNumber(-1), fenceFd(-1), fileFd(-1),
+ trackIndex(-1), anb(nullptr), appSegmentWritten(false),
+ pendingOutputTiles(0), codecInputCounter(0) { }
+ };
+
+ void compilePendingInputLocked();
+ // Find first complete and valid frame with smallest timestamp
+ bool getNextReadyInputLocked(int64_t *currentTs /*out*/);
+ // Find next failing frame number with smallest timestamp and return respective frame number
+ int64_t getNextFailingInputLocked(int64_t *currentTs /*out*/);
+
+ status_t processInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t processCodecInputFrame(InputFrame &inputFrame);
+ status_t startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t processAppSegment(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t processOneCodecOutputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t processCompletedInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+
+ void releaseInputFrameLocked(InputFrame *inputFrame /*out*/);
+ void releaseInputFramesLocked(int64_t currentTs);
+
+ size_t findAppSegmentsSize(const uint8_t* appSegmentBuffer, size_t maxSize,
+ size_t* app1SegmentSize);
+ int64_t findTimestampInNsLocked(int64_t timeInUs);
+ status_t copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
+ const CpuConsumer::LockedBuffer& yuvBuffer,
+ size_t top, size_t left, size_t width, size_t height);
+ static size_t calcAppSegmentMaxSize(const CameraMetadata& info);
+
+ static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ static const int32_t kDefaultJpegQuality = 99;
+ static const auto kJpegDataSpace = HAL_DATASPACE_V0_JFIF;
+ static const android_dataspace kAppSegmentDataSpace =
+ static_cast<android_dataspace>(HAL_DATASPACE_JPEG_APP_SEGMENTS);
+ static const android_dataspace kHeifDataSpace =
+ static_cast<android_dataspace>(HAL_DATASPACE_HEIF);
+
+ int mAppSegmentStreamId, mAppSegmentSurfaceId;
+ sp<CpuConsumer> mAppSegmentConsumer;
+ sp<Surface> mAppSegmentSurface;
+ bool mAppSegmentBufferAcquired;
+ size_t mAppSegmentMaxSize;
+
+ int mMainImageStreamId, mMainImageSurfaceId;
+ sp<Surface> mMainImageSurface;
+ sp<CpuConsumer> mMainImageConsumer; // Only applicable for HEVC codec.
+ bool mYuvBufferAcquired; // Only applicable to HEVC codec
+
+ sp<Surface> mOutputSurface;
+ sp<ProducerListener> mProducerListener;
+
+
+ // Map from frame number to JPEG setting of orientation+quality
+ std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByFrameNumber;
+ // Map from timestamp to JPEG setting of orientation+quality
+ std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByTimestamp;
+
+ // Keep all incoming APP segment Blob buffer pending further processing.
+ std::vector<int64_t> mInputAppSegmentBuffers;
+
+ // Keep all incoming HEIC blob buffer pending further processing.
+ std::vector<CodecOutputBufferInfo> mCodecOutputBuffers;
+ std::queue<int64_t> mCodecOutputBufferTimestamps;
+ size_t mOutputBufferCounter;
+
+ // Keep all incoming Yuv buffer pending tiling and encoding (for HEVC YUV tiling only)
+ std::vector<int64_t> mInputYuvBuffers;
+ // Keep all codec input buffers ready to be filled out (for HEVC YUV tiling only)
+ std::vector<int32_t> mCodecInputBuffers;
+
+ // Artificial strictly incremental YUV grid timestamp to make encoder happy.
+ int64_t mGridTimestampUs;
+
+ // In most common use case, entries are accessed in order.
+ std::map<int64_t, InputFrame> mPendingInputFrames;
+};
+
+}; // namespace camera3
+}; // namespace android
+
+#endif //ANDROID_SERVERS_CAMERA_CAMERA3_HEIC_COMPOSITE_STREAM_H
diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp
new file mode 100644
index 0000000..ed9be6e
--- /dev/null
+++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HeicEncoderInfoManager"
+//#define LOG_NDEBUG 0
+
+#include <cstdint>
+#include <regex>
+
+#include <cutils/properties.h>
+#include <log/log_main.h>
+#include <system/graphics.h>
+
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/ABuffer.h>
+
+#include "HeicEncoderInfoManager.h"
+
+namespace android {
+namespace camera3 {
+
+HeicEncoderInfoManager::HeicEncoderInfoManager() :
+ mIsInited(false),
+ mMinSizeHeic(0, 0),
+ mMaxSizeHeic(INT32_MAX, INT32_MAX),
+ mHasHEVC(false),
+ mHasHEIC(false),
+ mDisableGrid(false) {
+ if (initialize() == OK) {
+ mIsInited = true;
+ }
+}
+
+HeicEncoderInfoManager::~HeicEncoderInfoManager() {
+}
+
+bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool* useHeic,
+ bool* useGrid, int64_t* stall) const {
+ if (useHeic == nullptr || useGrid == nullptr) {
+ ALOGE("%s: invalid parameters: useHeic %p, useGrid %p",
+ __FUNCTION__, useHeic, useGrid);
+ return false;
+ }
+ if (!mIsInited) return false;
+
+ bool chooseHeic = false, enableGrid = true;
+ if (mHasHEIC && width >= mMinSizeHeic.first &&
+ height >= mMinSizeHeic.second && width <= mMaxSizeHeic.first &&
+ height <= mMaxSizeHeic.second) {
+ chooseHeic = true;
+ enableGrid = false;
+ } else if (mHasHEVC) {
+ bool fullSizeSupportedByHevc = (width >= mMinSizeHevc.first &&
+ height >= mMinSizeHevc.second &&
+ width <= mMaxSizeHevc.first &&
+ height <= mMaxSizeHevc.second);
+ if (fullSizeSupportedByHevc && (mDisableGrid ||
+ (width <= 1920 && height <= 1080))) {
+ enableGrid = false;
+ }
+ } else {
+ // No encoder available for the requested size.
+ return false;
+ }
+
+ if (stall != nullptr) {
+ // Find preferred encoder which advertise
+ // "measured-frame-rate-WIDTHxHEIGHT-range" key.
+ const FrameRateMaps& maps =
+ (chooseHeic && mHeicFrameRateMaps.size() > 0) ?
+ mHeicFrameRateMaps : mHevcFrameRateMaps;
+ const auto& closestSize = findClosestSize(maps, width, height);
+ if (closestSize == maps.end()) {
+ // The "measured-frame-rate-WIDTHxHEIGHT-range" key is optional.
+ // Hardcode to some default value (3.33ms * tile count) based on resolution.
+ *stall = 3333333LL * width * height / (kGridWidth * kGridHeight);
+ return true;
+ }
+
+ // Derive stall durations based on average fps of the closest size.
+ constexpr int64_t NSEC_PER_SEC = 1000000000LL;
+ int32_t avgFps = (closestSize->second.first + closestSize->second.second)/2;
+ float ratio = 1.0f * width * height /
+ (closestSize->first.first * closestSize->first.second);
+ *stall = ratio * NSEC_PER_SEC / avgFps;
+ }
+
+ *useHeic = chooseHeic;
+ *useGrid = enableGrid;
+ return true;
+}
+
+status_t HeicEncoderInfoManager::initialize() {
+ mDisableGrid = property_get_bool("camera.heic.disable_grid", false);
+ sp<IMediaCodecList> codecsList = MediaCodecList::getInstance();
+ if (codecsList == nullptr) {
+ // No media codec available.
+ return OK;
+ }
+
+ sp<AMessage> heicDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+ sp<AMessage> hevcDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC);
+
+ if (hevcDetails == nullptr) {
+ if (heicDetails != nullptr) {
+ ALOGE("%s: Device must support HEVC codec if HEIC codec is available!",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+ return OK;
+ }
+
+ // Check CQ mode for HEVC codec
+ {
+ AString bitrateModes;
+ auto hasItem = hevcDetails->findString("feature-bitrate-modes", &bitrateModes);
+ if (!hasItem) {
+ ALOGE("%s: Failed to query bitrate modes for HEVC codec", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ ALOGV("%s: HEVC codec's feature-bitrate-modes value is %d, %s",
+ __FUNCTION__, hasItem, bitrateModes.c_str());
+ std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase);
+ if (!std::regex_search(bitrateModes.c_str(), pattern)) {
+ return OK;
+ }
+ }
+
+ // HEIC size range
+ if (heicDetails != nullptr) {
+ auto res = getCodecSizeRange(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
+ heicDetails, &mMinSizeHeic, &mMaxSizeHeic, &mHeicFrameRateMaps);
+ if (res != OK) {
+ ALOGE("%s: Failed to get HEIC codec size range: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return BAD_VALUE;
+ }
+ mHasHEIC = true;
+ }
+
+ // HEVC size range
+ {
+ auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC,
+ hevcDetails, &mMinSizeHevc, &mMaxSizeHevc, &mHevcFrameRateMaps);
+ if (res != OK) {
+ ALOGE("%s: Failed to get HEVC codec size range: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return BAD_VALUE;
+ }
+
+ mHasHEVC = true;
+ }
+
+ return OK;
+}
+
+status_t HeicEncoderInfoManager::getFrameRateMaps(sp<AMessage> details, FrameRateMaps* maps) {
+ if (details == nullptr || maps == nullptr) {
+ ALOGE("%s: Invalid input: details: %p, maps: %p", __FUNCTION__, details.get(), maps);
+ return BAD_VALUE;
+ }
+
+ for (size_t i = 0; i < details->countEntries(); i++) {
+ AMessage::Type type;
+ const char* entryName = details->getEntryNameAt(i, &type);
+ if (type != AMessage::kTypeString) continue;
+ std::regex frameRateNamePattern("measured-frame-rate-([0-9]+)[*x]([0-9]+)-range",
+ std::regex_constants::icase);
+ std::cmatch sizeMatch;
+ if (std::regex_match(entryName, sizeMatch, frameRateNamePattern) &&
+ sizeMatch.size() == 3) {
+ AMessage::ItemData item = details->getEntryAt(i);
+ AString fpsRangeStr;
+ if (item.find(&fpsRangeStr)) {
+ ALOGV("%s: %s", entryName, fpsRangeStr.c_str());
+ std::regex frameRatePattern("([0-9]+)-([0-9]+)");
+ std::cmatch fpsMatch;
+ if (std::regex_match(fpsRangeStr.c_str(), fpsMatch, frameRatePattern) &&
+ fpsMatch.size() == 3) {
+ maps->emplace(
+ std::make_pair(stoi(sizeMatch[1]), stoi(sizeMatch[2])),
+ std::make_pair(stoi(fpsMatch[1]), stoi(fpsMatch[2])));
+ } else {
+ return BAD_VALUE;
+ }
+ }
+ }
+ }
+ return OK;
+}
+
+status_t HeicEncoderInfoManager::getCodecSizeRange(
+ const char* codecName,
+ sp<AMessage> details,
+ std::pair<int32_t, int32_t>* minSize,
+ std::pair<int32_t, int32_t>* maxSize,
+ FrameRateMaps* frameRateMaps) {
+ if (codecName == nullptr || minSize == nullptr || maxSize == nullptr ||
+ details == nullptr || frameRateMaps == nullptr) {
+ return BAD_VALUE;
+ }
+
+ AString sizeRange;
+ auto hasItem = details->findString("size-range", &sizeRange);
+ if (!hasItem) {
+ ALOGE("%s: Failed to query size range for codec %s", __FUNCTION__, codecName);
+ return BAD_VALUE;
+ }
+ ALOGV("%s: %s codec's size range is %s", __FUNCTION__, codecName, sizeRange.c_str());
+ std::regex pattern("([0-9]+)[*x]([0-9]+)-([0-9]+)[*x]([0-9]+)");
+ std::cmatch match;
+ if (std::regex_match(sizeRange.c_str(), match, pattern)) {
+ if (match.size() == 5) {
+ minSize->first = stoi(match[1]);
+ minSize->second = stoi(match[2]);
+ maxSize->first = stoi(match[3]);
+ maxSize->second = stoi(match[4]);
+ if (minSize->first > maxSize->first ||
+ minSize->second > maxSize->second) {
+ ALOGE("%s: Invalid %s code size range: %s",
+ __FUNCTION__, codecName, sizeRange.c_str());
+ return BAD_VALUE;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ }
+
+ auto res = getFrameRateMaps(details, frameRateMaps);
+ if (res != OK) {
+ return res;
+ }
+
+ return OK;
+}
+
+HeicEncoderInfoManager::FrameRateMaps::const_iterator HeicEncoderInfoManager::findClosestSize(
+ const FrameRateMaps& maps, int32_t width, int32_t height) const {
+ int32_t minDiff = INT32_MAX;
+ FrameRateMaps::const_iterator closestIter = maps.begin();
+ for (auto iter = maps.begin(); iter != maps.end(); iter++) {
+ // Use area difference between the sizes to approximate size
+ // difference.
+ int32_t diff = abs(iter->first.first * iter->first.second - width * height);
+ if (diff < minDiff) {
+ closestIter = iter;
+ minDiff = diff;
+ }
+ }
+ return closestIter;
+}
+
+sp<AMessage> HeicEncoderInfoManager::getCodecDetails(
+ sp<IMediaCodecList> codecsList, const char* name) {
+ ssize_t idx = codecsList->findCodecByType(name, true /*encoder*/);
+ if (idx < 0) {
+ return nullptr;
+ }
+
+ const sp<MediaCodecInfo> info = codecsList->getCodecInfo(idx);
+ if (info == nullptr) {
+ ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, name);
+ return nullptr;
+ }
+ const sp<MediaCodecInfo::Capabilities> caps =
+ info->getCapabilitiesFor(name);
+ if (caps == nullptr) {
+ ALOGE("%s: Failed to get capabilities for codec %s", __FUNCTION__, name);
+ return nullptr;
+ }
+ const sp<AMessage> details = caps->getDetails();
+ if (details == nullptr) {
+ ALOGE("%s: Failed to get details for codec %s", __FUNCTION__, name);
+ return nullptr;
+ }
+
+ return details;
+}
+} //namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h
new file mode 100644
index 0000000..fb0b914
--- /dev/null
+++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H
+#define ANDROID_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H
+
+#include <unordered_map>
+#include <utility>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+
+#include <media/IMediaCodecList.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+namespace camera3 {
+
+class HeicEncoderInfoManager {
+public:
+ static HeicEncoderInfoManager& getInstance() {
+ static HeicEncoderInfoManager instance;
+ return instance;
+ }
+
+ bool isSizeSupported(int32_t width, int32_t height,
+ bool* useHeic, bool* useGrid, int64_t* stall) const;
+
+ static const auto kGridWidth = 512;
+ static const auto kGridHeight = 512;
+private:
+ struct SizePairHash {
+ std::size_t operator () (const std::pair<int32_t,int32_t> &p) const {
+ return p.first * 31 + p.second;
+ }
+ };
+
+ typedef std::unordered_map<std::pair<int32_t, int32_t>,
+ std::pair<int32_t, int32_t>, SizePairHash> FrameRateMaps;
+
+ HeicEncoderInfoManager();
+ virtual ~HeicEncoderInfoManager();
+
+ status_t initialize();
+ status_t getFrameRateMaps(sp<AMessage> details, FrameRateMaps* maps);
+ status_t getCodecSizeRange(const char* codecName, sp<AMessage> details,
+ std::pair<int32_t, int32_t>* minSize, std::pair<int32_t, int32_t>* maxSize,
+ FrameRateMaps* frameRateMaps);
+ FrameRateMaps::const_iterator findClosestSize(const FrameRateMaps& maps,
+ int32_t width, int32_t height) const;
+ sp<AMessage> getCodecDetails(sp<IMediaCodecList> codecsList, const char* name);
+
+ bool mIsInited;
+ std::pair<int32_t, int32_t> mMinSizeHeic, mMaxSizeHeic;
+ std::pair<int32_t, int32_t> mMinSizeHevc, mMaxSizeHevc;
+ bool mHasHEVC, mHasHEIC;
+ FrameRateMaps mHeicFrameRateMaps, mHevcFrameRateMaps;
+ bool mDisableGrid;
+
+};
+
+} // namespace camera3
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_HEICENCODER_INFO_MANAGER_H
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 8ee3298..d6789a4 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -38,6 +38,8 @@
#include <hwbinder/IPCThreadState.h>
#include <utils/Trace.h>
+#include "api2/HeicCompositeStream.h"
+
namespace android {
using namespace ::android::hardware::camera;
@@ -69,6 +71,8 @@
}
mListener = listener;
mServiceProxy = proxy;
+ mDeviceState = static_cast<hardware::hidl_bitfield<provider::V2_5::DeviceState>>(
+ provider::V2_5::DeviceState::NORMAL);
// Registering will trigger notifications for all already-known providers
bool success = mServiceProxy->registerForNotifications(
@@ -272,6 +276,26 @@
return OK;
}
+status_t CameraProviderManager::notifyDeviceStateChange(
+ hardware::hidl_bitfield<provider::V2_5::DeviceState> newState) {
+ std::lock_guard<std::mutex> lock(mInterfaceMutex);
+ mDeviceState = newState;
+ status_t res = OK;
+ for (auto& provider : mProviders) {
+ ALOGV("%s: Notifying %s for new state 0x%" PRIx64,
+ __FUNCTION__, provider->mProviderName.c_str(), newState);
+ status_t singleRes = provider->notifyDeviceStateChange(mDeviceState);
+ if (singleRes != OK) {
+ ALOGE("%s: Unable to notify provider %s about device state change",
+ __FUNCTION__,
+ provider->mProviderName.c_str());
+ res = singleRes;
+ // continue to do the rest of the providers instead of returning now
+ }
+ }
+ return res;
+}
+
status_t CameraProviderManager::openSession(const std::string &id,
const sp<device::V3_2::ICameraDeviceCallback>& callback,
/*out*/
@@ -357,7 +381,7 @@
if (!kEnableLazyHal) {
return;
}
- ALOGI("Saving camera provider %s for camera device %s", provider->descriptor, cameraId.c_str());
+ ALOGV("Saving camera provider %s for camera device %s", provider->descriptor, cameraId.c_str());
std::lock_guard<std::mutex> lock(mProviderInterfaceMapLock);
std::unordered_map<std::string, sp<provider::V2_4::ICameraProvider>> *primaryMap, *alternateMap;
if (usageType == DeviceMode::TORCH) {
@@ -381,7 +405,7 @@
if (!kEnableLazyHal) {
return;
}
- ALOGI("Removing camera device %s", cameraId.c_str());
+ ALOGV("Removing camera device %s", cameraId.c_str());
std::unordered_map<std::string, sp<provider::V2_4::ICameraProvider>> *providerMap;
if (usageType == DeviceMode::TORCH) {
providerMap = &mTorchProviderByCameraId;
@@ -874,6 +898,130 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::fillHeicStreamCombinations(
+ std::vector<int32_t>* outputs,
+ std::vector<int64_t>* durations,
+ std::vector<int64_t>* stallDurations,
+ const camera_metadata_entry& halStreamConfigs,
+ const camera_metadata_entry& halStreamDurations) {
+ if (outputs == nullptr || durations == nullptr || stallDurations == nullptr) {
+ return BAD_VALUE;
+ }
+
+ static bool supportInMemoryTempFile =
+ camera3::HeicCompositeStream::isInMemoryTempFileSupported();
+ if (!supportInMemoryTempFile) {
+ ALOGI("%s: No HEIC support due to absence of in memory temp file support",
+ __FUNCTION__);
+ return OK;
+ }
+
+ for (size_t i = 0; i < halStreamConfigs.count; i += 4) {
+ int32_t format = halStreamConfigs.data.i32[i];
+ // Only IMPLEMENTATION_DEFINED and YUV_888 can be used to generate HEIC
+ // image.
+ if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ format != HAL_PIXEL_FORMAT_YCBCR_420_888) {
+ continue;
+ }
+
+ bool sizeAvail = false;
+ for (size_t j = 0; j < outputs->size(); j+= 4) {
+ if ((*outputs)[j+1] == halStreamConfigs.data.i32[i+1] &&
+ (*outputs)[j+2] == halStreamConfigs.data.i32[i+2]) {
+ sizeAvail = true;
+ break;
+ }
+ }
+ if (sizeAvail) continue;
+
+ int64_t stall = 0;
+ bool useHeic, useGrid;
+ if (camera3::HeicCompositeStream::isSizeSupportedByHeifEncoder(
+ halStreamConfigs.data.i32[i+1], halStreamConfigs.data.i32[i+2],
+ &useHeic, &useGrid, &stall)) {
+ if (useGrid != (format == HAL_PIXEL_FORMAT_YCBCR_420_888)) {
+ continue;
+ }
+
+ // HEIC configuration
+ int32_t config[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1],
+ halStreamConfigs.data.i32[i+2], 0 /*isInput*/};
+ outputs->insert(outputs->end(), config, config + 4);
+
+ // HEIC minFrameDuration
+ for (size_t j = 0; j < halStreamDurations.count; j += 4) {
+ if (halStreamDurations.data.i64[j] == format &&
+ halStreamDurations.data.i64[j+1] == halStreamConfigs.data.i32[i+1] &&
+ halStreamDurations.data.i64[j+2] == halStreamConfigs.data.i32[i+2]) {
+ int64_t duration[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1],
+ halStreamConfigs.data.i32[i+2], halStreamDurations.data.i64[j+3]};
+ durations->insert(durations->end(), duration, duration+4);
+ break;
+ }
+ }
+
+ // HEIC stallDuration
+ int64_t stallDuration[] = {HAL_PIXEL_FORMAT_BLOB, halStreamConfigs.data.i32[i+1],
+ halStreamConfigs.data.i32[i+2], stall};
+ stallDurations->insert(stallDurations->end(), stallDuration, stallDuration+4);
+ }
+ }
+ return OK;
+}
+
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::deriveHeicTags() {
+ auto& c = mCameraCharacteristics;
+
+ camera_metadata_entry halHeicSupport = c.find(ANDROID_HEIC_INFO_SUPPORTED);
+ if (halHeicSupport.count > 1) {
+ ALOGE("%s: Invalid entry count %zu for ANDROID_HEIC_INFO_SUPPORTED",
+ __FUNCTION__, halHeicSupport.count);
+ return BAD_VALUE;
+ } else if (halHeicSupport.count == 0 ||
+ halHeicSupport.data.u8[0] == ANDROID_HEIC_INFO_SUPPORTED_FALSE) {
+ // Camera HAL doesn't support mandatory stream combinations for HEIC.
+ return OK;
+ }
+
+ camera_metadata_entry maxJpegAppsSegments =
+ c.find(ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT);
+ if (maxJpegAppsSegments.count != 1 || maxJpegAppsSegments.data.u8[0] == 0 ||
+ maxJpegAppsSegments.data.u8[0] > 16) {
+ ALOGE("%s: ANDROID_HEIC_INFO_MAX_JPEG_APP_SEGMENTS_COUNT must be within [1, 16]",
+ __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ // Populate HEIC output configurations and its related min frame duration
+ // and stall duration.
+ std::vector<int32_t> heicOutputs;
+ std::vector<int64_t> heicDurations;
+ std::vector<int64_t> heicStallDurations;
+
+ camera_metadata_entry halStreamConfigs =
+ c.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ camera_metadata_entry minFrameDurations =
+ c.find(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+
+ status_t res = fillHeicStreamCombinations(&heicOutputs, &heicDurations, &heicStallDurations,
+ halStreamConfigs, minFrameDurations);
+ if (res != OK) {
+ ALOGE("%s: Failed to fill HEIC stream combinations: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ c.update(ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS,
+ heicOutputs.data(), heicOutputs.size());
+ c.update(ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS,
+ heicDurations.data(), heicDurations.size());
+ c.update(ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS,
+ heicStallDurations.data(), heicStallDurations.size());
+
+ return OK;
+}
+
bool CameraProviderManager::isLogicalCamera(const std::string& id,
std::vector<std::string>* physicalCameraIds) {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -962,7 +1110,7 @@
}
sp<ProviderInfo> providerInfo = new ProviderInfo(newProvider, this);
- status_t res = providerInfo->initialize(interface);
+ status_t res = providerInfo->initialize(interface, mDeviceState);
if (res != OK) {
return res;
}
@@ -1023,7 +1171,8 @@
}
status_t CameraProviderManager::ProviderInfo::initialize(
- sp<provider::V2_4::ICameraProvider>& interface) {
+ sp<provider::V2_4::ICameraProvider>& interface,
+ hardware::hidl_bitfield<provider::V2_5::DeviceState> currentDeviceState) {
status_t res = parseProviderName(mProviderName, &mType, &mId);
if (res != OK) {
ALOGE("%s: Invalid provider name, ignoring", __FUNCTION__);
@@ -1031,6 +1180,15 @@
}
ALOGI("Connecting to new camera provider: %s, isRemote? %d",
mProviderName.c_str(), interface->isRemote());
+
+ // Determine minor version
+ auto castResult = provider::V2_5::ICameraProvider::castFrom(interface);
+ if (castResult.isOk()) {
+ mMinorVersion = 5;
+ } else {
+ mMinorVersion = 4;
+ }
+
// cameraDeviceStatusChange callbacks may be called (and causing new devices added)
// before setCallback returns
hardware::Return<Status> status = interface->setCallback(this);
@@ -1055,6 +1213,24 @@
__FUNCTION__, mProviderName.c_str());
}
+ if (!kEnableLazyHal) {
+ // Save HAL reference indefinitely
+ mSavedInterface = interface;
+ } else {
+ mActiveInterface = interface;
+ }
+
+ ALOGV("%s: Setting device state for %s: 0x%" PRIx64,
+ __FUNCTION__, mProviderName.c_str(), mDeviceState);
+ notifyDeviceStateChange(currentDeviceState);
+
+ res = setUpVendorTags();
+ if (res != OK) {
+ ALOGE("%s: Unable to set up vendor tags from provider '%s'",
+ __FUNCTION__, mProviderName.c_str());
+ return res;
+ }
+
// Get initial list of camera devices, if any
std::vector<std::string> devices;
hardware::Return<void> ret = interface->getCameraIdList([&status, this, &devices](
@@ -1111,34 +1287,28 @@
}
}
- res = setUpVendorTags();
- if (res != OK) {
- ALOGE("%s: Unable to set up vendor tags from provider '%s'",
- __FUNCTION__, mProviderName.c_str());
- return res;
- }
-
ALOGI("Camera provider %s ready with %zu camera devices",
mProviderName.c_str(), mDevices.size());
mInitialized = true;
- if (!kEnableLazyHal) {
- // Save HAL reference indefinitely
- mSavedInterface = interface;
- }
return OK;
}
const sp<provider::V2_4::ICameraProvider>
CameraProviderManager::ProviderInfo::startProviderInterface() {
ATRACE_CALL();
- ALOGI("Request to start camera provider: %s", mProviderName.c_str());
+ ALOGV("Request to start camera provider: %s", mProviderName.c_str());
if (mSavedInterface != nullptr) {
return mSavedInterface;
}
+ if (!kEnableLazyHal) {
+ ALOGE("Bad provider state! Should not be here on a non-lazy HAL!");
+ return nullptr;
+ }
+
auto interface = mActiveInterface.promote();
if (interface == nullptr) {
- ALOGI("Could not promote, calling getService(%s)", mProviderName.c_str());
+ ALOGI("Camera HAL provider needs restart, calling getService(%s)", mProviderName.c_str());
interface = mManager->mServiceProxy->getService(mProviderName);
interface->setCallback(this);
hardware::Return<bool> linked = interface->linkToDeath(this, /*cookie*/ mId);
@@ -1151,9 +1321,22 @@
ALOGW("%s: Unable to link to provider '%s' death notifications",
__FUNCTION__, mProviderName.c_str());
}
+ // Send current device state
+ if (mMinorVersion >= 5) {
+ auto castResult = provider::V2_5::ICameraProvider::castFrom(interface);
+ if (castResult.isOk()) {
+ sp<provider::V2_5::ICameraProvider> interface_2_5 = castResult;
+ if (interface_2_5 != nullptr) {
+ ALOGV("%s: Initial device state for %s: 0x %" PRIx64,
+ __FUNCTION__, mProviderName.c_str(), mDeviceState);
+ interface_2_5->notifyDeviceStateChange(mDeviceState);
+ }
+ }
+ }
+
mActiveInterface = interface;
} else {
- ALOGI("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str());
+ ALOGV("Camera provider (%s) already in use. Re-using instance.", mProviderName.c_str());
}
return interface;
}
@@ -1238,8 +1421,10 @@
}
status_t CameraProviderManager::ProviderInfo::dump(int fd, const Vector<String16>&) const {
- dprintf(fd, "== Camera Provider HAL %s (v2.4, %s) static info: %zu devices: ==\n",
- mProviderName.c_str(), mIsRemote ? "remote" : "passthrough",
+ dprintf(fd, "== Camera Provider HAL %s (v2.%d, %s) static info: %zu devices: ==\n",
+ mProviderName.c_str(),
+ mMinorVersion,
+ mIsRemote ? "remote" : "passthrough",
mDevices.size());
for (auto& device : mDevices) {
@@ -1438,6 +1623,26 @@
return OK;
}
+status_t CameraProviderManager::ProviderInfo::notifyDeviceStateChange(
+ hardware::hidl_bitfield<provider::V2_5::DeviceState> newDeviceState) {
+ mDeviceState = newDeviceState;
+ if (mMinorVersion >= 5) {
+ // Check if the provider is currently active - not going to start it up for this notification
+ auto interface = mSavedInterface != nullptr ? mSavedInterface : mActiveInterface.promote();
+ if (interface != nullptr) {
+ // Send current device state
+ auto castResult = provider::V2_5::ICameraProvider::castFrom(interface);
+ if (castResult.isOk()) {
+ sp<provider::V2_5::ICameraProvider> interface_2_5 = castResult;
+ if (interface_2_5 != nullptr) {
+ interface_2_5->notifyDeviceStateChange(mDeviceState);
+ }
+ }
+ }
+ }
+ return OK;
+}
+
template<class DeviceInfoT>
std::unique_ptr<CameraProviderManager::ProviderInfo::DeviceInfo>
CameraProviderManager::ProviderInfo::initializeDeviceInfo(
@@ -1738,6 +1943,12 @@
ALOGE("%s: Failed appending dynamic depth tags: %s (%d)", __FUNCTION__, strerror(-stat),
stat);
}
+ res = deriveHeicTags();
+ if (OK != res) {
+ ALOGE("%s: Unable to derive HEIC tags based on camera and media capabilities: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ }
+
camera_metadata_entry flashAvailable =
mCameraCharacteristics.find(ANDROID_FLASH_INFO_AVAILABLE);
if (flashAvailable.count == 1 &&
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 18869f5..a42fb4d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -28,9 +28,8 @@
#include <camera/CameraBase.h>
#include <utils/Errors.h>
#include <android/hardware/camera/common/1.0/types.h>
-#include <android/hardware/camera/provider/2.4/ICameraProvider.h>
+#include <android/hardware/camera/provider/2.5/ICameraProvider.h>
#include <android/hardware/camera/device/3.4/ICameraDeviceSession.h>
-//#include <android/hardware/camera/provider/2.4/ICameraProviderCallbacks.h>
#include <android/hidl/manager/1.0/IServiceNotification.h>
#include <camera/VendorTagDescriptor.h>
@@ -206,6 +205,12 @@
status_t setUpVendorTags();
/**
+ * Inform registered providers about a device state change, such as folding or unfolding
+ */
+ status_t notifyDeviceStateChange(
+ android::hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState> newState);
+
+ /**
* Open an active session to a camera device.
*
* This fully powers on the camera device hardware, and returns a handle to a
@@ -277,6 +282,9 @@
wp<StatusListener> mListener;
ServiceInteractionProxy* mServiceProxy;
+ // Current overall Android device physical status
+ android::hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState> mDeviceState;
+
// mProviderLifecycleLock is locked during onRegistration and removeProvider
mutable std::mutex mProviderLifecycleLock;
@@ -303,10 +311,14 @@
{
const std::string mProviderName;
const metadata_vendor_id_t mProviderTagid;
+ int mMinorVersion;
sp<VendorTagDescriptor> mVendorTagDescriptor;
bool mSetTorchModeSupported;
bool mIsRemote;
+ // Current overall Android device physical status
+ hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState> mDeviceState;
+
// This pointer is used to keep a reference to the ICameraProvider that was last accessed.
wp<hardware::camera::provider::V2_4::ICameraProvider> mActiveInterface;
@@ -316,7 +328,9 @@
CameraProviderManager *manager);
~ProviderInfo();
- status_t initialize(sp<hardware::camera::provider::V2_4::ICameraProvider>& interface);
+ status_t initialize(sp<hardware::camera::provider::V2_4::ICameraProvider>& interface,
+ hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState>
+ currentDeviceState);
const sp<hardware::camera::provider::V2_4::ICameraProvider> startProviderInterface();
@@ -345,6 +359,13 @@
*/
status_t setUpVendorTags();
+ /**
+ * Notify provider about top-level device physical state changes
+ */
+ status_t notifyDeviceStateChange(
+ hardware::hidl_bitfield<hardware::camera::provider::V2_5::DeviceState>
+ newDeviceState);
+
// Basic device information, common to all camera devices
struct DeviceInfo {
const std::string mName; // Full instance name
@@ -494,6 +515,12 @@
std::vector<std::tuple<size_t, size_t>> *internalDepthSizes /*out*/);
status_t removeAvailableKeys(CameraMetadata& c, const std::vector<uint32_t>& keys,
uint32_t keyTag);
+ status_t fillHeicStreamCombinations(std::vector<int32_t>* outputs,
+ std::vector<int64_t>* durations,
+ std::vector<int64_t>* stallDurations,
+ const camera_metadata_entry& halStreamConfigs,
+ const camera_metadata_entry& halStreamDurations);
+ status_t deriveHeicTags();
};
private:
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 82dfc0f..918dcf7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1757,18 +1757,20 @@
if (format == HAL_PIXEL_FORMAT_BLOB) {
ssize_t blobBufferSize;
- if (dataSpace != HAL_DATASPACE_DEPTH) {
- blobBufferSize = getJpegBufferSize(width, height);
- if (blobBufferSize <= 0) {
- SET_ERR_L("Invalid jpeg buffer size %zd", blobBufferSize);
- return BAD_VALUE;
- }
- } else {
+ if (dataSpace == HAL_DATASPACE_DEPTH) {
blobBufferSize = getPointCloudBufferSize();
if (blobBufferSize <= 0) {
SET_ERR_L("Invalid point cloud buffer size %zd", blobBufferSize);
return BAD_VALUE;
}
+ } else if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_JPEG_APP_SEGMENTS)) {
+ blobBufferSize = width * height;
+ } else {
+ blobBufferSize = getJpegBufferSize(width, height);
+ if (blobBufferSize <= 0) {
+ SET_ERR_L("Invalid jpeg buffer size %zd", blobBufferSize);
+ return BAD_VALUE;
+ }
}
newStream = new Camera3OutputStream(mNextStreamId, consumers[0],
width, height, blobBufferSize, format, dataSpace, rotation,
@@ -5473,8 +5475,22 @@
return TIMED_OUT;
}
}
- outputStream->fireBufferRequestForFrameNumber(
- captureRequest->mResultExtras.frameNumber);
+
+ {
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != nullptr) {
+ const String8& streamCameraId = outputStream->getPhysicalCameraId();
+ for (const auto& settings : captureRequest->mSettingsList) {
+ if ((streamCameraId.isEmpty() &&
+ parent->getId() == settings.cameraId.c_str()) ||
+ streamCameraId == settings.cameraId.c_str()) {
+ outputStream->fireBufferRequestForFrameNumber(
+ captureRequest->mResultExtras.frameNumber,
+ settings.metadata);
+ }
+ }
+ }
+ }
String8 physicalCameraId = outputStream->getPhysicalCameraId();
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index b296513..d29e5c0 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -763,14 +763,15 @@
return getInputBufferProducerLocked(producer);
}
-void Camera3Stream::fireBufferRequestForFrameNumber(uint64_t frameNumber) {
+void Camera3Stream::fireBufferRequestForFrameNumber(uint64_t frameNumber,
+ const CameraMetadata& settings) {
ATRACE_CALL();
Mutex::Autolock l(mLock);
for (auto &it : mBufferListenerList) {
sp<Camera3StreamBufferListener> listener = it.promote();
if (listener.get() != nullptr) {
- listener->onBufferRequestForFrameNumber(frameNumber, getId());
+ listener->onBufferRequestForFrameNumber(frameNumber, getId(), settings);
}
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 06deba9..5eb6a23 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -434,7 +434,8 @@
/**
* Notify buffer stream listeners about incoming request with particular frame number.
*/
- void fireBufferRequestForFrameNumber(uint64_t frameNumber) override;
+ void fireBufferRequestForFrameNumber(uint64_t frameNumber,
+ const CameraMetadata& settings) override;
protected:
const int mId;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
index 0e6104e..d0aee27 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H
#define ANDROID_SERVERS_CAMERA3_STREAMBUFFERLISTENER_H
+#include <camera/CameraMetadata.h>
#include <gui/Surface.h>
#include <utils/RefBase.h>
@@ -42,7 +43,8 @@
// Buffer was released by the HAL
virtual void onBufferReleased(const BufferInfo& bufferInfo) = 0;
// Notify about incoming buffer request frame number
- virtual void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId) = 0;
+ virtual void onBufferRequestForFrameNumber(uint64_t frameNumber, int streamId,
+ const CameraMetadata& settings) = 0;
};
}; //namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 7b80cbd..5cd11b7 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -18,6 +18,8 @@
#define ANDROID_SERVERS_CAMERA3_STREAM_INTERFACE_H
#include <utils/RefBase.h>
+
+#include <camera/CameraMetadata.h>
#include "Camera3StreamBufferListener.h"
#include "Camera3StreamBufferFreedListener.h"
@@ -346,7 +348,8 @@
/**
* Notify buffer stream listeners about incoming request with particular frame number.
*/
- virtual void fireBufferRequestForFrameNumber(uint64_t frameNumber) = 0;
+ virtual void fireBufferRequestForFrameNumber(uint64_t frameNumber,
+ const CameraMetadata& settings) = 0;
};
} // namespace camera3
diff --git a/services/camera/libcameraservice/tests/Android.mk b/services/camera/libcameraservice/tests/Android.mk
index ad9963a..d777ca1 100644
--- a/services/camera/libcameraservice/tests/Android.mk
+++ b/services/camera/libcameraservice/tests/Android.mk
@@ -29,6 +29,7 @@
libutils \
android.hardware.camera.common@1.0 \
android.hardware.camera.provider@2.4 \
+ android.hardware.camera.provider@2.5 \
android.hardware.camera.device@1.0 \
android.hardware.camera.device@3.2 \
android.hardware.camera.device@3.4
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 0086c6c..f47e5a5 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -33,6 +33,7 @@
using android::hardware::camera::common::V1_0::CameraMetadataType;
using android::hardware::camera::device::V3_2::ICameraDeviceCallback;
using android::hardware::camera::device::V3_2::ICameraDeviceSession;
+using android::hardware::camera::provider::V2_5::DeviceState;
/**
* Basic test implementation of a camera ver. 3.2 device interface
@@ -87,7 +88,7 @@
/**
* Basic test implementation of a camera provider
*/
-struct TestICameraProvider : virtual public provider::V2_4::ICameraProvider {
+struct TestICameraProvider : virtual public provider::V2_5::ICameraProvider {
sp<provider::V2_4::ICameraProviderCallback> mCallbacks;
std::vector<hardware::hidl_string> mDeviceNames;
sp<device::V3_2::ICameraDevice> mDeviceInterface;
@@ -101,6 +102,7 @@
virtual hardware::Return<Status> setCallback(
const sp<provider::V2_4::ICameraProviderCallback>& callbacks) override {
+ mCalledCounter[SET_CALLBACK]++;
mCallbacks = callbacks;
return hardware::Return<Status>(Status::OK);
}
@@ -108,6 +110,7 @@
using getVendorTags_cb = std::function<void(Status status,
const hardware::hidl_vec<common::V1_0::VendorTagSection>& sections)>;
hardware::Return<void> getVendorTags(getVendorTags_cb _hidl_cb) override {
+ mCalledCounter[GET_VENDOR_TAGS]++;
_hidl_cb(Status::OK, mVendorTagSections);
return hardware::Void();
}
@@ -117,6 +120,7 @@
bool support)>;
virtual ::hardware::Return<void> isSetTorchModeSupported(
isSetTorchModeSupported_cb _hidl_cb) override {
+ mCalledCounter[IS_SET_TORCH_MODE_SUPPORTED]++;
_hidl_cb(Status::OK, false);
return hardware::Void();
}
@@ -124,6 +128,7 @@
using getCameraIdList_cb = std::function<void(Status status,
const hardware::hidl_vec<hardware::hidl_string>& cameraDeviceNames)>;
virtual hardware::Return<void> getCameraIdList(getCameraIdList_cb _hidl_cb) override {
+ mCalledCounter[GET_CAMERA_ID_LIST]++;
_hidl_cb(Status::OK, mDeviceNames);
return hardware::Void();
}
@@ -148,6 +153,25 @@
return hardware::Void();
}
+ virtual hardware::Return<void> notifyDeviceStateChange(
+ hardware::hidl_bitfield<DeviceState> newState) override {
+ mCalledCounter[NOTIFY_DEVICE_STATE]++;
+ mCurrentState = newState;
+ return hardware::Void();
+ }
+
+ enum MethodNames {
+ SET_CALLBACK,
+ GET_VENDOR_TAGS,
+ IS_SET_TORCH_MODE_SUPPORTED,
+ NOTIFY_DEVICE_STATE,
+ GET_CAMERA_ID_LIST,
+
+ METHOD_NAME_COUNT
+ };
+ int mCalledCounter[METHOD_NAME_COUNT] {0};
+
+ hardware::hidl_bitfield<DeviceState> mCurrentState = 0xFFFFFFFF; // Unlikely to be a real state
};
/**
@@ -209,11 +233,26 @@
res = providerManager->initialize(statusListener, &serviceProxy);
ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
+ // Check that both "legacy" and "external" providers (really the same object) are called
+ // once for all the init methods
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::SET_CALLBACK], 2) <<
+ "Only one call to setCallback per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_VENDOR_TAGS], 2) <<
+ "Only one call to getVendorTags per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::IS_SET_TORCH_MODE_SUPPORTED], 2) <<
+ "Only one call to isSetTorchModeSupported per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::GET_CAMERA_ID_LIST], 2) <<
+ "Only one call to getCameraIdList per provider expected during init";
+ EXPECT_EQ(provider->mCalledCounter[TestICameraProvider::NOTIFY_DEVICE_STATE], 2) <<
+ "Only one call to notifyDeviceState per provider expected during init";
std::string legacyInstanceName = "legacy/0";
std::string externalInstanceName = "external/0";
bool gotLegacy = false;
bool gotExternal = false;
+ EXPECT_EQ(2u, serviceProxy.mLastRequestedServiceNames.size()) <<
+ "Only two service queries expected to be seen by hardware service manager";
+
for (auto& serviceName : serviceProxy.mLastRequestedServiceNames) {
if (serviceName == legacyInstanceName) gotLegacy = true;
if (serviceName == externalInstanceName) gotExternal = true;
@@ -375,3 +414,35 @@
metadataCopy.dump(1, 2);
secondMetadata.dump(1, 2);
}
+
+TEST(CameraProviderManagerTest, NotifyStateChangeTest) {
+ std::vector<hardware::hidl_string> deviceNames {
+ "device@3.2/test/0",
+ "device@1.0/test/0",
+ "device@3.2/test/1"};
+
+ hardware::hidl_vec<common::V1_0::VendorTagSection> vendorSection;
+ status_t res;
+ sp<CameraProviderManager> providerManager = new CameraProviderManager();
+ sp<TestStatusListener> statusListener = new TestStatusListener();
+ TestInteractionProxy serviceProxy;
+ sp<TestICameraProvider> provider = new TestICameraProvider(deviceNames,
+ vendorSection);
+ serviceProxy.setProvider(provider);
+
+ res = providerManager->initialize(statusListener, &serviceProxy);
+ ASSERT_EQ(res, OK) << "Unable to initialize provider manager";
+
+ ASSERT_EQ(provider->mCurrentState,
+ static_cast<hardware::hidl_bitfield<DeviceState>>(DeviceState::NORMAL))
+ << "Initial device state not set";
+
+ res = providerManager->notifyDeviceStateChange(
+ static_cast<hardware::hidl_bitfield<DeviceState>>(DeviceState::FOLDED));
+
+ ASSERT_EQ(res, OK) << "Unable to call notifyDeviceStateChange";
+ ASSERT_EQ(provider->mCurrentState,
+ static_cast<hardware::hidl_bitfield<DeviceState>>(DeviceState::FOLDED))
+ << "Unable to change device state";
+
+}
diff --git a/services/camera/libcameraservice/utils/ExifUtils.cpp b/services/camera/libcameraservice/utils/ExifUtils.cpp
new file mode 100644
index 0000000..a4027cc
--- /dev/null
+++ b/services/camera/libcameraservice/utils/ExifUtils.cpp
@@ -0,0 +1,1046 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CameraServerExifUtils"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+
+#include <inttypes.h>
+#include <math.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+#include "ExifUtils.h"
+
+extern "C" {
+#include <libexif/exif-data.h>
+}
+
+namespace std {
+
+template <>
+struct default_delete<ExifEntry> {
+ inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
+};
+
+} // namespace std
+
+
+namespace android {
+namespace camera3 {
+
+
+class ExifUtilsImpl : public ExifUtils {
+public:
+ ExifUtilsImpl();
+
+ virtual ~ExifUtilsImpl();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize);
+
+ // set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth,
+ const size_t imageHeight);
+
+ // sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator);
+
+ // sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space);
+
+ // sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration);
+
+ // sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression);
+
+ // sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast);
+
+ // sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t);
+
+ // sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description);
+
+ // sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator);
+
+ // sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator);
+
+ // sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode);
+
+ // sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program);
+
+ // sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator);
+
+ // sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash);
+
+ // sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator);
+
+ // sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator);
+
+ // sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control);
+
+ // sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude);
+
+ // sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude);
+
+ // sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude);
+
+ // sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method);
+
+ // sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t);
+
+ // sets the length (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length);
+
+ // sets the width (number of columes) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width);
+
+ // sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings);
+
+ // sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source);
+
+ // sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator);
+
+ // sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode);
+
+ // sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation);
+
+ // sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit);
+
+ // sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation);
+
+ // sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type);
+
+ // sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness);
+
+ // sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator);
+
+ // sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator);
+
+ // sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time);
+
+ // sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance);
+
+ // sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning);
+
+ // sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator);
+
+ // sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make);
+
+ // sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model);
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1();
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer();
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length();
+
+ protected:
+ // sets the version of this standard supported.
+ // Returns false if memory allocation fails.
+ virtual bool setExifVersion(const std::string& exif_version);
+
+ // Resets the pointers and memories.
+ virtual void reset();
+
+ // Adds a variable length tag to |exif_data_|. It will remove the original one
+ // if the tag exists.
+ // Returns the entry of the tag. The reference count of returned ExifEntry is
+ // two.
+ virtual std::unique_ptr<ExifEntry> addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag, ExifFormat format, uint64_t components, unsigned int size);
+
+ // Adds a entry of |tag| in |exif_data_|. It won't remove the original one if
+ // the tag exists.
+ // Returns the entry of the tag. It adds one reference count to returned
+ // ExifEntry.
+ virtual std::unique_ptr<ExifEntry> addEntry(ExifIfd ifd, ExifTag tag);
+
+ // Helpe functions to add exif data with different types.
+ virtual bool setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg);
+
+ virtual bool setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg);
+
+ virtual bool setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator,
+ uint32_t denominator, const std::string& msg);
+
+ virtual bool setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator,
+ int32_t denominator, const std::string& msg);
+
+ virtual bool setString(ExifIfd ifd, ExifTag tag, ExifFormat format,
+ const std::string& buffer, const std::string& msg);
+
+ // Destroys the buffer of APP1 segment if exists.
+ virtual void destroyApp1();
+
+ // The Exif data (APP1). Owned by this class.
+ ExifData* exif_data_;
+ // The raw data of APP1 segment. It's allocated by ExifMem in |exif_data_| but
+ // owned by this class.
+ uint8_t* app1_buffer_;
+ // The length of |app1_buffer_|.
+ unsigned int app1_length_;
+
+};
+
+#define SET_SHORT(ifd, tag, value) \
+ do { \
+ if (setShort(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_LONG(ifd, tag, value) \
+ do { \
+ if (setLong(ifd, tag, value, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_RATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_SRATIONAL(ifd, tag, numerator, denominator) \
+ do { \
+ if (setSRational(ifd, tag, numerator, denominator, #tag) == false) \
+ return false; \
+ } while (0);
+
+#define SET_STRING(ifd, tag, format, buffer) \
+ do { \
+ if (setString(ifd, tag, format, buffer, #tag) == false) \
+ return false; \
+ } while (0);
+
+// This comes from the Exif Version 2.2 standard table 6.
+const char gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0};
+
+static void setLatitudeOrLongitudeData(unsigned char* data, double num) {
+ // Take the integer part of |num|.
+ ExifLong degrees = static_cast<ExifLong>(num);
+ ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
+ ExifLong microseconds =
+ static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
+ exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
+ exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL, {minutes, 1});
+ exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {microseconds, 1000000});
+}
+
+ExifUtils *ExifUtils::create() {
+ return new ExifUtilsImpl();
+}
+
+ExifUtils::~ExifUtils() {
+}
+
+ExifUtilsImpl::ExifUtilsImpl()
+ : exif_data_(nullptr), app1_buffer_(nullptr), app1_length_(0) {}
+
+ExifUtilsImpl::~ExifUtilsImpl() {
+ reset();
+}
+
+
+bool ExifUtilsImpl::initialize(const unsigned char *app1Segment, size_t app1SegmentSize) {
+ reset();
+ exif_data_ = exif_data_new_from_data(app1Segment, app1SegmentSize);
+ if (exif_data_ == nullptr) {
+ ALOGE("%s: allocate memory for exif_data_ failed", __FUNCTION__);
+ return false;
+ }
+ // set the image options.
+ exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+ exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);
+
+ // set exif version to 2.2.
+ if (!setExifVersion("0220")) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ExifUtilsImpl::setAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_APERTURE_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setBrightness(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_BRIGHTNESS_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setColorSpace(uint16_t color_space) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE, color_space);
+ return true;
+}
+
+bool ExifUtilsImpl::setComponentsConfiguration(
+ const std::string& components_configuration) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_COMPONENTS_CONFIGURATION,
+ EXIF_FORMAT_UNDEFINED, components_configuration);
+ return true;
+}
+
+bool ExifUtilsImpl::setCompression(uint16_t compression) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
+ return true;
+}
+
+bool ExifUtilsImpl::setContrast(uint16_t contrast) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_CONTRAST, contrast);
+ return true;
+}
+
+bool ExifUtilsImpl::setDateTime(const struct tm& t) {
+ // The length is 20 bytes including NULL for termination in Exif standard.
+ char str[20];
+ int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+ if (result != sizeof(str) - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+ std::string buffer(str);
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII, buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_ORIGINAL, EXIF_FORMAT_ASCII, buffer);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_DATE_TIME_DIGITIZED, EXIF_FORMAT_ASCII, buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setDescription(const std::string& description) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_IMAGE_DESCRIPTION, EXIF_FORMAT_ASCII, description);
+ return true;
+}
+
+bool ExifUtilsImpl::setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_DIGITAL_ZOOM_RATIO, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureBias(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_BIAS_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureMode(uint16_t exposure_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_MODE, exposure_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureProgram(uint16_t exposure_program) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_PROGRAM, exposure_program);
+ return true;
+}
+
+bool ExifUtilsImpl::setExposureTime(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_EXPOSURE_TIME, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFlash(uint16_t flash) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_FLASH, flash);
+ return true;
+}
+
+bool ExifUtilsImpl::setFNumber(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FNUMBER, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setFocalLength(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setGainControl(uint16_t gain_control) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_GAIN_CONTROL, gain_control);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsAltitude(double altitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSAltitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (altitude >= 0) {
+ *refEntry->data = 0;
+ } else {
+ *refEntry->data = 1;
+ altitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSAltitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(altitude * 1000), 1000});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLatitude(double latitude) {
+ const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLatitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (latitude >= 0) {
+ memcpy(refEntry->data, "N", sizeof("N"));
+ } else {
+ memcpy(refEntry->data, "S", sizeof("S"));
+ latitude *= -1;
+ }
+
+ const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLatitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, latitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsLongitude(double longitude) {
+ ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
+ std::unique_ptr<ExifEntry> refEntry =
+ addVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
+ if (!refEntry) {
+ ALOGE("%s: Adding GPSLongitudeRef exif entry failed", __FUNCTION__);
+ return false;
+ }
+ if (longitude >= 0) {
+ memcpy(refEntry->data, "E", sizeof("E"));
+ } else {
+ memcpy(refEntry->data, "W", sizeof("W"));
+ longitude *= -1;
+ }
+
+ ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(
+ EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
+ if (!entry) {
+ exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
+ ALOGE("%s: Adding GPSLongitude exif entry failed", __FUNCTION__);
+ return false;
+ }
+ setLatitudeOrLongitudeData(entry->data, longitude);
+
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsProcessingMethod(const std::string& method) {
+ std::string buffer =
+ std::string(gExifAsciiPrefix, sizeof(gExifAsciiPrefix)) + method;
+ SET_STRING(EXIF_IFD_GPS, static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD),
+ EXIF_FORMAT_UNDEFINED, buffer);
+ return true;
+}
+
+bool ExifUtilsImpl::setGpsTimestamp(const struct tm& t) {
+ const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
+ const size_t kGpsDateStampSize = 11;
+ std::unique_ptr<ExifEntry> entry = addVariableLengthEntry(EXIF_IFD_GPS,
+ dateTag, EXIF_FORMAT_ASCII, kGpsDateStampSize, kGpsDateStampSize);
+ if (!entry) {
+ ALOGE("%s: Adding GPSDateStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ int result = snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
+ "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
+ if (result != kGpsDateStampSize - 1) {
+ ALOGW("%s: Input time is invalid", __FUNCTION__);
+ return false;
+ }
+
+ const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
+ entry = addVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
+ 3 * sizeof(ExifRational));
+ if (!entry) {
+ ALOGE("%s: Adding GPSTimeStamp exif entry failed", __FUNCTION__);
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_hour), 1});
+ exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_min), 1});
+ exif_set_rational(entry->data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
+ {static_cast<ExifLong>(t.tm_sec), 1});
+
+ return true;
+}
+
+bool ExifUtilsImpl::setImageHeight(uint32_t length) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH, length);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, length);
+ return true;
+}
+
+bool ExifUtilsImpl::setImageWidth(uint32_t width) {
+ SET_LONG(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH, width);
+ SET_LONG(EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width);
+ return true;
+}
+
+bool ExifUtilsImpl::setIsoSpeedRating(uint16_t iso_speed_ratings) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_ISO_SPEED_RATINGS, iso_speed_ratings);
+ return true;
+}
+
+bool ExifUtilsImpl::setLightSource(uint16_t light_source) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_LIGHT_SOURCE, light_source);
+ return true;
+}
+
+bool ExifUtilsImpl::setMaxAperture(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_MAX_APERTURE_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setMeteringMode(uint16_t metering_mode) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_METERING_MODE, metering_mode);
+ return true;
+}
+
+bool ExifUtilsImpl::setOrientation(uint16_t orientation) {
+ /*
+ * Orientation value:
+ * 1 2 3 4 5 6 7 8
+ *
+ * 888888 888888 88 88 8888888888 88 88 8888888888
+ * 88 88 88 88 88 88 88 88 88 88 88 88
+ * 8888 8888 8888 8888 88 8888888888 8888888888 88
+ * 88 88 88 88
+ * 88 88 888888 888888
+ */
+ int value = 1;
+ switch (orientation) {
+ case 90:
+ value = 6;
+ break;
+ case 180:
+ value = 3;
+ break;
+ case 270:
+ value = 8;
+ break;
+ default:
+ break;
+ }
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_ORIENTATION, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setResolutionUnit(uint16_t resolution_unit) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_RESOLUTION_UNIT, resolution_unit);
+ return true;
+}
+
+bool ExifUtilsImpl::setSaturation(uint16_t saturation) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SATURATION, saturation);
+ return true;
+}
+
+bool ExifUtilsImpl::setSceneCaptureType(uint16_t type) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SCENE_CAPTURE_TYPE, type);
+ return true;
+}
+
+bool ExifUtilsImpl::setSharpness(uint16_t sharpness) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_SHARPNESS, sharpness);
+ return true;
+}
+
+bool ExifUtilsImpl::setShutterSpeed(int32_t numerator, int32_t denominator) {
+ SET_SRATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SHUTTER_SPEED_VALUE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubjectDistance(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_DISTANCE, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setSubsecTime(const std::string& subsec_time) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME, EXIF_FORMAT_ASCII, subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_ORIGINAL, EXIF_FORMAT_ASCII, subsec_time);
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_SUB_SEC_TIME_DIGITIZED, EXIF_FORMAT_ASCII, subsec_time);
+ return true;
+}
+
+bool ExifUtilsImpl::setWhiteBalance(uint16_t white_balance) {
+ SET_SHORT(EXIF_IFD_EXIF, EXIF_TAG_WHITE_BALANCE, white_balance);
+ return true;
+}
+
+bool ExifUtilsImpl::setXResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_X_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::setYCbCrPositioning(uint16_t ycbcr_positioning) {
+ SET_SHORT(EXIF_IFD_0, EXIF_TAG_YCBCR_POSITIONING, ycbcr_positioning);
+ return true;
+}
+
+bool ExifUtilsImpl::setYResolution(uint32_t numerator, uint32_t denominator) {
+ SET_RATIONAL(EXIF_IFD_EXIF, EXIF_TAG_Y_RESOLUTION, numerator, denominator);
+ return true;
+}
+
+bool ExifUtilsImpl::generateApp1() {
+ destroyApp1();
+ // Save the result into |app1_buffer_|.
+ exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
+ if (!app1_length_) {
+ ALOGE("%s: Allocate memory for app1_buffer_ failed", __FUNCTION__);
+ return false;
+ }
+ /*
+ * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
+ * be smaller than 65533 because there are two bytes for segment size field.
+ */
+ if (app1_length_ > 65533) {
+ destroyApp1();
+ ALOGE("%s: The size of APP1 segment is too large", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+const uint8_t* ExifUtilsImpl::getApp1Buffer() {
+ return app1_buffer_;
+}
+
+unsigned int ExifUtilsImpl::getApp1Length() {
+ return app1_length_;
+}
+
+bool ExifUtilsImpl::setExifVersion(const std::string& exif_version) {
+ SET_STRING(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, exif_version);
+ return true;
+}
+
+bool ExifUtilsImpl::setMake(const std::string& make) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, make);
+ return true;
+}
+
+bool ExifUtilsImpl::setModel(const std::string& model) {
+ SET_STRING(EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, model);
+ return true;
+}
+
+void ExifUtilsImpl::reset() {
+ destroyApp1();
+ if (exif_data_) {
+ /*
+ * Since we decided to ignore the original APP1, we are sure that there is
+ * no thumbnail allocated by libexif. |exif_data_->data| is actually
+ * allocated by JpegCompressor. sets |exif_data_->data| to nullptr to
+ * prevent exif_data_unref() destroy it incorrectly.
+ */
+ exif_data_->data = nullptr;
+ exif_data_->size = 0;
+ exif_data_unref(exif_data_);
+ exif_data_ = nullptr;
+ }
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addVariableLengthEntry(ExifIfd ifd,
+ ExifTag tag, ExifFormat format, uint64_t components, unsigned int size) {
+ // Remove old entry if exists.
+ exif_content_remove_entry(exif_data_->ifd[ifd],
+ exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ ExifMem* mem = exif_mem_new_default();
+ if (!mem) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+ void* tmpBuffer = exif_mem_alloc(mem, size);
+ if (!tmpBuffer) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ exif_mem_unref(mem);
+ return nullptr;
+ }
+
+ entry->data = static_cast<unsigned char*>(tmpBuffer);
+ entry->tag = tag;
+ entry->format = format;
+ entry->components = components;
+ entry->size = size;
+
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_mem_unref(mem);
+
+ return entry;
+}
+
+std::unique_ptr<ExifEntry> ExifUtilsImpl::addEntry(ExifIfd ifd, ExifTag tag) {
+ std::unique_ptr<ExifEntry> entry(exif_content_get_entry(exif_data_->ifd[ifd], tag));
+ if (entry) {
+ // exif_content_get_entry() won't ref the entry, so we ref here.
+ exif_entry_ref(entry.get());
+ return entry;
+ }
+ entry.reset(exif_entry_new());
+ if (!entry) {
+ ALOGE("%s: Allocate memory for exif entry failed", __FUNCTION__);
+ return nullptr;
+ }
+ entry->tag = tag;
+ exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
+ exif_entry_initialize(entry.get(), tag);
+ return entry;
+}
+
+bool ExifUtilsImpl::setShort(ExifIfd ifd, ExifTag tag, uint16_t value, const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setLong(ExifIfd ifd, ExifTag tag, uint32_t value, const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_long(entry->data, EXIF_BYTE_ORDER_INTEL, value);
+ return true;
+}
+
+bool ExifUtilsImpl::setRational(ExifIfd ifd, ExifTag tag, uint32_t numerator,
+ uint32_t denominator, const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setSRational(ExifIfd ifd, ExifTag tag, int32_t numerator,
+ int32_t denominator, const std::string& msg) {
+ std::unique_ptr<ExifEntry> entry = addEntry(ifd, tag);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ exif_set_srational(entry->data, EXIF_BYTE_ORDER_INTEL, {numerator, denominator});
+ return true;
+}
+
+bool ExifUtilsImpl::setString(ExifIfd ifd, ExifTag tag, ExifFormat format,
+ const std::string& buffer, const std::string& msg) {
+ size_t entry_size = buffer.length();
+ // Since the exif format is undefined, NULL termination is not necessary.
+ if (format == EXIF_FORMAT_ASCII) {
+ entry_size++;
+ }
+ std::unique_ptr<ExifEntry> entry =
+ addVariableLengthEntry(ifd, tag, format, entry_size, entry_size);
+ if (!entry) {
+ ALOGE("%s: Adding '%s' entry failed", __FUNCTION__, msg.c_str());
+ return false;
+ }
+ memcpy(entry->data, buffer.c_str(), entry_size);
+ return true;
+}
+
+void ExifUtilsImpl::destroyApp1() {
+ /*
+ * Since there is no API to access ExifMem in ExifData->priv, we use free
+ * here, which is the default free function in libexif. See
+ * exif_data_save_data() for detail.
+ */
+ free(app1_buffer_);
+ app1_buffer_ = nullptr;
+ app1_length_ = 0;
+}
+
+bool ExifUtilsImpl::setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth, const size_t imageHeight) {
+ // How precise the float-to-rational conversion for EXIF tags would be.
+ constexpr int kRationalPrecision = 10000;
+ if (!setImageWidth(imageWidth) ||
+ !setImageHeight(imageHeight)) {
+ ALOGE("%s: setting image resolution failed.", __FUNCTION__);
+ return false;
+ }
+
+ struct timespec tp;
+ struct tm time_info;
+ bool time_available = clock_gettime(CLOCK_REALTIME, &tp) != -1;
+ localtime_r(&tp.tv_sec, &time_info);
+ if (!setDateTime(time_info)) {
+ ALOGE("%s: setting data time failed.", __FUNCTION__);
+ return false;
+ }
+
+ float focal_length;
+ camera_metadata_ro_entry entry = metadata.find(ANDROID_LENS_FOCAL_LENGTH);
+ if (entry.count) {
+ focal_length = entry.data.f[0];
+
+ if (!setFocalLength(
+ static_cast<uint32_t>(focal_length * kRationalPrecision), kRationalPrecision)) {
+ ALOGE("%s: setting focal length failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGV("%s: Cannot find focal length in metadata.", __FUNCTION__);
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_COORDINATES)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_COORDINATES);
+ if (entry.count < 3) {
+ ALOGE("%s: Gps coordinates in metadata is not complete.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLatitude(entry.data.d[0])) {
+ ALOGE("%s: setting gps latitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsLongitude(entry.data.d[1])) {
+ ALOGE("%s: setting gps longitude failed.", __FUNCTION__);
+ return false;
+ }
+ if (!setGpsAltitude(entry.data.d[2])) {
+ ALOGE("%s: setting gps altitude failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_GPS_PROCESSING_METHOD)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_PROCESSING_METHOD);
+ std::string method_str(reinterpret_cast<const char*>(entry.data.u8));
+ if (!setGpsProcessingMethod(method_str)) {
+ ALOGE("%s: setting gps processing method failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (time_available && metadata.exists(ANDROID_JPEG_GPS_TIMESTAMP)) {
+ entry = metadata.find(ANDROID_JPEG_GPS_TIMESTAMP);
+ time_t timestamp = static_cast<time_t>(entry.data.i64[0]);
+ if (gmtime_r(×tamp, &time_info)) {
+ if (!setGpsTimestamp(time_info)) {
+ ALOGE("%s: setting gps timestamp failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Time tranformation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_JPEG_ORIENTATION)) {
+ entry = metadata.find(ANDROID_JPEG_ORIENTATION);
+ if (!setOrientation(entry.data.i32[0])) {
+ ALOGE("%s: setting orientation failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_SENSOR_EXPOSURE_TIME)) {
+ entry = metadata.find(ANDROID_SENSOR_EXPOSURE_TIME);
+ // int64_t of nanoseconds
+ if (!setExposureTime(entry.data.i64[0],1000000000u)) {
+ ALOGE("%s: setting exposure time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_LENS_APERTURE)) {
+ const int kAperturePrecision = 10000;
+ entry = metadata.find(ANDROID_LENS_APERTURE);
+ if (!setFNumber(entry.data.f[0] * kAperturePrecision, kAperturePrecision)) {
+ ALOGE("%s: setting F number failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_FLASH_INFO_AVAILABLE)) {
+ entry = metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
+ if (entry.data.u8[0] == ANDROID_FLASH_INFO_AVAILABLE_FALSE) {
+ const uint32_t kNoFlashFunction = 0x20;
+ if (!setFlash(kNoFlashFunction)) {
+ ALOGE("%s: setting flash failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported flash info: %d",__FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (metadata.exists(ANDROID_CONTROL_AWB_MODE)) {
+ entry = metadata.find(ANDROID_CONTROL_AWB_MODE);
+ if (entry.data.u8[0] == ANDROID_CONTROL_AWB_MODE_AUTO) {
+ const uint16_t kAutoWhiteBalance = 0;
+ if (!setWhiteBalance(kAutoWhiteBalance)) {
+ ALOGE("%s: setting white balance failed.", __FUNCTION__);
+ return false;
+ }
+ } else {
+ ALOGE("%s: Unsupported awb mode: %d", __FUNCTION__, entry.data.u8[0]);
+ return false;
+ }
+ }
+
+ if (time_available) {
+ char str[4];
+ if (snprintf(str, sizeof(str), "%03ld", tp.tv_nsec / 1000000) < 0) {
+ ALOGE("%s: Subsec is invalid: %ld", __FUNCTION__, tp.tv_nsec);
+ return false;
+ }
+ if (!setSubsecTime(std::string(str))) {
+ ALOGE("%s: setting subsec time failed.", __FUNCTION__);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace camera3
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/ExifUtils.h b/services/camera/libcameraservice/utils/ExifUtils.h
new file mode 100644
index 0000000..8ccdd8f
--- /dev/null
+++ b/services/camera/libcameraservice/utils/ExifUtils.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_EXIF_UTILS_H
+#define ANDROID_SERVERS_CAMERA_EXIF_UTILS_H
+
+#include "CameraMetadata.h"
+
+namespace android {
+namespace camera3 {
+
+// This is based on the camera HIDL shim implementation, which was in turned
+// based on original ChromeOS ARC implementation of a V4L2 HAL
+
+// ExifUtils can override APP1 segment with tags which caller set. ExifUtils can
+// also add a thumbnail in the APP1 segment if thumbnail size is specified.
+// ExifUtils can be reused with different images by calling initialize().
+//
+// Example of using this class :
+// std::unique_ptr<ExifUtils> utils(ExifUtils::Create());
+// utils->initialize(const unsigned char* app1Segment, size_t app1SegmentSize);
+// ...
+// // Call ExifUtils functions to set Exif tags.
+// ...
+// utils->GenerateApp1();
+// unsigned int app1Length = utils->GetApp1Length();
+// uint8_t* app1Buffer = new uint8_t[app1Length];
+// memcpy(app1Buffer, utils->GetApp1Buffer(), app1Length);
+class ExifUtils {
+
+public:
+ virtual ~ExifUtils();
+
+ static ExifUtils* create();
+
+ // Initialize() can be called multiple times. The setting of Exif tags will be
+ // cleared.
+ virtual bool initialize(const unsigned char *app1Segment, size_t app1SegmentSize) = 0;
+
+ // Set all known fields from a metadata structure
+ virtual bool setFromMetadata(const CameraMetadata& metadata,
+ const size_t imageWidth, const size_t imageHeight) = 0;
+
+ // Sets the len aperture.
+ // Returns false if memory allocation fails.
+ virtual bool setAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the value of brightness.
+ // Returns false if memory allocation fails.
+ virtual bool setBrightness(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the color space.
+ // Returns false if memory allocation fails.
+ virtual bool setColorSpace(uint16_t color_space) = 0;
+
+ // Sets the information to compressed data.
+ // Returns false if memory allocation fails.
+ virtual bool setComponentsConfiguration(const std::string& components_configuration) = 0;
+
+ // Sets the compression scheme used for the image data.
+ // Returns false if memory allocation fails.
+ virtual bool setCompression(uint16_t compression) = 0;
+
+ // Sets image contrast.
+ // Returns false if memory allocation fails.
+ virtual bool setContrast(uint16_t contrast) = 0;
+
+ // Sets the date and time of image last modified. It takes local time. The
+ // name of the tag is DateTime in IFD0.
+ // Returns false if memory allocation fails.
+ virtual bool setDateTime(const struct tm& t) = 0;
+
+ // Sets the image description.
+ // Returns false if memory allocation fails.
+ virtual bool setDescription(const std::string& description) = 0;
+
+ // Sets the digital zoom ratio. If the numerator is 0, it means digital zoom
+ // was not used.
+ // Returns false if memory allocation fails.
+ virtual bool setDigitalZoomRatio(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the exposure bias.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureBias(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the exposure mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureMode(uint16_t exposure_mode) = 0;
+
+ // Sets the program used by the camera to set exposure when the picture is
+ // taken.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureProgram(uint16_t exposure_program) = 0;
+
+ // Sets the exposure time, given in seconds.
+ // Returns false if memory allocation fails.
+ virtual bool setExposureTime(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the status of flash.
+ // Returns false if memory allocation fails.
+ virtual bool setFlash(uint16_t flash) = 0;
+
+ // Sets the F number.
+ // Returns false if memory allocation fails.
+ virtual bool setFNumber(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the focal length of lens used to take the image in millimeters.
+ // Returns false if memory allocation fails.
+ virtual bool setFocalLength(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the degree of overall image gain adjustment.
+ // Returns false if memory allocation fails.
+ virtual bool setGainControl(uint16_t gain_control) = 0;
+
+ // Sets the altitude in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsAltitude(double altitude) = 0;
+
+ // Sets the latitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLatitude(double latitude) = 0;
+
+ // Sets the longitude with degrees minutes seconds format.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsLongitude(double longitude) = 0;
+
+ // Sets GPS processing method.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsProcessingMethod(const std::string& method) = 0;
+
+ // Sets GPS date stamp and time stamp (atomic clock). It takes UTC time.
+ // Returns false if memory allocation fails.
+ virtual bool setGpsTimestamp(const struct tm& t) = 0;
+
+ // Sets the height (number of rows) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageHeight(uint32_t length) = 0;
+
+ // Sets the width (number of columns) of main image.
+ // Returns false if memory allocation fails.
+ virtual bool setImageWidth(uint32_t width) = 0;
+
+ // Sets the ISO speed.
+ // Returns false if memory allocation fails.
+ virtual bool setIsoSpeedRating(uint16_t iso_speed_ratings) = 0;
+
+ // Sets the kind of light source.
+ // Returns false if memory allocation fails.
+ virtual bool setLightSource(uint16_t light_source) = 0;
+
+ // Sets the smallest F number of the lens.
+ // Returns false if memory allocation fails.
+ virtual bool setMaxAperture(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the metering mode.
+ // Returns false if memory allocation fails.
+ virtual bool setMeteringMode(uint16_t metering_mode) = 0;
+
+ // Sets image orientation.
+ // Returns false if memory allocation fails.
+ virtual bool setOrientation(uint16_t orientation) = 0;
+
+ // Sets the unit for measuring XResolution and YResolution.
+ // Returns false if memory allocation fails.
+ virtual bool setResolutionUnit(uint16_t resolution_unit) = 0;
+
+ // Sets image saturation.
+ // Returns false if memory allocation fails.
+ virtual bool setSaturation(uint16_t saturation) = 0;
+
+ // Sets the type of scene that was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setSceneCaptureType(uint16_t type) = 0;
+
+ // Sets image sharpness.
+ // Returns false if memory allocation fails.
+ virtual bool setSharpness(uint16_t sharpness) = 0;
+
+ // Sets the shutter speed.
+ // Returns false if memory allocation fails.
+ virtual bool setShutterSpeed(int32_t numerator, int32_t denominator) = 0;
+
+ // Sets the distance to the subject, given in meters.
+ // Returns false if memory allocation fails.
+ virtual bool setSubjectDistance(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the fractions of seconds for the <DateTime> tag.
+ // Returns false if memory allocation fails.
+ virtual bool setSubsecTime(const std::string& subsec_time) = 0;
+
+ // Sets the white balance mode set when the image was shot.
+ // Returns false if memory allocation fails.
+ virtual bool setWhiteBalance(uint16_t white_balance) = 0;
+
+ // Sets the number of pixels per resolution unit in the image width.
+ // Returns false if memory allocation fails.
+ virtual bool setXResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the position of chrominance components in relation to the luminance
+ // component.
+ // Returns false if memory allocation fails.
+ virtual bool setYCbCrPositioning(uint16_t ycbcr_positioning) = 0;
+
+ // Sets the number of pixels per resolution unit in the image length.
+ // Returns false if memory allocation fails.
+ virtual bool setYResolution(uint32_t numerator, uint32_t denominator) = 0;
+
+ // Sets the manufacturer of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setMake(const std::string& make) = 0;
+
+ // Sets the model number of camera.
+ // Returns false if memory allocation fails.
+ virtual bool setModel(const std::string& model) = 0;
+
+ // Generates APP1 segment.
+ // Returns false if generating APP1 segment fails.
+ virtual bool generateApp1() = 0;
+
+ // Gets buffer of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual const uint8_t* getApp1Buffer() = 0;
+
+ // Gets length of APP1 segment. This method must be called only after calling
+ // GenerateAPP1().
+ virtual unsigned int getApp1Length() = 0;
+};
+
+} // namespace camera3
+} // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_EXIF_UTILS_H