Merge "audioserver: log system event for binder timeouts"
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 4c28789..a86cc87 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -566,7 +566,7 @@
for (size_t i = 0; i < static_cast<size_t>(tagCount); ++i) {
uint32_t tag = tagArray[i];
- String8 sectionString = tagToSectionMap.valueFor(tag);
+ const String8& sectionString = tagToSectionMap.valueFor(tag);
// Set up tag to section index map
ssize_t index = sections.indexOf(sectionString);
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 7398f78..bd85469 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -5092,12 +5092,26 @@
* the following code snippet can be used:</p>
* <pre><code>// Returns true if the device supports the required hardware level, or better.
* boolean isHardwareLevelSupported(CameraCharacteristics c, int requiredLevel) {
+ * final int[] sortedHwLevels = {
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
+ * CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
+ * };
* int deviceLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
- * if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
- * return requiredLevel == deviceLevel;
+ * if (requiredLevel == deviceLevel) {
+ * return true;
* }
- * // deviceLevel is not LEGACY, can use numerical sort
- * return requiredLevel <= deviceLevel;
+ *
+ * for (int sortedlevel : sortedHwLevels) {
+ * if (sortedlevel == requiredLevel) {
+ * return true;
+ * } else if (sortedlevel == deviceLevel) {
+ * return false;
+ * }
+ * }
+ * return false; // Should never reach here
* }
* </code></pre>
* <p>At a high level, the levels are:</p>
@@ -5111,6 +5125,8 @@
* post-processing settings, and image capture at a high rate.</li>
* <li><code>LEVEL_3</code> devices additionally support YUV reprocessing and RAW image capture, along
* with additional output stream configurations.</li>
+ * <li><code>EXTERNAL</code> devices are similar to <code>LIMITED</code> devices with exceptions like some sensor or
+ * lens information not reorted or less stable framerates.</li>
* </ul>
* <p>See the individual level enums for full descriptions of the supported capabilities. The
* ACAMERA_REQUEST_AVAILABLE_CAPABILITIES entry describes the device's capabilities at a
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 85aab57..d41d3fd 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -354,7 +354,10 @@
decodeTimesUs.push(delayDecodeUs);
}
- if (showProgress && (n++ % 16) == 0) {
+ if (gVerbose) {
+ MetaDataBase &meta = buffer->meta_data();
+ fprintf(stdout, "%ld sample format: %s\n", numFrames, meta.toString().c_str());
+ } else if (showProgress && (n++ % 16) == 0) {
printf(".");
fflush(stdout);
}
@@ -647,7 +650,7 @@
MEDIA_MIMETYPE_AUDIO_MPEG, MEDIA_MIMETYPE_AUDIO_G711_MLAW,
MEDIA_MIMETYPE_AUDIO_G711_ALAW, MEDIA_MIMETYPE_AUDIO_VORBIS,
MEDIA_MIMETYPE_VIDEO_VP8, MEDIA_MIMETYPE_VIDEO_VP9,
- MEDIA_MIMETYPE_VIDEO_DOLBY_VISION
+ MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, MEDIA_MIMETYPE_AUDIO_AC4
};
const char *codecType = queryDecoders? "decoder" : "encoder";
diff --git a/drm/common/Android.bp b/drm/common/Android.bp
index 1552c3f..272684c 100644
--- a/drm/common/Android.bp
+++ b/drm/common/Android.bp
@@ -35,7 +35,7 @@
cflags: ["-Wall", "-Werror"],
- static_libs: ["libbinder"],
+ shared_libs: ["libbinder"],
export_include_dirs: ["include"],
}
diff --git a/media/extractors/midi/MidiExtractor.cpp b/media/extractors/midi/MidiExtractor.cpp
index 949fbe0..a30b6f8 100644
--- a/media/extractors/midi/MidiExtractor.cpp
+++ b/media/extractors/midi/MidiExtractor.cpp
@@ -247,8 +247,9 @@
EAS_I32 numRendered;
EAS_RESULT result = EAS_Render(mEasData, p, mEasConfig->mixBufferSize, &numRendered);
if (result != EAS_SUCCESS) {
- ALOGE("EAS_Render returned %ld", result);
- break;
+ ALOGE("EAS_Render() returned %ld, numBytesOutput = %d", result, numBytesOutput);
+ buffer->release();
+ return NULL; // Stop processing to prevent infinite loops.
}
p += numRendered * mEasConfig->numChannels;
numBytesOutput += numRendered * mEasConfig->numChannels * sizeof(EAS_PCM);
diff --git a/media/extractors/mp3/XINGSeeker.cpp b/media/extractors/mp3/XINGSeeker.cpp
index 95ca556..01e06ca 100644
--- a/media/extractors/mp3/XINGSeeker.cpp
+++ b/media/extractors/mp3/XINGSeeker.cpp
@@ -44,7 +44,7 @@
}
bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
- if (mSizeBytes == 0 || !mTOCValid || mDurationUs < 0) {
+ if (mSizeBytes == 0 || mDurationUs < 0) {
return false;
}
@@ -54,7 +54,7 @@
fx = 0.0f;
} else if( percent >= 100.0f ) {
fx = 256.0f;
- } else {
+ } else if (mTOCValid) {
int a = (int)percent;
float fa, fb;
if ( a == 0 ) {
@@ -68,6 +68,8 @@
fb = 256.0f;
}
fx = fa + (fb-fa)*(percent-a);
+ } else {
+ fx = percent * 2.56f;
}
*pos = (int)((1.0f/256.0f)*fx*mSizeBytes) + mFirstFramePos;
diff --git a/media/extractors/mp4/AC4Parser.cpp b/media/extractors/mp4/AC4Parser.cpp
new file mode 100644
index 0000000..167d474
--- /dev/null
+++ b/media/extractors/mp4/AC4Parser.cpp
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2018 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_NDEBUG 0
+#define LOG_TAG "AC4Parser"
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "AC4Parser.h"
+
+#define BOOLSTR(a) ((a)?"true":"false")
+#define BYTE_ALIGN mBitReader.skipBits(mBitReader.numBitsLeft() % 8)
+#define CHECK_BITS_LEFT(n) if (mBitReader.numBitsLeft() < n) {return false;}
+
+namespace android {
+
+AC4Parser::AC4Parser() {
+}
+
+AC4DSIParser::AC4DSIParser(ABitReader &br)
+ : mBitReader(br){
+
+ mDSISize = mBitReader.numBitsLeft();
+}
+
+// ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
+static const char *ChannelModes[] = {
+ "mono",
+ "stereo",
+ "3.0",
+ "5.0",
+ "5.1",
+ "7.0 (3/4/0)",
+ "7.1 (3/4/0.1)",
+ "7.0 (5/2/0)",
+ "7.1 (5/2/0.1)",
+ "7.0 (3/2/2)",
+ "7.1 (3/2/2.1)",
+ "7.0.4",
+ "7.1.4",
+ "9.0.4",
+ "9.1.4",
+ "22.2"
+};
+
+static const char* ContentClassifier[] = {
+ "Complete Main",
+ "Music and Effects",
+ "Visually Impaired",
+ "Hearing Impaired",
+ "Dialog",
+ "Commentary",
+ "Emergency",
+ "Voice Over"
+};
+
+bool AC4DSIParser::parseLanguageTag(uint32_t presentationID, uint32_t substreamID){
+ CHECK_BITS_LEFT(6);
+ uint32_t n_language_tag_bytes = mBitReader.getBits(6);
+ if (n_language_tag_bytes < 2 || n_language_tag_bytes >= 42) {
+ return false;
+ }
+ CHECK_BITS_LEFT(n_language_tag_bytes * 8);
+ char language_tag_bytes[42]; // TS 103 190 part 1 4.3.3.8.7
+ for (uint32_t i = 0; i < n_language_tag_bytes; i++) {
+ language_tag_bytes[i] = (char)mBitReader.getBits(8);
+ }
+ language_tag_bytes[n_language_tag_bytes] = 0;
+ ALOGV("%u.%u: language_tag = %s\n", presentationID, substreamID, language_tag_bytes);
+
+ std::string language(language_tag_bytes, n_language_tag_bytes);
+ mPresentations[presentationID].mLanguage = language;
+
+ return true;
+}
+
+// TS 103 190-1 v1.2.1 E.5 and TS 103 190-2 v1.1.1 E.9
+bool AC4DSIParser::parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID){
+ CHECK_BITS_LEFT(5);
+ uint32_t channel_mode = mBitReader.getBits(5);
+ CHECK_BITS_LEFT(2);
+ uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
+ CHECK_BITS_LEFT(1);
+ bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u: channel_mode = %u (%s)\n", presentationID, substreamID, channel_mode,
+ channel_mode < NELEM(ChannelModes) ? ChannelModes[channel_mode] : "reserved");
+ ALOGV("%u.%u: dsi_sf_multiplier = %u\n", presentationID,
+ substreamID, dsi_sf_multiplier);
+ ALOGV("%u.%u: b_substream_bitrate_indicator = %s\n", presentationID,
+ substreamID, BOOLSTR(b_substream_bitrate_indicator));
+
+ if (b_substream_bitrate_indicator) {
+ CHECK_BITS_LEFT(5);
+ uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
+ ALOGV("%u.%u: substream_bitrate_indicator = %u\n", presentationID, substreamID,
+ substream_bitrate_indicator);
+ }
+ if (channel_mode >= 7 && channel_mode <= 10) {
+ CHECK_BITS_LEFT(1);
+ uint32_t add_ch_base = mBitReader.getBits(1);
+ ALOGV("%u.%u: add_ch_base = %u\n", presentationID, substreamID, add_ch_base);
+ }
+ CHECK_BITS_LEFT(1);
+ bool b_content_type = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u: b_content_type = %s\n", presentationID, substreamID, BOOLSTR(b_content_type));
+ if (b_content_type) {
+ CHECK_BITS_LEFT(3);
+ uint32_t content_classifier = mBitReader.getBits(3);
+ ALOGV("%u.%u: content_classifier = %u (%s)\n", presentationID, substreamID,
+ content_classifier, ContentClassifier[content_classifier]);
+
+ // For streams based on TS 103 190 part 1 the presentation level channel_mode doesn't
+ // exist and so we use the channel_mode from either the CM or M&E substream
+ // (they are mutually exclusive)
+ if (mPresentations[presentationID].mChannelMode == -1 &&
+ (content_classifier == 0 || content_classifier == 1)) {
+ mPresentations[presentationID].mChannelMode = channel_mode;
+ }
+ mPresentations[presentationID].mContentClassifier = content_classifier;
+ CHECK_BITS_LEFT(1);
+ bool b_language_indicator = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, substreamID,
+ BOOLSTR(b_language_indicator));
+ if (b_language_indicator) {
+ if (!parseLanguageTag(presentationID, substreamID)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// ETSI TS 103 190-2 v1.1.1 section E.11
+bool AC4DSIParser::parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID)
+{
+ CHECK_BITS_LEFT(1);
+ bool b_substreams_present = (mBitReader.getBits(1) == 1);
+ CHECK_BITS_LEFT(1);
+ bool b_hsf_ext = (mBitReader.getBits(1) == 1);
+ CHECK_BITS_LEFT(1);
+ bool b_channel_coded = (mBitReader.getBits(1) == 1);
+ CHECK_BITS_LEFT(8);
+ uint32_t n_substreams = mBitReader.getBits(8);
+ ALOGV("%u.%u: b_substreams_present = %s\n", presentationID, groupID,
+ BOOLSTR(b_substreams_present));
+ ALOGV("%u.%u: b_hsf_ext = %s\n", presentationID, groupID, BOOLSTR(b_hsf_ext));
+ ALOGV("%u.%u: b_channel_coded = %s\n", presentationID, groupID, BOOLSTR(b_channel_coded));
+ ALOGV("%u.%u: n_substreams = %u\n", presentationID, groupID, n_substreams);
+
+ for (uint32_t i = 0; i < n_substreams; i++) {
+ CHECK_BITS_LEFT(2);
+ uint32_t dsi_sf_multiplier = mBitReader.getBits(2);
+ CHECK_BITS_LEFT(1);
+ bool b_substream_bitrate_indicator = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u.%u: dsi_sf_multiplier = %u\n", presentationID, groupID, i, dsi_sf_multiplier);
+ ALOGV("%u.%u.%u: b_substream_bitrate_indicator = %s\n", presentationID, groupID, i,
+ BOOLSTR(b_substream_bitrate_indicator));
+
+ if (b_substream_bitrate_indicator) {
+ CHECK_BITS_LEFT(5);
+ uint32_t substream_bitrate_indicator = mBitReader.getBits(5);
+ ALOGV("%u.%u.%u: substream_bitrate_indicator = %u\n", presentationID, groupID, i,
+ substream_bitrate_indicator);
+ }
+ if (b_channel_coded) {
+ CHECK_BITS_LEFT(24);
+ uint32_t dsi_substream_channel_mask = mBitReader.getBits(24);
+ ALOGV("%u.%u.%u: dsi_substream_channel_mask = 0x%06x\n", presentationID, groupID, i,
+ dsi_substream_channel_mask);
+ } else {
+ CHECK_BITS_LEFT(1);
+ bool b_ajoc = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u.%u: b_ajoc = %s\n", presentationID, groupID, i, BOOLSTR(b_ajoc));
+ if (b_ajoc) {
+ CHECK_BITS_LEFT(1);
+ bool b_static_dmx = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u.%u: b_static_dmx = %s\n", presentationID, groupID, i,
+ BOOLSTR(b_static_dmx));
+ if (!b_static_dmx) {
+ CHECK_BITS_LEFT(4);
+ uint32_t n_dmx_objects_minus1 = mBitReader.getBits(4);
+ ALOGV("%u.%u.%u: n_dmx_objects_minus1 = %u\n", presentationID, groupID, i,
+ n_dmx_objects_minus1);
+ }
+ CHECK_BITS_LEFT(6);
+ uint32_t n_umx_objects_minus1 = mBitReader.getBits(6);
+ ALOGV("%u.%u.%u: n_umx_objects_minus1 = %u\n", presentationID, groupID, i,
+ n_umx_objects_minus1);
+ }
+ CHECK_BITS_LEFT(4);
+ mBitReader.skipBits(4); // objects_assignment_mask
+ }
+ }
+
+ CHECK_BITS_LEFT(1);
+ bool b_content_type = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u: b_content_type = %s\n", presentationID, groupID, BOOLSTR(b_content_type));
+ if (b_content_type) {
+ CHECK_BITS_LEFT(3);
+ uint32_t content_classifier = mBitReader.getBits(3);
+ ALOGV("%u.%u: content_classifier = %s (%u)\n", presentationID, groupID,
+ ContentClassifier[content_classifier], content_classifier);
+
+ mPresentations[presentationID].mContentClassifier = content_classifier;
+
+ CHECK_BITS_LEFT(1);
+ bool b_language_indicator = (mBitReader.getBits(1) == 1);
+ ALOGV("%u.%u: b_language_indicator = %s\n", presentationID, groupID,
+ BOOLSTR(b_language_indicator));
+
+ if (b_language_indicator) {
+ if (!parseLanguageTag(presentationID, groupID)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool AC4DSIParser::parseBitrateDsi() {
+ CHECK_BITS_LEFT(2 + 32 + 32);
+ mBitReader.skipBits(2); // bit_rate_mode
+ mBitReader.skipBits(32); // bit_rate
+ mBitReader.skipBits(32); // bit_rate_precision
+
+ return true;
+}
+
+// TS 103 190-1 section E.4 (ac4_dsi) and TS 103 190-2 section E.6 (ac4_dsi_v1)
+bool AC4DSIParser::parse() {
+ CHECK_BITS_LEFT(3);
+ uint32_t ac4_dsi_version = mBitReader.getBits(3);
+ if (ac4_dsi_version > 1) {
+ ALOGE("error while parsing ac-4 dsi: only versions 0 and 1 are supported");
+ return false;
+ }
+
+ CHECK_BITS_LEFT(7 + 1 + 4 + 9);
+ uint32_t bitstream_version = mBitReader.getBits(7);
+ mBitReader.skipBits(1); // fs_index
+ mBitReader.skipBits(4); // frame_rate_index
+ uint32_t n_presentations = mBitReader.getBits(9);
+
+ int32_t short_program_id = -1;
+ if (bitstream_version > 1) {
+ if (ac4_dsi_version == 0){
+ ALOGE("invalid ac4 dsi");
+ return false;
+ }
+ CHECK_BITS_LEFT(1);
+ bool b_program_id = (mBitReader.getBits(1) == 1);
+ if (b_program_id) {
+ CHECK_BITS_LEFT(16 + 1);
+ short_program_id = mBitReader.getBits(16);
+ bool b_uuid = (mBitReader.getBits(1) == 1);
+ if (b_uuid) {
+ const uint32_t kAC4UUIDSizeInBytes = 16;
+ char program_uuid[kAC4UUIDSizeInBytes];
+ CHECK_BITS_LEFT(kAC4UUIDSizeInBytes * 8);
+ for (uint32_t i = 0; i < kAC4UUIDSizeInBytes; i++) {
+ program_uuid[i] = (char)(mBitReader.getBits(8));
+ }
+ ALOGV("UUID = %s", program_uuid);
+ }
+ }
+ }
+
+ if (ac4_dsi_version == 1) {
+ if (!parseBitrateDsi()) {
+ return false;
+ }
+ BYTE_ALIGN;
+ }
+
+ for (uint32_t presentation = 0; presentation < n_presentations; presentation++) {
+ mPresentations[presentation].mProgramID = short_program_id;
+ // known as b_single_substream in ac4_dsi_version 0
+ bool b_single_substream_group = false;
+ uint32_t presentation_config = 0, presentation_version = 0;
+ uint32_t pres_bytes = 0;
+
+ if (ac4_dsi_version == 0) {
+ CHECK_BITS_LEFT(1 + 5 + 5);
+ b_single_substream_group = (mBitReader.getBits(1) == 1);
+ presentation_config = mBitReader.getBits(5);
+ presentation_version = mBitReader.getBits(5);
+ } else if (ac4_dsi_version == 1) {
+ CHECK_BITS_LEFT(8 + 8);
+ presentation_version = mBitReader.getBits(8);
+ pres_bytes = mBitReader.getBits(8);
+ if (pres_bytes == 0xff) {
+ CHECK_BITS_LEFT(16);
+ pres_bytes += mBitReader.getBits(16);
+ }
+ ALOGV("%u: pres_bytes = %u\n", presentation, pres_bytes);
+ if (presentation_version > 1) {
+ CHECK_BITS_LEFT(pres_bytes * 8);
+ mBitReader.skipBits(pres_bytes * 8);
+ continue;
+ }
+ // ac4_presentation_v0_dsi() and ac4_presentation_v1_dsi() both
+ // start with a presentation_config of 5 bits
+ CHECK_BITS_LEFT(5);
+ presentation_config = mBitReader.getBits(5);
+ b_single_substream_group = (presentation_config == 0x1f);
+ }
+
+ static const char *PresentationConfig[] = {
+ "Music&Effects + Dialog",
+ "Main + DE",
+ "Main + Associate",
+ "Music&Effects + Dialog + Associate",
+ "Main + DE + Associate",
+ "Arbitrary substream groups",
+ "EMDF only"
+ };
+ ALOGV("%u: b_single_substream/group = %s\n", presentation,
+ BOOLSTR(b_single_substream_group));
+ ALOGV("%u: presentation_version = %u\n", presentation, presentation_version);
+ ALOGV("%u: presentation_config = %u (%s)\n", presentation, presentation_config,
+ (presentation_config >= NELEM(PresentationConfig) ?
+ "reserved" : PresentationConfig[presentation_config]));
+
+ /* record a marker, less the size of the presentation_config */
+ uint64_t start = (mDSISize - mBitReader.numBitsLeft()) / 8;
+
+ bool b_add_emdf_substreams = false;
+ if (!b_single_substream_group && presentation_config == 6) {
+ b_add_emdf_substreams = true;
+ ALOGV("%u: b_add_emdf_substreams = %s\n", presentation, BOOLSTR(b_add_emdf_substreams));
+ } else {
+ CHECK_BITS_LEFT(3 + 1);
+ uint32_t mdcompat = mBitReader.getBits(3);
+ ALOGV("%u: mdcompat = %d\n", presentation, mdcompat);
+
+ bool b_presentation_group_index = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_presentation_group_index = %s\n", presentation,
+ BOOLSTR(b_presentation_group_index));
+ if (b_presentation_group_index) {
+ CHECK_BITS_LEFT(5);
+ mPresentations[presentation].mGroupIndex = mBitReader.getBits(5);
+ ALOGV("%u: presentation_group_index = %d\n", presentation,
+ mPresentations[presentation].mGroupIndex);
+ }
+ CHECK_BITS_LEFT(2);
+ uint32_t dsi_frame_rate_multiply_info = mBitReader.getBits(2);
+ ALOGV("%u: dsi_frame_rate_multiply_info = %d\n", presentation,
+ dsi_frame_rate_multiply_info);
+ if (ac4_dsi_version == 1 && presentation_version == 1) {
+ CHECK_BITS_LEFT(2);
+ uint32_t dsi_frame_rate_fraction_info = mBitReader.getBits(2);
+ ALOGV("%u: dsi_frame_rate_fraction_info = %d\n", presentation,
+ dsi_frame_rate_fraction_info);
+ }
+ CHECK_BITS_LEFT(5 + 10);
+ uint32_t presentation_emdf_version = mBitReader.getBits(5);
+ uint32_t presentation_key_id = mBitReader.getBits(10);
+ ALOGV("%u: presentation_emdf_version = %d\n", presentation, presentation_emdf_version);
+ ALOGV("%u: presentation_key_id = %d\n", presentation, presentation_key_id);
+
+ if (ac4_dsi_version == 1) {
+ bool b_presentation_channel_coded = false;
+ if (presentation_version == 0) {
+ b_presentation_channel_coded = true;
+ } else {
+ CHECK_BITS_LEFT(1);
+ b_presentation_channel_coded = (mBitReader.getBits(1) == 1);
+ }
+ ALOGV("%u: b_presentation_channel_coded = %s\n", presentation,
+ BOOLSTR(b_presentation_channel_coded));
+ if (b_presentation_channel_coded) {
+ if (presentation_version == 1) {
+ CHECK_BITS_LEFT(5);
+ uint32_t dsi_presentation_ch_mode = mBitReader.getBits(5);
+ mPresentations[presentation].mChannelMode = dsi_presentation_ch_mode;
+ ALOGV("%u: dsi_presentation_ch_mode = %d (%s)\n", presentation,
+ dsi_presentation_ch_mode,
+ dsi_presentation_ch_mode < NELEM(ChannelModes) ?
+ ChannelModes[dsi_presentation_ch_mode] : "reserved");
+
+ if (dsi_presentation_ch_mode >= 11 && dsi_presentation_ch_mode <= 14) {
+ CHECK_BITS_LEFT(1 + 2);
+ uint32_t pres_b_4_back_channels_present = mBitReader.getBits(1);
+ uint32_t pres_top_channel_pairs = mBitReader.getBits(2);
+ ALOGV("%u: pres_b_4_back_channels_present = %s\n", presentation,
+ BOOLSTR(pres_b_4_back_channels_present));
+ ALOGV("%u: pres_top_channel_pairs = %d\n", presentation,
+ pres_top_channel_pairs);
+ }
+ }
+ // presentation_channel_mask in ac4_presentation_v0_dsi()
+ CHECK_BITS_LEFT(24);
+ uint32_t presentation_channel_mask_v1 = mBitReader.getBits(24);
+ ALOGV("%u: presentation_channel_mask_v1 = 0x%06x\n", presentation,
+ presentation_channel_mask_v1);
+ }
+ if (presentation_version == 1) {
+ CHECK_BITS_LEFT(1);
+ bool b_presentation_core_differs = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_presentation_core_differs = %s\n", presentation,
+ BOOLSTR(b_presentation_core_differs));
+ if (b_presentation_core_differs) {
+ CHECK_BITS_LEFT(1);
+ bool b_presentation_core_channel_coded = (mBitReader.getBits(1) == 1);
+ if (b_presentation_core_channel_coded) {
+ CHECK_BITS_LEFT(2);
+ mBitReader.skipBits(2); // dsi_presentation_channel_mode_core
+ }
+ }
+ CHECK_BITS_LEFT(1);
+ bool b_presentation_filter = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_presentation_filter = %s\n", presentation,
+ BOOLSTR(b_presentation_filter));
+ if (b_presentation_filter) {
+ CHECK_BITS_LEFT(1 + 8);
+ bool b_enable_presentation = (mBitReader.getBits(1) == 1);
+ if (!b_enable_presentation) {
+ mPresentations[presentation].mEnabled = false;
+ }
+ ALOGV("%u: b_enable_presentation = %s\n", presentation,
+ BOOLSTR(b_enable_presentation));
+ uint32_t n_filter_bytes = mBitReader.getBits(8);
+ CHECK_BITS_LEFT(n_filter_bytes * 8);
+ for (uint32_t i = 0; i < n_filter_bytes; i++) {
+ mBitReader.skipBits(8); // filter_data
+ }
+ }
+ }
+ } /* ac4_dsi_version == 1 */
+
+ if (b_single_substream_group) {
+ if (presentation_version == 0) {
+ if (!parseSubstreamDSI(presentation, 0)) {
+ return false;
+ }
+ } else {
+ if (!parseSubstreamGroupDSI(presentation, 0)) {
+ return false;
+ }
+ }
+ } else {
+ if (ac4_dsi_version == 1) {
+ CHECK_BITS_LEFT(1);
+ bool b_multi_pid = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_multi_pid = %s\n", presentation, BOOLSTR(b_multi_pid));
+ } else {
+ CHECK_BITS_LEFT(1);
+ bool b_hsf_ext = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_hsf_ext = %s\n", presentation, BOOLSTR(b_hsf_ext));
+ }
+ switch (presentation_config) {
+ case 0:
+ case 1:
+ case 2:
+ if (presentation_version == 0) {
+ if (!parseSubstreamDSI(presentation, 0)) {
+ return false;
+ }
+ if (!parseSubstreamDSI(presentation, 1)) {
+ return false;
+ }
+ } else {
+ if (!parseSubstreamGroupDSI(presentation, 0)) {
+ return false;
+ }
+ if (!parseSubstreamGroupDSI(presentation, 1)) {
+ return false;
+ }
+ }
+ break;
+ case 3:
+ case 4:
+ if (presentation_version == 0) {
+ if (!parseSubstreamDSI(presentation, 0)) {
+ return false;
+ }
+ if (!parseSubstreamDSI(presentation, 1)) {
+ return false;
+ }
+ if (!parseSubstreamDSI(presentation, 2)) {
+ return false;
+ }
+ } else {
+ if (!parseSubstreamGroupDSI(presentation, 0)) {
+ return false;
+ }
+ if (!parseSubstreamGroupDSI(presentation, 1)) {
+ return false;
+ }
+ if (!parseSubstreamGroupDSI(presentation, 2)) {
+ return false;
+ }
+ }
+ break;
+ case 5:
+ if (presentation_version == 0) {
+ if (!parseSubstreamDSI(presentation, 0)) {
+ return false;
+ }
+ } else {
+ CHECK_BITS_LEFT(3);
+ uint32_t n_substream_groups_minus2 = mBitReader.getBits(3);
+ ALOGV("%u: n_substream_groups_minus2 = %d\n", presentation,
+ n_substream_groups_minus2);
+ for (uint32_t sg = 0; sg < n_substream_groups_minus2 + 2; sg++) {
+ if (!parseSubstreamGroupDSI(presentation, sg)) {
+ return false;
+ }
+ }
+ }
+ break;
+ default:
+ CHECK_BITS_LEFT(7);
+ uint32_t n_skip_bytes = mBitReader.getBits(7);
+ CHECK_BITS_LEFT(n_skip_bytes * 8)
+ for (uint32_t j = 0; j < n_skip_bytes; j++) {
+ mBitReader.getBits(8);
+ }
+ break;
+ }
+ CHECK_BITS_LEFT(1 + 1);
+ bool b_pre_virtualized = (mBitReader.getBits(1) == 1);
+ mPresentations[presentation].mPreVirtualized = b_pre_virtualized;
+ b_add_emdf_substreams = (mBitReader.getBits(1) == 1);
+ ALOGV("%u: b_pre_virtualized = %s\n", presentation, BOOLSTR(b_pre_virtualized));
+ ALOGV("%u: b_add_emdf_substreams = %s\n", presentation,
+ BOOLSTR(b_add_emdf_substreams));
+ }
+ }
+ if (b_add_emdf_substreams) {
+ CHECK_BITS_LEFT(7);
+ uint32_t n_add_emdf_substreams = mBitReader.getBits(7);
+ for (uint32_t j = 0; j < n_add_emdf_substreams; j++) {
+ CHECK_BITS_LEFT(5 + 10);
+ uint32_t substream_emdf_version = mBitReader.getBits(5);
+ uint32_t substream_key_id = mBitReader.getBits(10);
+ ALOGV("%u: emdf_substream[%d]: version=%d, key_id=%d\n", presentation, j,
+ substream_emdf_version, substream_key_id);
+ }
+ }
+
+ bool b_presentation_bitrate_info = false;
+ if (presentation_version > 0) {
+ CHECK_BITS_LEFT(1);
+ b_presentation_bitrate_info = (mBitReader.getBits(1) == 1);
+ }
+
+ ALOGV("b_presentation_bitrate_info = %s\n", BOOLSTR(b_presentation_bitrate_info));
+ if (b_presentation_bitrate_info) {
+ if (!parseBitrateDsi()) {
+ return false;
+ }
+ }
+
+ if (presentation_version > 0) {
+ CHECK_BITS_LEFT(1);
+ bool b_alternative = (mBitReader.getBits(1) == 1);
+ ALOGV("b_alternative = %s\n", BOOLSTR(b_alternative));
+ if (b_alternative) {
+ BYTE_ALIGN;
+ CHECK_BITS_LEFT(16);
+ uint32_t name_len = mBitReader.getBits(16);
+ char* presentation_name = new char[name_len+1];
+ CHECK_BITS_LEFT(name_len * 8);
+ for (uint32_t i = 0; i < name_len; i++) {
+ presentation_name[i] = (char)(mBitReader.getBits(8));
+ }
+ presentation_name[name_len] = '\0';
+ std::string description(presentation_name, name_len);
+ mPresentations[presentation].mDescription = description;
+ CHECK_BITS_LEFT(5);
+ uint32_t n_targets = mBitReader.getBits(5);
+ CHECK_BITS_LEFT(n_targets * (3 + 8));
+ for (uint32_t i = 0; i < n_targets; i++){
+ mBitReader.skipBits(3); // target_md_compat
+ mBitReader.skipBits(8); // target_device_category
+ }
+ }
+ }
+
+ BYTE_ALIGN;
+
+ if (ac4_dsi_version == 1) {
+ uint64_t end = (mDSISize - mBitReader.numBitsLeft()) / 8;
+ if (mBitReader.numBitsLeft() % 8 != 0) {
+ end += 1;
+ }
+
+ uint64_t presentation_bytes = end - start;
+ uint64_t skip_bytes = pres_bytes - presentation_bytes;
+ ALOGV("skipping = %" PRIu64 " bytes", skip_bytes);
+ CHECK_BITS_LEFT(skip_bytes * 8);
+ mBitReader.skipBits(skip_bytes * 8);
+ }
+
+ // we should know this or something is probably wrong
+ // with the bitstream (or we don't support it)
+ if (mPresentations[presentation].mChannelMode == -1){
+ ALOGE("could not determing channel mode of presentation %d", presentation);
+ return false;
+ }
+ } /* each presentation */
+
+ return true;
+}
+
+};
diff --git a/media/extractors/mp4/AC4Parser.h b/media/extractors/mp4/AC4Parser.h
new file mode 100644
index 0000000..73b6e31
--- /dev/null
+++ b/media/extractors/mp4/AC4Parser.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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 AC4_PARSER_H_
+#define AC4_PARSER_H_
+
+#include <cstdint>
+#include <map>
+#include <string>
+
+#include <media/stagefright/foundation/ABitReader.h>
+
+namespace android {
+
+class AC4Parser {
+public:
+ AC4Parser();
+ virtual ~AC4Parser() { }
+
+ virtual bool parse() = 0;
+
+ struct AC4Presentation {
+ int32_t mChannelMode = -1;
+ int32_t mProgramID = -1;
+ int32_t mGroupIndex = -1;
+
+ // TS 103 190-1 v1.2.1 4.3.3.8.1
+ enum ContentClassifiers {
+ kCompleteMain,
+ kMusicAndEffects,
+ kVisuallyImpaired,
+ kHearingImpaired,
+ kDialog,
+ kCommentary,
+ kEmergency,
+ kVoiceOver
+ };
+
+ uint32_t mContentClassifier = kCompleteMain;
+
+ // ETSI TS 103 190-2 V1.1.1 (2015-09) Table 79: channel_mode
+ enum InputChannelMode {
+ kChannelMode_Mono,
+ kChannelMode_Stereo,
+ kChannelMode_3_0,
+ kChannelMode_5_0,
+ kChannelMode_5_1,
+ kChannelMode_7_0_34,
+ kChannelMode_7_1_34,
+ kChannelMode_7_0_52,
+ kChannelMode_7_1_52,
+ kChannelMode_7_0_322,
+ kChannelMode_7_1_322,
+ kChannelMode_7_0_4,
+ kChannelMode_7_1_4,
+ kChannelMode_9_0_4,
+ kChannelMode_9_1_4,
+ kChannelMode_22_2,
+ kChannelMode_Reserved,
+ };
+
+ bool mHasDialogEnhancements = false;
+ bool mPreVirtualized = false;
+ bool mEnabled = true;
+
+ std::string mLanguage;
+ std::string mDescription;
+ };
+ typedef std::map<uint32_t, AC4Presentation> AC4Presentations;
+
+ const AC4Presentations& getPresentations() const { return mPresentations; }
+
+protected:
+ AC4Presentations mPresentations;
+};
+
+class AC4DSIParser: public AC4Parser {
+public:
+ explicit AC4DSIParser(ABitReader &br);
+ virtual ~AC4DSIParser() { }
+
+ bool parse();
+
+private:
+ bool parseSubstreamDSI(uint32_t presentationID, uint32_t substreamID);
+ bool parseSubstreamGroupDSI(uint32_t presentationID, uint32_t groupID);
+ bool parseLanguageTag(uint32_t presentationID, uint32_t substreamID);
+ bool parseBitrateDsi();
+
+ uint64_t mDSISize;
+ ABitReader& mBitReader;
+};
+
+};
+
+#endif // AC4_PARSER_H_
diff --git a/media/extractors/mp4/Android.bp b/media/extractors/mp4/Android.bp
index fa739e8..40b2c97 100644
--- a/media/extractors/mp4/Android.bp
+++ b/media/extractors/mp4/Android.bp
@@ -2,6 +2,7 @@
name: "libmp4extractor_defaults",
srcs: [
+ "AC4Parser.cpp",
"ItemTable.cpp",
"MPEG4Extractor.cpp",
"SampleIterator.cpp",
diff --git a/media/extractors/mp4/MPEG4Extractor.cpp b/media/extractors/mp4/MPEG4Extractor.cpp
index 7b3b81d..8412812 100644
--- a/media/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/extractors/mp4/MPEG4Extractor.cpp
@@ -26,6 +26,7 @@
#include <utils/Log.h>
+#include "AC4Parser.h"
#include "MPEG4Extractor.h"
#include "SampleTable.h"
#include "ItemTable.h"
@@ -125,6 +126,8 @@
bool mIsAVC;
bool mIsHEVC;
+ bool mIsAC4;
+
size_t mNALLengthSize;
bool mStarted;
@@ -324,6 +327,8 @@
case FOURCC('h', 'v', 'c', '1'):
case FOURCC('h', 'e', 'v', '1'):
return MEDIA_MIMETYPE_VIDEO_HEVC;
+ case FOURCC('a', 'c', '-', '4'):
+ return MEDIA_MIMETYPE_AUDIO_AC4;
default:
ALOGW("Unknown fourcc: %c%c%c%c",
(fourcc >> 24) & 0xff,
@@ -2436,6 +2441,12 @@
return parseAC3SampleEntry(data_offset);
}
+ case FOURCC('a', 'c', '-', '4'):
+ {
+ *offset += chunk_size;
+ return parseAC4SampleEntry(data_offset);
+ }
+
case FOURCC('f', 't', 'y', 'p'):
{
if (chunk_data_size < 8 || depth != 0) {
@@ -2507,6 +2518,84 @@
return OK;
}
+status_t MPEG4Extractor::parseAC4SampleEntry(off64_t offset) {
+ // skip 16 bytes:
+ // + 6-byte reserved,
+ // + 2-byte data reference index,
+ // + 8-byte reserved
+ offset += 16;
+ uint16_t channelCount;
+ if (!mDataSource->getUInt16(offset, &channelCount)) {
+ ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read channel count");
+ return ERROR_MALFORMED;
+ }
+ // skip 8 bytes:
+ // + 2-byte channelCount,
+ // + 2-byte sample size,
+ // + 4-byte reserved
+ offset += 8;
+ uint16_t sampleRate;
+ if (!mDataSource->getUInt16(offset, &sampleRate)) {
+ ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read sample rate");
+ return ERROR_MALFORMED;
+ }
+
+ // skip 4 bytes:
+ // + 2-byte sampleRate,
+ // + 2-byte reserved
+ offset += 4;
+
+ if (mLastTrack == NULL) {
+ return ERROR_MALFORMED;
+ }
+ mLastTrack->meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
+ mLastTrack->meta.setInt32(kKeyChannelCount, channelCount);
+ mLastTrack->meta.setInt32(kKeySampleRate, sampleRate);
+ return parseAC4SpecificBox(offset);
+}
+
+status_t MPEG4Extractor::parseAC4SpecificBox(off64_t offset) {
+ uint32_t size;
+ // + 4-byte size
+ // + 4-byte type
+ // + 3-byte payload
+ const uint32_t kAC4MinimumBoxSize = 4 + 4 + 3;
+ if (!mDataSource->getUInt32(offset, &size) || size < kAC4MinimumBoxSize) {
+ ALOGE("MPEG4Extractor: error while reading ac-4 block: cannot read specific box size");
+ return ERROR_MALFORMED;
+ }
+
+ // + 4-byte size
+ offset += 4;
+ uint32_t type;
+ if (!mDataSource->getUInt32(offset, &type) || type != FOURCC('d', 'a', 'c', '4')) {
+ ALOGE("MPEG4Extractor: error while reading ac-4 specific block: header not dac4");
+ return ERROR_MALFORMED;
+ }
+
+ // + 4-byte type
+ offset += 4;
+ // at least for AC4 DSI v1 this is big enough
+ const uint32_t kAC4SpecificBoxPayloadSize = 256;
+ uint8_t chunk[kAC4SpecificBoxPayloadSize];
+ ssize_t dsiSize = size - 8; // size of box - size and type fields
+ if (dsiSize >= (ssize_t)kAC4SpecificBoxPayloadSize ||
+ mDataSource->readAt(offset, chunk, dsiSize) != dsiSize) {
+ ALOGE("MPEG4Extractor: error while reading ac-4 specific block: bitstream fields");
+ return ERROR_MALFORMED;
+ }
+ // + size-byte payload
+ offset += dsiSize;
+ ABitReader br(chunk, dsiSize);
+ AC4DSIParser parser(br);
+ if (!parser.parse()){
+ ALOGE("MPEG4Extractor: error while parsing ac-4 specific block");
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
status_t MPEG4Extractor::parseAC3SampleEntry(off64_t offset) {
// skip 16 bytes:
// + 6-byte reserved,
@@ -3857,6 +3946,7 @@
mCurrentSampleInfoOffsets(NULL),
mIsAVC(false),
mIsHEVC(false),
+ mIsAC4(false),
mNALLengthSize(0),
mStarted(false),
mGroup(NULL),
@@ -3890,6 +3980,7 @@
mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHEVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+ mIsAC4 = !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AC4);
if (mIsAVC) {
uint32_t type;
@@ -4830,7 +4921,7 @@
}
}
- if ((!mIsAVC && !mIsHEVC) || mWantsNALFragments) {
+ if ((!mIsAVC && !mIsHEVC && !mIsAC4) || mWantsNALFragments) {
if (newBuffer) {
ssize_t num_bytes_read =
mDataSource->readAt(offset, (uint8_t *)mBuffer->data(), size);
@@ -4862,13 +4953,20 @@
++mCurrentSampleIndex;
}
- if (!mIsAVC && !mIsHEVC) {
+ if (!mIsAVC && !mIsHEVC && !mIsAC4) {
*out = mBuffer;
mBuffer = NULL;
return OK;
}
+ if (mIsAC4) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_IO;
+ }
+
// Each NAL unit is split up into its constituent fragments and
// each one of them returned in its own buffer.
@@ -4907,6 +5005,58 @@
*out = clone;
return OK;
+ } else if (mIsAC4) {
+ CHECK(mBuffer != NULL);
+ // Make sure there is enough space to write the sync header and the raw frame
+ if (mBuffer->range_length() < (7 + size)) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
+ size_t dstOffset = 0;
+ // Add AC-4 sync header to MPEG4 encapsulated AC-4 raw frame
+ // AC40 sync word, meaning no CRC at the end of the frame
+ dstData[dstOffset++] = 0xAC;
+ dstData[dstOffset++] = 0x40;
+ dstData[dstOffset++] = 0xFF;
+ dstData[dstOffset++] = 0xFF;
+ dstData[dstOffset++] = (uint8_t)((size >> 16) & 0xFF);
+ dstData[dstOffset++] = (uint8_t)((size >> 8) & 0xFF);
+ dstData[dstOffset++] = (uint8_t)((size >> 0) & 0xFF);
+
+ ssize_t numBytesRead = mDataSource->readAt(offset, dstData + dstOffset, size);
+ if (numBytesRead != (ssize_t)size) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ mBuffer->set_range(0, dstOffset + size);
+ mBuffer->meta_data().clear();
+ mBuffer->meta_data().setInt64(
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
+ mBuffer->meta_data().setInt64(
+ kKeyDuration, ((int64_t)stts * 1000000) / mTimescale);
+
+ if (targetSampleTimeUs >= 0) {
+ mBuffer->meta_data().setInt64(
+ kKeyTargetTime, targetSampleTimeUs);
+ }
+
+ if (isSyncSample) {
+ mBuffer->meta_data().setInt32(kKeyIsSyncFrame, 1);
+ }
+
+ ++mCurrentSampleIndex;
+
+ *out = mBuffer;
+ mBuffer = NULL;
+
+ return OK;
} else {
// Whole NAL units are returned but each fragment is prefixed by
// the start code (0x00 00 00 01).
@@ -5361,6 +5511,8 @@
return OK;
}
+
+ return OK;
}
MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
diff --git a/media/extractors/mp4/MPEG4Extractor.h b/media/extractors/mp4/MPEG4Extractor.h
index 3ea0963..ed70aa7 100644
--- a/media/extractors/mp4/MPEG4Extractor.h
+++ b/media/extractors/mp4/MPEG4Extractor.h
@@ -141,6 +141,8 @@
status_t parseAC3SampleEntry(off64_t offset);
status_t parseAC3SpecificBox(off64_t offset, uint16_t sampleRate);
+ status_t parseAC4SampleEntry(off64_t offset);
+ status_t parseAC4SpecificBox(off64_t offset);
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);
diff --git a/media/libaaudio/examples/input_monitor/jni/Android.mk b/media/libaaudio/examples/input_monitor/jni/Android.mk
deleted file mode 100644
index a0b981c..0000000
--- a/media/libaaudio/examples/input_monitor/jni/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/src \
- frameworks/av/media/libaaudio/examples/utils
-
-# NDK recommends using this kind of relative path instead of an absolute path.
-LOCAL_SRC_FILES:= ../src/input_monitor.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := input_monitor
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := input_monitor_callback
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libaaudio_prebuilt
-LOCAL_SRC_FILES := libaaudio.so
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
-include $(PREBUILT_SHARED_LIBRARY)
diff --git a/media/libaaudio/examples/input_monitor/jni/Application.mk b/media/libaaudio/examples/input_monitor/jni/Application.mk
deleted file mode 100644
index e74475c..0000000
--- a/media/libaaudio/examples/input_monitor/jni/Application.mk
+++ /dev/null
@@ -1,3 +0,0 @@
-# TODO remove then when we support other architectures
-APP_ABI := arm64-v8a
-APP_CPPFLAGS += -std=c++11
diff --git a/media/libaaudio/examples/loopback/jni/Android.mk b/media/libaaudio/examples/loopback/jni/Android.mk
deleted file mode 100644
index aebe877..0000000
--- a/media/libaaudio/examples/loopback/jni/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-# NDK recommends using this kind of relative path instead of an absolute path.
-LOCAL_SRC_FILES:= ../src/loopback.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_STATIC_LIBRARIES := libsndfile
-LOCAL_SHARED_LIBRARIES := libaaudio libaudioutils
-LOCAL_MODULE := aaudio_loopback
-include $(BUILD_EXECUTABLE)
diff --git a/media/libaaudio/examples/loopback/jni/Application.mk b/media/libaaudio/examples/loopback/jni/Application.mk
deleted file mode 100644
index ba44f37..0000000
--- a/media/libaaudio/examples/loopback/jni/Application.mk
+++ /dev/null
@@ -1 +0,0 @@
-APP_CPPFLAGS += -std=c++11
diff --git a/media/libaaudio/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
deleted file mode 100644
index 1a1bd43..0000000
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/src \
- frameworks/av/media/libaaudio/examples/utils
-
-# NDK recommends using this kind of relative path instead of an absolute path.
-LOCAL_SRC_FILES:= ../src/write_sine.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := write_sine
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_C_INCLUDES := \
- $(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include \
- frameworks/av/media/libaaudio/examples/utils
-
-LOCAL_SRC_FILES:= ../src/write_sine_callback.cpp
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_SHARED_LIBRARIES := libaaudio
-LOCAL_MODULE := write_sine_callback
-include $(BUILD_EXECUTABLE)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libaaudio_prebuilt
-LOCAL_SRC_FILES := libaaudio.so
-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
-include $(PREBUILT_SHARED_LIBRARY)
diff --git a/media/libaaudio/examples/write_sine/jni/Application.mk b/media/libaaudio/examples/write_sine/jni/Application.mk
deleted file mode 100644
index ba44f37..0000000
--- a/media/libaaudio/examples/write_sine/jni/Application.mk
+++ /dev/null
@@ -1 +0,0 @@
-APP_CPPFLAGS += -std=c++11
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index c072901..cb4bcfc 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -878,31 +878,25 @@
flags, selectedDeviceId, portId);
}
-status_t AudioSystem::startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+status_t AudioSystem::startOutput(audio_port_handle_t portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->startOutput(output, stream, session);
+ return aps->startOutput(portId);
}
-status_t AudioSystem::stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+status_t AudioSystem::stopOutput(audio_port_handle_t portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
- return aps->stopOutput(output, stream, session);
+ return aps->stopOutput(portId);
}
-void AudioSystem::releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+void AudioSystem::releaseOutput(audio_port_handle_t portId)
{
const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return;
- aps->releaseOutput(output, stream, session);
+ aps->releaseOutput(portId);
}
status_t AudioSystem::getInputForAttr(const audio_attributes_t *attr,
diff --git a/media/libaudioclient/IAudioPolicyService.cpp b/media/libaudioclient/IAudioPolicyService.cpp
index cf4b600..e229f4c 100644
--- a/media/libaudioclient/IAudioPolicyService.cpp
+++ b/media/libaudioclient/IAudioPolicyService.cpp
@@ -244,41 +244,29 @@
return status;
}
- virtual status_t startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+ virtual status_t startOutput(audio_port_handle_t portId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(output);
- data.writeInt32((int32_t) stream);
- data.writeInt32((int32_t) session);
+ data.writeInt32((int32_t)portId);
remote()->transact(START_OUTPUT, data, &reply);
return static_cast <status_t> (reply.readInt32());
}
- virtual status_t stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+ virtual status_t stopOutput(audio_port_handle_t portId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(output);
- data.writeInt32((int32_t) stream);
- data.writeInt32((int32_t) session);
+ data.writeInt32((int32_t)portId);
remote()->transact(STOP_OUTPUT, data, &reply);
return static_cast <status_t> (reply.readInt32());
}
- virtual void releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+ virtual void releaseOutput(audio_port_handle_t portId)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
- data.writeInt32(output);
- data.writeInt32((int32_t)stream);
- data.writeInt32((int32_t)session);
+ data.writeInt32((int32_t)portId);
remote()->transact(RELEASE_OUTPUT, data, &reply);
}
@@ -1075,34 +1063,22 @@
case START_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- audio_session_t session = (audio_session_t)data.readInt32();
- reply->writeInt32(static_cast <uint32_t>(startOutput(output,
- stream,
- session)));
+ const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(startOutput(portId)));
return NO_ERROR;
} break;
case STOP_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
- audio_stream_type_t stream =
- static_cast <audio_stream_type_t>(data.readInt32());
- audio_session_t session = (audio_session_t)data.readInt32();
- reply->writeInt32(static_cast <uint32_t>(stopOutput(output,
- stream,
- session)));
+ const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(stopOutput(portId)));
return NO_ERROR;
} break;
case RELEASE_OUTPUT: {
CHECK_INTERFACE(IAudioPolicyService, data, reply);
- audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
- audio_stream_type_t stream = (audio_stream_type_t)data.readInt32();
- audio_session_t session = (audio_session_t)data.readInt32();
- releaseOutput(output, stream, session);
+ const audio_port_handle_t portId = static_cast <audio_port_handle_t>(data.readInt32());
+ releaseOutput(portId);
return NO_ERROR;
} break;
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 4c0f796..10d6e92 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -224,15 +224,9 @@
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
- static status_t startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- static status_t stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- static void releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
+ static status_t startOutput(audio_port_handle_t portId);
+ static status_t stopOutput(audio_port_handle_t portId);
+ static void releaseOutput(audio_port_handle_t portId);
// Client must successfully hand off the handle reference to AudioFlinger via createRecord(),
// or release it with releaseInput().
diff --git a/media/libaudioclient/include/media/IAudioPolicyService.h b/media/libaudioclient/include/media/IAudioPolicyService.h
index c3876af..6c017a3 100644
--- a/media/libaudioclient/include/media/IAudioPolicyService.h
+++ b/media/libaudioclient/include/media/IAudioPolicyService.h
@@ -66,15 +66,9 @@
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId) = 0;
- virtual status_t startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session) = 0;
- virtual status_t stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session) = 0;
- virtual void releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session) = 0;
+ virtual status_t startOutput(audio_port_handle_t portId) = 0;
+ virtual status_t stopOutput(audio_port_handle_t portId) = 0;
+ virtual void releaseOutput(audio_port_handle_t portId) = 0;
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
index 55b946f..fa0415b 100644
--- a/media/libeffects/config/include/media/EffectsConfig.h
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -96,7 +96,7 @@
/** Parsed config, nullptr if the xml lib could not load the file */
std::unique_ptr<Config> parsedConfig;
size_t nbSkippedElement; //< Number of skipped invalid library, effect or processing chain
- const char* configPath; //< Path to the loaded configuration
+ const std::string configPath; //< Path to the loaded configuration
};
/** Parses the provided effect configuration.
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
index d79501f..351b1ee 100644
--- a/media/libeffects/config/src/EffectsConfig.cpp
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -250,14 +250,14 @@
return true;
}
-/** Internal version of the public parse(const char* path) with precondition `path != nullptr`. */
-ParsingResult parseWithPath(const char* path) {
+/** Internal version of the public parse(const char* path) where path always exist. */
+ParsingResult parseWithPath(std::string&& path) {
XMLDocument doc;
- doc.LoadFile(path);
+ doc.LoadFile(path.c_str());
if (doc.Error()) {
- ALOGE("Failed to parse %s: Tinyxml2 error (%d): %s", path,
+ ALOGE("Failed to parse %s: Tinyxml2 error (%d): %s", path.c_str(),
doc.ErrorID(), doc.ErrorStr());
- return {nullptr, 0, path};
+ return {nullptr, 0, std::move(path)};
}
auto config = std::make_unique<Config>();
@@ -295,7 +295,7 @@
}
}
}
- return {std::move(config), nbSkippedElements, path};
+ return {std::move(config), nbSkippedElements, std::move(path)};
}
}; // namespace
@@ -310,14 +310,14 @@
if (access(defaultPath.c_str(), R_OK) != 0) {
continue;
}
- auto result = parseWithPath(defaultPath.c_str());
+ auto result = parseWithPath(std::move(defaultPath));
if (result.parsedConfig != nullptr) {
return result;
}
}
ALOGE("Could not parse effect configuration in any of the default locations.");
- return {nullptr, 0, nullptr};
+ return {nullptr, 0, ""};
}
} // namespace effectsConfig
diff --git a/media/libeffects/factory/EffectsXmlConfigLoader.cpp b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
index 7a7d431..052a88b 100644
--- a/media/libeffects/factory/EffectsXmlConfigLoader.cpp
+++ b/media/libeffects/factory/EffectsXmlConfigLoader.cpp
@@ -327,7 +327,8 @@
&gSkippedEffects, &gSubEffectList);
ALOGE_IF(result.nbSkippedElement != 0, "%zu errors during loading of configuration: %s",
- result.nbSkippedElement, result.configPath ?: "No config file found");
+ result.nbSkippedElement,
+ result.configPath.empty() ? "No config file found" : result.configPath.c_str());
return result.nbSkippedElement;
}
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 1fa8789..0fb5abc 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -9,6 +9,7 @@
srcs: [
"JAudioTrack.cpp",
+ "JavaVMHelper.cpp",
"MediaPlayer2AudioOutput.cpp",
"mediaplayer2.cpp",
],
@@ -49,6 +50,10 @@
"media_plugin_headers",
],
+ include_dirs: [
+ "frameworks/base/core/jni",
+ ],
+
static_libs: [
"libmedia_helper",
"libstagefright_nuplayer2",
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index ac0cc57..778ae1b 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -21,7 +21,7 @@
#include "mediaplayer2/JAudioTrack.h"
#include <android_media_AudioErrors.h>
-#include <android_runtime/AndroidRuntime.h>
+#include <mediaplayer2/JavaVMHelper.h>
namespace android {
@@ -39,7 +39,7 @@
const audio_attributes_t* pAttributes, // AudioAttributes
float maxRequiredSpeed) { // bufferSizeInBytes
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
@@ -116,19 +116,19 @@
}
JAudioTrack::~JAudioTrack() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mAudioTrackCls);
}
size_t JAudioTrack::frameCount() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetBufferSizeInFrames = env->GetMethodID(
mAudioTrackCls, "getBufferSizeInFrames", "()I");
return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
}
size_t JAudioTrack::channelCount() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
}
@@ -143,7 +143,7 @@
return BAD_VALUE;
}
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
mAudioTrackCls, "getPlaybackHeadPosition", "()I");
*position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
@@ -152,7 +152,7 @@
}
bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
@@ -189,7 +189,7 @@
status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
// TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
// Should we do the same thing?
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
@@ -224,7 +224,7 @@
}
const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetPlaybackParams = env->GetMethodID(
mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
@@ -266,7 +266,7 @@
return media::VolumeShaper::Status(BAD_VALUE);
}
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
"(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
@@ -282,7 +282,7 @@
}
status_t JAudioTrack::setAuxEffectSendLevel(float level) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
@@ -290,14 +290,14 @@
}
status_t JAudioTrack::attachAuxEffect(int effectId) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
return javaToNativeStatus(result);
}
status_t JAudioTrack::setVolume(float left, float right) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
// TODO: Java setStereoVolume is deprecated. Do we really need this method?
jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
@@ -305,14 +305,14 @@
}
status_t JAudioTrack::setVolume(float volume) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
return javaToNativeStatus(result);
}
status_t JAudioTrack::start() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
// TODO: Should we catch the Java IllegalStateException from play()?
env->CallVoidMethod(mAudioTrackObj, jPlay);
@@ -324,7 +324,7 @@
return BAD_VALUE;
}
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jbyteArray jAudioData = env->NewByteArray(size);
env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
@@ -353,7 +353,7 @@
}
void JAudioTrack::stop() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
env->CallVoidMethod(mAudioTrackObj, jStop);
// TODO: Should we catch IllegalStateException?
@@ -365,20 +365,20 @@
}
void JAudioTrack::flush() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
env->CallVoidMethod(mAudioTrackObj, jFlush);
}
void JAudioTrack::pause() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
env->CallVoidMethod(mAudioTrackObj, jPause);
// TODO: Should we catch IllegalStateException?
}
bool JAudioTrack::isPlaying() const {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
@@ -393,7 +393,7 @@
}
uint32_t JAudioTrack::getSampleRate() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
}
@@ -403,7 +403,7 @@
return BAD_VALUE;
}
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetBufferSizeInFrames = env->GetMethodID(
mAudioTrackCls, "getBufferSizeInFrames", "()I");
int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
@@ -417,7 +417,7 @@
}
audio_format_t JAudioTrack::format() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
return audioFormatToNative(javaFormat);
@@ -454,7 +454,7 @@
}
audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
"()Landroid/media/AudioDeviceInfo;");
jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
@@ -469,14 +469,14 @@
}
audio_session_t JAudioTrack::getAudioSessionId() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
return (audio_session_t) sessionId;
}
status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
@@ -550,7 +550,7 @@
return NULL;
}
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
// Referenced "android_media_VolumeShaper.h".
jfloatArray xarray = nullptr;
@@ -595,7 +595,7 @@
jobject JAudioTrack::createVolumeShaperOperationObj(
const sp<media::VolumeShaper::Operation>& operation) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
@@ -647,7 +647,7 @@
}
jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
@@ -655,7 +655,7 @@
}
jobject JAudioTrack::createCallbackExecutor() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
+ JNIEnv *env = JavaVMHelper::getJNIEnv();
jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
"newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
diff --git a/media/libmediaplayer2/JavaVMHelper.cpp b/media/libmediaplayer2/JavaVMHelper.cpp
new file mode 100644
index 0000000..90aaa7f
--- /dev/null
+++ b/media/libmediaplayer2/JavaVMHelper.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 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 "JavaVMHelper"
+
+#include "mediaplayer2/JavaVMHelper.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <stdlib.h>
+
+namespace android {
+
+// static
+std::atomic<JavaVM *> JavaVMHelper::sJavaVM(NULL);
+
+// static
+JNIEnv *JavaVMHelper::getJNIEnv() {
+ JNIEnv *env;
+ JavaVM *vm = sJavaVM.load();
+ CHECK(vm != NULL);
+
+ if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) {
+ return NULL;
+ }
+
+ return env;
+}
+
+// static
+void JavaVMHelper::setJavaVM(JavaVM *vm) {
+ sJavaVM.store(vm);
+}
+
+} // namespace android
diff --git a/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h
new file mode 100644
index 0000000..35091b7
--- /dev/null
+++ b/media/libmediaplayer2/include/mediaplayer2/JavaVMHelper.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 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 JAVA_VM_HELPER_H_
+
+#define JAVA_VM_HELPER_H_
+
+#include "jni.h"
+
+#include <atomic>
+
+namespace android {
+
+struct JavaVMHelper {
+ static JNIEnv *getJNIEnv();
+ static void setJavaVM(JavaVM *vm);
+
+private:
+ // Once a valid JavaVM has been set, it should never be reset or changed.
+ // However, as it may be accessed from multiple threads, access needs to be
+ // synchronized.
+ static std::atomic<JavaVM *> sJavaVM;
+};
+
+} // namespace android
+
+#endif // JAVA_VM_HELPER_H_
diff --git a/media/libnblog/PerformanceAnalysis.cpp b/media/libnblog/PerformanceAnalysis.cpp
index f09e93d..22a30b9 100644
--- a/media/libnblog/PerformanceAnalysis.cpp
+++ b/media/libnblog/PerformanceAnalysis.cpp
@@ -254,7 +254,7 @@
// of PerformanceAnalysis
void PerformanceAnalysis::reportPerformance(String8 *body, int author, log_hash_t hash,
int maxHeight) {
- if (mHists.empty()) {
+ if (mHists.empty() || body == nullptr) {
return;
}
@@ -273,10 +273,13 @@
}
}
- // underscores and spaces length corresponds to maximum width of histogram
- static const int kLen = 200;
- std::string underscores(kLen, '_');
- std::string spaces(kLen, ' ');
+ static const int SIZE = 128;
+ char title[SIZE];
+ snprintf(title, sizeof(title), "\n%s %3.2f %s\n%s%d, %lld, %lld\n",
+ "Occurrences in", (elapsedMs / kMsPerSec), "seconds of audio:",
+ "Thread, hash, starting timestamp: ", author,
+ static_cast<long long>(hash), static_cast<long long>(startingTs));
+ static const char * const kLabel = "ms";
auto it = buckets.begin();
double maxDelta = it->first;
@@ -299,11 +302,7 @@
scalingFactor = (height + maxHeight) / maxHeight;
height /= scalingFactor;
}
- body->appendFormat("\n%*s %3.2f %s", leftPadding + 11,
- "Occurrences in", (elapsedMs / kMsPerSec), "seconds of audio:");
- body->appendFormat("\n%*s%d, %lld, %lld\n", leftPadding + 11,
- "Thread, hash, starting timestamp: ", author,
- static_cast<long long int>(hash), static_cast<long long int>(startingTs));
+ body->appendFormat("%s", title);
// write histogram label line with bucket values
body->appendFormat("\n%s", " ");
body->appendFormat("%*s", leftPadding, " ");
@@ -312,6 +311,11 @@
body->appendFormat("%*d", colWidth, x.second);
}
// write histogram ascii art
+ // underscores and spaces length corresponds to maximum width of histogram
+ static const int kLen = 200;
+ static const std::string underscores(kLen, '_');
+ static const std::string spaces(kLen, ' ');
+
body->appendFormat("\n%s", " ");
for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) {
const int value = 1 << row;
@@ -335,7 +339,7 @@
const int colWidth = numberWidth(x.first, leftPadding);
body->appendFormat("%*.*f", colWidth, 1, x.first);
}
- body->appendFormat("%.*s%s", bucketWidth, spaces.c_str(), "ms\n");
+ body->appendFormat("%.*s%s\n", bucketWidth, spaces.c_str(), kLabel);
// Now report glitches
body->appendFormat("\ntime elapsed between glitches and glitch timestamps:\n");
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 7f39d10..3526047 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -5556,6 +5556,11 @@
break;
}
+ case kWhatCheckIfStuck: {
+ ALOGV("No-op by default");
+ break;
+ }
+
default:
return false;
}
@@ -7873,6 +7878,18 @@
break;
}
+ case kWhatCheckIfStuck:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation == mCodec->mStateGeneration) {
+ mCodec->signalError(OMX_ErrorUndefined, TIMED_OUT);
+ }
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
@@ -7884,6 +7901,11 @@
void ACodec::OutputPortSettingsChangedState::stateEntered() {
ALOGV("[%s] Now handling output port settings change",
mCodec->mComponentName.c_str());
+
+ // If we haven't transitioned after 3 seconds, we're probably stuck.
+ sp<AMessage> msg = new AMessage(ACodec::kWhatCheckIfStuck, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
}
bool ACodec::OutputPortSettingsChangedState::onOMXFrameRendered(
@@ -8146,6 +8168,11 @@
ALOGV("[%s] Now Flushing", mCodec->mComponentName.c_str());
mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
+
+ // If we haven't transitioned after 3 seconds, we're probably stuck.
+ sp<AMessage> msg = new AMessage(ACodec::kWhatCheckIfStuck, mCodec);
+ msg->setInt32("generation", mCodec->mStateGeneration);
+ msg->post(3000000);
}
bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) {
@@ -8160,6 +8187,7 @@
msg->setInt32("generation", mCodec->mStateGeneration);
msg->post(3000000);
}
+ handled = true;
break;
}
@@ -8180,6 +8208,18 @@
break;
}
+ case kWhatCheckIfStuck:
+ {
+ int32_t generation = 0;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation == mCodec->mStateGeneration) {
+ mCodec->signalError(OMX_ErrorUndefined, TIMED_OUT);
+ }
+
+ handled = true;
+ break;
+ }
+
default:
handled = BaseState::onMessageReceived(msg);
break;
diff --git a/media/libstagefright/ACodecBufferChannel.cpp b/media/libstagefright/ACodecBufferChannel.cpp
index 710ae68..266a240 100644
--- a/media/libstagefright/ACodecBufferChannel.cpp
+++ b/media/libstagefright/ACodecBufferChannel.cpp
@@ -129,6 +129,7 @@
secureHandle = static_cast<native_handle_t *>(secureData->getDestinationPointer());
}
ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
if (mCrypto != NULL) {
ICrypto::DestinationBuffer destination;
if (secure) {
@@ -180,9 +181,16 @@
Status status = Status::OK;
hidl_string detailedError;
+ ScramblingControl sctrl = ScramblingControl::UNSCRAMBLED;
+
+ if (key != NULL) {
+ sctrl = (ScramblingControl)key[0];
+ // Adjust for the PES offset
+ codecDataOffset = key[2] | (key[3] << 8);
+ }
auto returnVoid = mDescrambler->descramble(
- key != NULL ? (ScramblingControl)key[0] : ScramblingControl::UNSCRAMBLED,
+ sctrl,
hidlSubSamples,
srcBuffer,
0,
@@ -202,6 +210,11 @@
return UNKNOWN_ERROR;
}
+ if (result < codecDataOffset) {
+ ALOGD("invalid codec data offset: %zd, result %zd", codecDataOffset, result);
+ return BAD_VALUE;
+ }
+
ALOGV("descramble succeeded, %zd bytes", result);
if (dstBuffer.type == BufferType::SHARED_MEMORY) {
@@ -210,7 +223,7 @@
}
}
- it->mCodecBuffer->setRange(0, result);
+ it->mCodecBuffer->setRange(codecDataOffset, result - codecDataOffset);
// Copy metadata from client to codec buffer.
it->mCodecBuffer->meta()->clear();
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 72eff94..5361159 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -860,7 +860,15 @@
}
//static
-sp<CodecBase> MediaCodec::GetCodecBase(const AString &name) {
+sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, const char *owner) {
+ if (owner) {
+ if (strncmp(owner, "default", 8) == 0) {
+ return new ACodec;
+ } else if (strncmp(owner, "codec2", 7) == 0) {
+ return CreateCCodec();
+ }
+ }
+
if (name.startsWithIgnoreCase("c2.")) {
return CreateCCodec();
} else if (name.startsWithIgnoreCase("omx.")) {
@@ -884,11 +892,6 @@
// we need to invest in an extra looper to free the main event
// queue.
- mCodec = GetCodecBase(name);
- if (mCodec == NULL) {
- return NAME_NOT_FOUND;
- }
-
mCodecInfo.clear();
bool secureCodec = false;
@@ -922,6 +925,11 @@
return NAME_NOT_FOUND;
}
+ mCodec = GetCodecBase(name, mCodecInfo->getOwnerName());
+ if (mCodec == NULL) {
+ return NAME_NOT_FOUND;
+ }
+
if (mIsVideo) {
// video codec needs dedicated looper
if (mCodecLooper == NULL) {
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index cf5e91e..ea778a4 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1577,6 +1577,7 @@
{ MEDIA_MIMETYPE_AUDIO_VORBIS, AUDIO_FORMAT_VORBIS },
{ MEDIA_MIMETYPE_AUDIO_OPUS, AUDIO_FORMAT_OPUS},
{ MEDIA_MIMETYPE_AUDIO_AC3, AUDIO_FORMAT_AC3},
+ { MEDIA_MIMETYPE_AUDIO_AC4, AUDIO_FORMAT_AC4},
{ MEDIA_MIMETYPE_AUDIO_FLAC, AUDIO_FORMAT_FLAC},
{ 0, AUDIO_FORMAT_INVALID }
};
diff --git a/media/libstagefright/foundation/MediaDefs.cpp b/media/libstagefright/foundation/MediaDefs.cpp
index 1695c75..a32cf08 100644
--- a/media/libstagefright/foundation/MediaDefs.cpp
+++ b/media/libstagefright/foundation/MediaDefs.cpp
@@ -50,6 +50,7 @@
const char *MEDIA_MIMETYPE_AUDIO_MSGSM = "audio/gsm";
const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3";
const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+const char *MEDIA_MIMETYPE_AUDIO_AC4 = "audio/ac4";
const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mp4";
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
index 25be89f..b165bcb 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/MediaDefs.h
@@ -52,6 +52,7 @@
extern const char *MEDIA_MIMETYPE_AUDIO_MSGSM;
extern const char *MEDIA_MIMETYPE_AUDIO_AC3;
extern const char *MEDIA_MIMETYPE_AUDIO_EAC3;
+extern const char *MEDIA_MIMETYPE_AUDIO_AC4;
extern const char *MEDIA_MIMETYPE_AUDIO_SCRAMBLED;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 52791b9..8ab33f7 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -234,7 +234,11 @@
if (mSelectedIndex >= 0 && i == (size_t)mSelectedIndex) {
const Media &item = mMediaItems.itemAt(i);
- *uri = item.makeURL(baseURL);
+ if (item.mURI.empty()) {
+ *uri = "";
+ } else {
+ *uri = item.makeURL(baseURL);
+ }
return true;
}
}
@@ -465,7 +469,7 @@
}
if ((*uri).empty()) {
- *uri = mItems.itemAt(index).mURI;
+ *uri = mItems.itemAt(index).makeURL(mBaseURI.c_str());
}
}
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index 97d15a7..1137cf1 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -137,6 +137,7 @@
kWhatOMXDied = 'OMXd',
kWhatReleaseCodecInstance = 'relC',
kWhatForceStateTransition = 'fstt',
+ kWhatCheckIfStuck = 'Cstk',
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index ad02004..7f6aae6 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -377,7 +377,7 @@
MediaCodec(const sp<ALooper> &looper, pid_t pid, uid_t uid);
- static sp<CodecBase> GetCodecBase(const AString &name);
+ static sp<CodecBase> GetCodecBase(const AString &name, const char *owner = nullptr);
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 5cc5093..cc31815 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -119,6 +119,7 @@
private:
struct StreamInfo {
unsigned mType;
+ unsigned mTypeExt;
unsigned mPID;
int32_t mCASystemId;
};
@@ -145,10 +146,12 @@
Stream(Program *program,
unsigned elementaryPID,
unsigned streamType,
+ unsigned streamTypeExt,
unsigned PCR_PID,
int32_t CA_system_ID);
unsigned type() const { return mStreamType; }
+ unsigned typeExt() const { return mStreamTypeExt; }
unsigned pid() const { return mElementaryPID; }
void setPID(unsigned pid) { mElementaryPID = pid; }
@@ -194,6 +197,7 @@
Program *mProgram;
unsigned mElementaryPID;
unsigned mStreamType;
+ unsigned mStreamTypeExt;
unsigned mPCR_PID;
int32_t mExpectedContinuityCounter;
@@ -447,7 +451,7 @@
if (descriptor_length > infoLength) {
break;
}
- if (descriptor_tag == 9 && descriptor_length >= 4) {
+ if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) {
found = true;
caDescriptor->mSystemID = br->getBits(16);
caDescriptor->mPID = br->getBits(16) & 0x1fff;
@@ -513,37 +517,65 @@
// infoBytesRemaining is the number of bytes that make up the
// variable length section of ES_infos. It does not include the
// final CRC.
- size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
+ int32_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
while (infoBytesRemaining >= 5) {
-
- unsigned streamType = br->getBits(8);
- ALOGV(" stream_type = 0x%02x", streamType);
-
+ StreamInfo info;
+ info.mType = br->getBits(8);
+ ALOGV(" stream_type = 0x%02x", info.mType);
MY_LOGV(" reserved = %u", br->getBits(3));
- unsigned elementaryPID = br->getBits(13);
- ALOGV(" elementary_PID = 0x%04x", elementaryPID);
+ info.mPID = br->getBits(13);
+ ALOGV(" elementary_PID = 0x%04x", info.mPID);
MY_LOGV(" reserved = %u", br->getBits(4));
unsigned ES_info_length = br->getBits(12);
ALOGV(" ES_info_length = %u", ES_info_length);
+ infoBytesRemaining -= 5 + ES_info_length;
CADescriptor streamCA;
- bool hasStreamCA = findCADescriptor(br, ES_info_length, &streamCA);
+ info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX;
+ bool hasStreamCA = false;
+ while (ES_info_length > 2 && infoBytesRemaining >= 0) {
+ unsigned descriptor_tag = br->getBits(8);
+ ALOGV(" tag = 0x%02x", descriptor_tag);
+
+ unsigned descriptor_length = br->getBits(8);
+ ALOGV(" len = %u", descriptor_length);
+
+ ES_info_length -= 2;
+ if (descriptor_length > ES_info_length) {
+ return ERROR_MALFORMED;
+ }
+ if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) {
+ hasStreamCA = true;
+ streamCA.mSystemID = br->getBits(16);
+ streamCA.mPID = br->getBits(16) & 0x1fff;
+ ES_info_length -= 4;
+ streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length - 4);
+ } else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA &&
+ descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) {
+ unsigned descTagExt = br->getBits(8);
+ ALOGV(" tag_ext = 0x%02x", descTagExt);
+ if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) {
+ info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4;
+ }
+ ES_info_length -= descriptor_length;
+ descriptor_length--;
+ br->skipBits(descriptor_length * 8);
+ } else {
+ ES_info_length -= descriptor_length;
+ br->skipBits(descriptor_length * 8);
+ }
+ }
if (hasStreamCA && !mParser->mCasManager->addStream(
- mProgramNumber, elementaryPID, streamCA)) {
+ mProgramNumber, info.mPID, streamCA)) {
return ERROR_MALFORMED;
}
- StreamInfo info;
- info.mType = streamType;
- info.mPID = elementaryPID;
info.mCASystemId = hasProgramCA ? programCA.mSystemID :
hasStreamCA ? streamCA.mSystemID : -1;
infos.push(info);
-
- infoBytesRemaining -= 5 + ES_info_length;
}
if (infoBytesRemaining != 0) {
@@ -602,7 +634,7 @@
if (index < 0) {
sp<Stream> stream = new Stream(
- this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
+ this, info.mPID, info.mType, info.mTypeExt, PCR_PID, info.mCASystemId);
if (mSampleAesKeyItem != NULL) {
stream->signalNewSampleAesKey(mSampleAesKeyItem);
@@ -720,11 +752,13 @@
Program *program,
unsigned elementaryPID,
unsigned streamType,
+ unsigned streamTypeExt,
unsigned PCR_PID,
int32_t CA_system_ID)
: mProgram(program),
mElementaryPID(elementaryPID),
mStreamType(streamType),
+ mStreamTypeExt(streamTypeExt),
mPCR_PID(PCR_PID),
mExpectedContinuityCounter(-1),
mPayloadStarted(false),
@@ -781,6 +815,12 @@
mode = ElementaryStreamQueue::AC3;
break;
+ case STREAMTYPE_PES_PRIVATE_DATA:
+ if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) {
+ mode = ElementaryStreamQueue::AC4;
+ }
+ break;
+
case STREAMTYPE_METADATA:
mode = ElementaryStreamQueue::METADATA;
break;
@@ -989,6 +1029,8 @@
case STREAMTYPE_AAC_ENCRYPTED:
case STREAMTYPE_AC3_ENCRYPTED:
return true;
+ case STREAMTYPE_PES_PRIVATE_DATA:
+ return mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4;
default:
return false;
@@ -1395,7 +1437,7 @@
// Perform the 1st pass descrambling if needed
if (descrambleBytes > 0) {
memcpy(mDescrambledBuffer->data(), mBuffer->data(), descrambleBytes);
- mDescrambledBuffer->setRange(0, descrambleBytes);
+ mDescrambledBuffer->setRange(0, mBuffer->size());
hidl_vec<SubSample> subSamples;
subSamples.resize(descrambleSubSamples);
@@ -1412,10 +1454,9 @@
}
}
- uint64_t srcOffset = 0, dstOffset = 0;
- // If scrambled at PES-level, PES header should be skipped
+ // If scrambled at PES-level, PES header is in the clear
if (pesScramblingControl != 0) {
- srcOffset = dstOffset = pesOffset;
+ subSamples[0].numBytesOfClearData = pesOffset;
subSamples[0].numBytesOfEncryptedData -= pesOffset;
}
@@ -1431,9 +1472,9 @@
(ScramblingControl) sctrl,
subSamples,
mDescramblerSrcBuffer,
- srcOffset,
+ 0 /*srcOffset*/,
dstBuffer,
- dstOffset,
+ 0 /*dstOffset*/,
[&status, &bytesWritten, &detailedError] (
Status _status, uint32_t _bytesWritten,
const hidl_string& _detailedError) {
@@ -1450,9 +1491,15 @@
ALOGV("[stream %d] descramble succeeded, %d bytes",
mElementaryPID, bytesWritten);
- memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes);
+
+ // Set descrambleBytes to the returned result.
+ // Note that this might be smaller than the total length of input data.
+ // (eg. when we're descrambling the PES header portion of a secure stream,
+ // the plugin might cut it off right after the PES header.)
+ descrambleBytes = bytesWritten;
}
+ sp<ABuffer> buffer;
if (mQueue->isScrambled()) {
// Queue subSample info for scrambled queue
sp<ABuffer> clearSizesBuffer = new ABuffer(mSubSamples.size() * 4);
@@ -1464,8 +1511,7 @@
for (auto it = mSubSamples.begin();
it != mSubSamples.end(); it++, i++) {
if ((it->transport_scrambling_mode == 0
- && pesScramblingControl == 0)
- || i < descrambleSubSamples) {
+ && pesScramblingControl == 0)) {
clearSizePtr[i] = it->subSampleSize;
encSizePtr[i] = 0;
} else {
@@ -1474,14 +1520,26 @@
}
isSync |= it->random_access_indicator;
}
+
+ // If scrambled at PES-level, PES header is in the clear
+ if (pesScramblingControl != 0) {
+ clearSizePtr[0] = pesOffset;
+ encSizePtr[0] -= pesOffset;
+ }
// Pass the original TS subsample size now. The PES header adjust
// will be applied when the scrambled AU is dequeued.
mQueue->appendScrambledData(
mBuffer->data(), mBuffer->size(), sctrl,
isSync, clearSizesBuffer, encSizesBuffer);
+
+ buffer = mDescrambledBuffer;
+ } else {
+ memcpy(mBuffer->data(), mDescrambledBuffer->data(), descrambleBytes);
+
+ buffer = mBuffer;
}
- ABitReader br(mBuffer->data(), mBuffer->size());
+ ABitReader br(buffer->data(), buffer->size());
status_t err = parsePES(&br, event);
if (err != OK) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 45ca06b..adb4fb2 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -142,6 +142,7 @@
STREAMTYPE_MPEG2_VIDEO = 0x02,
STREAMTYPE_MPEG1_AUDIO = 0x03,
STREAMTYPE_MPEG2_AUDIO = 0x04,
+ STREAMTYPE_PES_PRIVATE_DATA = 0x06,
STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f,
STREAMTYPE_MPEG4_VIDEO = 0x10,
STREAMTYPE_METADATA = 0x15,
@@ -160,6 +161,20 @@
STREAMTYPE_AC3_ENCRYPTED = 0xC1,
};
+ enum {
+ // From ISO/IEC 13818-1: 2007 (E), Table 2-29
+ DESCRIPTOR_CA = 0x09,
+
+ // DVB BlueBook A038 Table 12
+ DESCRIPTOR_DVB_EXTENSION = 0x7F,
+ };
+
+ // DVB BlueBook A038 Table 109
+ enum {
+ EXT_DESCRIPTOR_DVB_AC4 = 0x15,
+ EXT_DESCRIPTOR_DVB_RESERVED_MAX = 0x7F,
+ };
+
protected:
virtual ~ATSParser();
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index ece0692..9e154a3 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -226,6 +226,7 @@
int32_t cryptoMode;
if (buffer->meta()->findInt32("cryptoMode", &cryptoMode)) {
int32_t cryptoKey;
+ int32_t pesOffset;
sp<ABuffer> clearBytesBuffer, encBytesBuffer;
CHECK(buffer->meta()->findInt32("cryptoKey", &cryptoKey));
@@ -233,6 +234,8 @@
&& clearBytesBuffer != NULL);
CHECK(buffer->meta()->findBuffer("encBytes", &encBytesBuffer)
&& encBytesBuffer != NULL);
+ CHECK(buffer->meta()->findInt32("pesOffset", &pesOffset)
+ && (pesOffset >= 0) && (pesOffset < 65536));
bufmeta.setInt32(kKeyCryptoMode, cryptoMode);
@@ -240,6 +243,11 @@
bufmeta.setData(kKeyCryptoIV, 0, array, 16);
array[0] = (uint8_t) (cryptoKey & 0xff);
+ // array[1] contains PES header flag, which we don't use.
+ // array[2~3] contain the PES offset.
+ array[2] = (uint8_t) (pesOffset & 0xff);
+ array[3] = (uint8_t) ((pesOffset >> 8) & 0xff);
+
bufmeta.setData(kKeyCryptoKey, 0, array, 16);
bufmeta.setData(kKeyPlainSizes, 0,
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 0fa9fcb..34d0bcc 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -86,6 +86,21 @@
mCasSessionId = sessionId;
}
+static int32_t readVariableBits(ABitReader &bits, int32_t nbits) {
+ int32_t value = 0;
+ int32_t more_bits = 1;
+
+ while (more_bits) {
+ value += bits.getBits(nbits);
+ more_bits = bits.getBits(1);
+ if (!more_bits)
+ break;
+ value++;
+ value <<= nbits;
+ }
+ return value;
+}
+
// Parse AC3 header assuming the current ptr is start position of syncframe,
// update metadata only applicable, and return the payload size
static unsigned parseAC3SyncFrame(
@@ -199,6 +214,78 @@
return parseAC3SyncFrame(ptr, size, NULL) > 0;
}
+// Parse AC4 header assuming the current ptr is start position of syncframe
+// and update frameSize and metadata.
+static status_t parseAC4SyncFrame(
+ const uint8_t *ptr, size_t size, unsigned &frameSize, sp<MetaData> *metaData) {
+ // ETSI TS 103 190-2 V1.1.1 (2015-09), Annex C
+ // The sync_word can be either 0xAC40 or 0xAC41.
+ static const int kSyncWordAC40 = 0xAC40;
+ static const int kSyncWordAC41 = 0xAC41;
+
+ size_t headerSize = 0;
+ ABitReader bits(ptr, size);
+ int32_t syncWord = bits.getBits(16);
+ if ((syncWord != kSyncWordAC40) && (syncWord != kSyncWordAC41)) {
+ ALOGE("Invalid syncword in AC4 header");
+ return ERROR_MALFORMED;
+ }
+ headerSize += 2;
+
+ frameSize = bits.getBits(16);
+ headerSize += 2;
+ if (frameSize == 0xFFFF) {
+ frameSize = bits.getBits(24);
+ headerSize += 3;
+ }
+
+ if (frameSize == 0) {
+ ALOGE("Invalid frame size in AC4 header");
+ return ERROR_MALFORMED;
+ }
+ frameSize += headerSize;
+ // If the sync_word is 0xAC41, a crc_word is also transmitted.
+ if (syncWord == kSyncWordAC41) {
+ frameSize += 2; // crc_word
+ }
+ ALOGV("AC4 frameSize = %u", frameSize);
+
+ // ETSI TS 103 190-2 V1.1.1 6.2.1.1
+ uint32_t bitstreamVersion = bits.getBits(2);
+ if (bitstreamVersion == 3) {
+ bitstreamVersion += readVariableBits(bits, 2);
+ }
+
+ bits.skipBits(10); // Sequence Counter
+
+ uint32_t bWaitFrames = bits.getBits(1);
+ if (bWaitFrames) {
+ uint32_t waitFrames = bits.getBits(3);
+ if (waitFrames > 0) {
+ bits.skipBits(2); // br_code;
+ }
+ }
+
+ // ETSI TS 103 190 V1.1.1 Table 82
+ bool fsIndex = bits.getBits(1);
+ uint32_t samplingRate = fsIndex ? 48000 : 44100;
+
+ if (metaData != NULL) {
+ ALOGV("dequeueAccessUnitAC4 Setting mFormat");
+ (*metaData)->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AC4);
+ (*metaData)->setInt32(kKeyIsSyncFrame, 1);
+ // [FIXME] AC4 channel count is defined per presentation. Provide a default channel count
+ // as stereo for the entire stream.
+ (*metaData)->setInt32(kKeyChannelCount, 2);
+ (*metaData)->setInt32(kKeySampleRate, samplingRate);
+ }
+ return OK;
+}
+
+static status_t IsSeeminglyValidAC4Header(const uint8_t *ptr, size_t size, unsigned &frameSize) {
+ return parseAC4SyncFrame(ptr, size, frameSize, NULL);
+}
+
static bool IsSeeminglyValidADTSHeader(
const uint8_t *ptr, size_t size, size_t *frameLength) {
if (size < 7) {
@@ -416,6 +503,42 @@
break;
}
+ case AC4:
+ {
+ uint8_t *ptr = (uint8_t *)data;
+ unsigned frameSize = 0;
+ ssize_t startOffset = -1;
+
+ // A valid AC4 stream should have minimum of 7 bytes in its buffer.
+ // (Sync header 4 bytes + AC4 toc 3 bytes)
+ if (size < 7) {
+ return ERROR_MALFORMED;
+ }
+ for (size_t i = 0; i < size; ++i) {
+ if (IsSeeminglyValidAC4Header(&ptr[i], size - i, frameSize) == OK) {
+ startOffset = i;
+ break;
+ }
+ }
+
+ if (startOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (startOffset > 0) {
+ ALOGI("found something resembling an AC4 syncword at offset %zd",
+ startOffset);
+ }
+ if (frameSize != size - startOffset) {
+ ALOGV("AC4 frame size is %u bytes, while the buffer size is %zd bytes.",
+ frameSize, size - startOffset);
+ }
+
+ data = &ptr[startOffset];
+ size -= startOffset;
+ break;
+ }
+
case MPEG_AUDIO:
{
uint8_t *ptr = (uint8_t *)data;
@@ -568,25 +691,9 @@
return NULL;
}
- // skip the PES header, and copy the rest into scrambled access unit
+ // copy into scrambled access unit
sp<ABuffer> scrambledAccessUnit = ABuffer::CreateAsCopy(
- mScrambledBuffer->data() + pesOffset,
- scrambledLength - pesOffset);
-
- // fix up first sample size after skipping the PES header
- if (pesOffset > 0) {
- int32_t &firstClearSize = *(int32_t*)clearSizes->data();
- int32_t &firstEncSize = *(int32_t*)encSizes->data();
- // Cut away the PES header
- if (firstClearSize >= pesOffset) {
- // This is for TS-level scrambling, we descrambled the first
- // (or it was clear to begin with)
- firstClearSize -= pesOffset;
- } else if (firstEncSize >= pesOffset) {
- // This can only be PES-level scrambling
- firstEncSize -= pesOffset;
- }
- }
+ mScrambledBuffer->data(), scrambledLength);
scrambledAccessUnit->meta()->setInt64("timeUs", timeUs);
if (isSync) {
@@ -600,6 +707,7 @@
scrambledAccessUnit->meta()->setInt32("cryptoKey", keyId);
scrambledAccessUnit->meta()->setBuffer("clearBytes", clearSizes);
scrambledAccessUnit->meta()->setBuffer("encBytes", encSizes);
+ scrambledAccessUnit->meta()->setInt32("pesOffset", pesOffset);
memmove(mScrambledBuffer->data(),
mScrambledBuffer->data() + scrambledLength,
@@ -649,6 +757,8 @@
return dequeueAccessUnitAAC();
case AC3:
return dequeueAccessUnitAC3();
+ case AC4:
+ return dequeueAccessUnitAC4();
case MPEG_VIDEO:
return dequeueAccessUnitMPEGVideo();
case MPEG4_VIDEO:
@@ -730,6 +840,69 @@
return accessUnit;
}
+sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitAC4() {
+ unsigned syncStartPos = 0;
+ unsigned payloadSize = 0;
+ sp<MetaData> format = new MetaData;
+ ALOGV("dequeueAccessUnit_AC4[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
+
+ // A valid AC4 stream should have minimum of 7 bytes in its buffer.
+ // (Sync header 4 bytes + AC4 toc 3 bytes)
+ if (mBuffer->size() < 7) {
+ return NULL;
+ }
+
+ while (true) {
+ if (syncStartPos + 2 >= mBuffer->size()) {
+ return NULL;
+ }
+
+ status_t status = parseAC4SyncFrame(
+ mBuffer->data() + syncStartPos,
+ mBuffer->size() - syncStartPos,
+ payloadSize,
+ &format);
+ if (status == OK) {
+ break;
+ }
+
+ ALOGV("dequeueAccessUnit_AC4[%d]: syncStartPos %u payloadSize %u",
+ mAUIndex, syncStartPos, payloadSize);
+
+ ++syncStartPos;
+ }
+
+ if (mBuffer->size() < syncStartPos + payloadSize) {
+ ALOGV("Not enough buffer size for AC4");
+ return NULL;
+ }
+
+ if (mFormat == NULL) {
+ mFormat = format;
+ }
+
+ int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
+ if (timeUs < 0ll) {
+ ALOGE("negative timeUs");
+ return NULL;
+ }
+ mAUIndex++;
+
+ sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+ memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
+ accessUnit->meta()->setInt64("timeUs", timeUs);
+ accessUnit->meta()->setInt32("isSync", 1);
+
+ memmove(
+ mBuffer->data(),
+ mBuffer->data() + syncStartPos + payloadSize,
+ mBuffer->size() - syncStartPos - payloadSize);
+
+ mBuffer->setRange(0, mBuffer->size() - syncStartPos - payloadSize);
+ return accessUnit;
+}
+
sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitPCMAudio() {
if (mBuffer->size() < 4) {
return NULL;
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index ffcb502..399214a 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -38,6 +38,7 @@
H264,
AAC,
AC3,
+ AC4,
MPEG_AUDIO,
MPEG_VIDEO,
MPEG4_VIDEO,
@@ -116,6 +117,7 @@
sp<ABuffer> dequeueAccessUnitH264();
sp<ABuffer> dequeueAccessUnitAAC();
sp<ABuffer> dequeueAccessUnitAC3();
+ sp<ABuffer> dequeueAccessUnitAC4();
sp<ABuffer> dequeueAccessUnitMPEGAudio();
sp<ABuffer> dequeueAccessUnitMPEGVideo();
sp<ABuffer> dequeueAccessUnitMPEG4Video();
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 7d2c2dd..32113c2 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1085,7 +1085,8 @@
}
case OMXBuffer::kBufferTypeANWBuffer: {
- if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer) {
+ if (mPortMode[portIndex] != IOMX::kPortModePresetANWBuffer
+ && mPortMode[portIndex] != IOMX::kPortModeDynamicANWBuffer) {
break;
}
return useGraphicBuffer_l(portIndex, omxBuffer.mGraphicBuffer, buffer);
@@ -1609,12 +1610,15 @@
}
BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+ // Invalidate buffers in the client side first before calling OMX_FreeBuffer.
+ // If not, pending events in the client side might access the buffers after free.
+ invalidateBufferID(buffer);
+
OMX_ERRORTYPE err = OMX_FreeBuffer(mHandle, portIndex, header);
CLOG_IF_ERROR(freeBuffer, err, "%s:%u %#x", portString(portIndex), portIndex, buffer);
delete buffer_meta;
buffer_meta = NULL;
- invalidateBufferID(buffer);
return StatusFromOMXError(err);
}
diff --git a/media/mtp/PosixAsyncIO.cpp b/media/mtp/PosixAsyncIO.cpp
index e67c568..72c07cc 100644
--- a/media/mtp/PosixAsyncIO.cpp
+++ b/media/mtp/PosixAsyncIO.cpp
@@ -15,42 +15,109 @@
*/
#include <android-base/logging.h>
-#include <condition_variable>
#include <memory>
-#include <mutex>
+#include <pthread.h>
#include <queue>
+#include <thread>
#include <unistd.h>
#include "PosixAsyncIO.h"
namespace {
-void read_func(struct aiocb *aiocbp) {
- aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
- aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
- if (aiocbp->ret == -1) aiocbp->error = errno;
+std::thread gWorkerThread;
+std::deque<struct aiocb*> gWorkQueue;
+bool gSuspended = true;
+int gAiocbRefcount = 0;
+std::mutex gLock;
+std::condition_variable gWait;
+
+void work_func(void *) {
+ pthread_setname_np(pthread_self(), "AsyncIO work");
+ while (true) {
+ struct aiocb *aiocbp;
+ {
+ std::unique_lock<std::mutex> lk(gLock);
+ gWait.wait(lk, []{return gWorkQueue.size() > 0 || gSuspended;});
+ if (gSuspended)
+ return;
+ aiocbp = gWorkQueue.back();
+ gWorkQueue.pop_back();
+ }
+ CHECK(aiocbp->queued);
+ int ret;
+ if (aiocbp->read) {
+ ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
+ aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+ } else {
+ ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+ aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+ }
+ {
+ std::unique_lock<std::mutex> lk(aiocbp->lock);
+ aiocbp->ret = ret;
+ if (aiocbp->ret == -1) {
+ aiocbp->error = errno;
+ }
+ aiocbp->queued = false;
+ }
+ aiocbp->cv.notify_all();
+ }
}
-void write_func(struct aiocb *aiocbp) {
- aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
- aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
- if (aiocbp->ret == -1) aiocbp->error = errno;
+int aio_add(struct aiocb *aiocbp) {
+ CHECK(!aiocbp->queued);
+ aiocbp->queued = true;
+ {
+ std::unique_lock<std::mutex> lk(gLock);
+ gWorkQueue.push_front(aiocbp);
+ }
+ gWait.notify_one();
+ return 0;
}
} // end anonymous namespace
+aiocb::aiocb() {
+ this->ret = 0;
+ this->queued = false;
+ {
+ std::unique_lock<std::mutex> lk(gLock);
+ if (gAiocbRefcount == 0) {
+ CHECK(gWorkQueue.size() == 0);
+ CHECK(gSuspended);
+ gSuspended = false;
+ gWorkerThread = std::thread(work_func, nullptr);
+ }
+ gAiocbRefcount++;
+ }
+}
+
aiocb::~aiocb() {
- CHECK(!thread.joinable());
+ CHECK(!this->queued);
+ {
+ std::unique_lock<std::mutex> lk(gLock);
+ CHECK(!gSuspended);
+ if (gAiocbRefcount == 1) {
+ CHECK(gWorkQueue.size() == 0);
+ gSuspended = true;
+ lk.unlock();
+ gWait.notify_one();
+ gWorkerThread.join();
+ lk.lock();
+ }
+ gAiocbRefcount--;
+ }
}
int aio_read(struct aiocb *aiocbp) {
- aiocbp->thread = std::thread(read_func, aiocbp);
- return 0;
+ aiocbp->read = true;
+ return aio_add(aiocbp);
}
int aio_write(struct aiocb *aiocbp) {
- aiocbp->thread = std::thread(write_func, aiocbp);
- return 0;
+ aiocbp->read = false;
+ return aio_add(aiocbp);
}
int aio_error(const struct aiocb *aiocbp) {
@@ -64,7 +131,10 @@
int aio_suspend(struct aiocb *aiocbp[], int n,
const struct timespec *) {
for (int i = 0; i < n; i++) {
- aiocbp[i]->thread.join();
+ {
+ std::unique_lock<std::mutex> lk(aiocbp[i]->lock);
+ aiocbp[i]->cv.wait(lk, [aiocbp, i]{return !aiocbp[i]->queued;});
+ }
}
return 0;
}
diff --git a/media/mtp/PosixAsyncIO.h b/media/mtp/PosixAsyncIO.h
index 590aaef..2bb5735 100644
--- a/media/mtp/PosixAsyncIO.h
+++ b/media/mtp/PosixAsyncIO.h
@@ -17,10 +17,11 @@
#ifndef _POSIXASYNCIO_H
#define _POSIXASYNCIO_H
+#include <condition_variable>
+#include <mutex>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <time.h>
-#include <thread>
#include <unistd.h>
/**
@@ -35,10 +36,15 @@
size_t aio_nbytes;
// Used internally
- std::thread thread;
+ bool read;
+ bool queued;
ssize_t ret;
int error;
+ std::mutex lock;
+ std::condition_variable cv;
+
+ aiocb();
~aiocb();
};
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 6d10f1c..5597488 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -309,6 +309,7 @@
}
String8 defaultUrl;
DrmPlugin::KeyRequestType keyRequestType;
+ mObj->mKeyRequest.clear();
status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl,
&keyRequestType);
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 581c7bf..8f37f7b 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -119,7 +119,7 @@
extern const char* AMEDIAFORMAT_KEY_FRAME_RATE __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_GRID_COLUMNS __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_GRID_ROWS __INTRODUCED_IN(28);
-extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28);;
+extern const char* AMEDIAFORMAT_KEY_HDR_STATIC_INFO __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_HEIGHT __INTRODUCED_IN(21);
extern const char* AMEDIAFORMAT_KEY_INTRA_REFRESH_PERIOD __INTRODUCED_IN(28);
extern const char* AMEDIAFORMAT_KEY_IS_ADTS __INTRODUCED_IN(21);
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 25511bf..d828d6a 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -50,8 +50,8 @@
AMEDIAFORMAT_KEY_CSD_1; # var introduced=28
AMEDIAFORMAT_KEY_CSD_2; # var introduced=28
AMEDIAFORMAT_KEY_DISPLAY_CROP; # var introduced=28
- AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; introduced=28
- AMEDIAFORMAT_KEY_DISPLAY_WIDTH; introduced=28
+ AMEDIAFORMAT_KEY_DISPLAY_HEIGHT; # var introduced=28
+ AMEDIAFORMAT_KEY_DISPLAY_WIDTH; # var introduced=28
AMEDIAFORMAT_KEY_DURATION; # var introduced=21
AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL; # var introduced=21
AMEDIAFORMAT_KEY_FRAME_RATE; # var introduced=21
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 53a4ce9..79501cd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -306,7 +306,7 @@
*sessionId = actualSessionId;
} else {
if (direction == MmapStreamInterface::DIRECTION_OUTPUT) {
- AudioSystem::releaseOutput(io, streamType, actualSessionId);
+ AudioSystem::releaseOutput(portId);
} else {
AudioSystem::releaseInput(portId);
}
@@ -777,7 +777,7 @@
Exit:
if (lStatus != NO_ERROR && output.outputId != AUDIO_IO_HANDLE_NONE) {
- AudioSystem::releaseOutput(output.outputId, streamType, sessionId);
+ AudioSystem::releaseOutput(portId);
}
*status = lStatus;
return trackHandle;
@@ -1144,6 +1144,23 @@
}
}
+// forwardAudioHwSyncToDownstreamPatches_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::forwardParametersToDownstreamPatches_l(
+ audio_io_handle_t upStream, const String8& keyValuePairs,
+ std::function<bool(const sp<PlaybackThread>&)> useThread)
+{
+ std::vector<PatchPanel::SoftwarePatch> swPatches;
+ if (mPatchPanel.getDownstreamSoftwarePatches(upStream, &swPatches) != OK) return;
+ ALOGV_IF(!swPatches.empty(), "%s found %zu downstream patches for stream ID %d",
+ __func__, swPatches.size(), upStream);
+ for (const auto& swPatch : swPatches) {
+ sp<PlaybackThread> downStream = checkPlaybackThread_l(swPatch.getPlaybackThreadHandle());
+ if (downStream != NULL && (useThread == nullptr || useThread(downStream))) {
+ downStream->setParameters(keyValuePairs);
+ }
+ }
+}
+
// Filter reserved keys from setParameters() before forwarding to audio HAL or acting upon.
// Some keys are used for audio routing and audio path configuration and should be reserved for use
// by audio policy and audio flinger for functional, privacy and security reasons.
@@ -1259,7 +1276,9 @@
}
}
if (thread != 0) {
- return thread->setParameters(filteredKeyValuePairs);
+ status_t result = thread->setParameters(filteredKeyValuePairs);
+ forwardParametersToDownstreamPatches_l(thread->id(), filteredKeyValuePairs);
+ return result;
}
return BAD_VALUE;
}
@@ -1817,6 +1836,10 @@
mHardwareStatus = AUDIO_HW_IDLE;
}
+ if (strcmp(name, AUDIO_HAL_SERVICE_NAME_MSD) == 0) {
+ // An MSD module is inserted before hardware modules in order to mix encoded streams.
+ flags = static_cast<AudioHwDevice::Flags>(flags | AudioHwDevice::AHWD_IS_INSERT);
+ }
audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
@@ -1960,7 +1983,10 @@
if (sessions & ThreadBase::TRACK_SESSION) {
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyStreamHwAvSync), value);
- thread->setParameters(param.toString());
+ String8 keyValuePairs = param.toString();
+ thread->setParameters(keyValuePairs);
+ forwardParametersToDownstreamPatches_l(thread->id(), keyValuePairs,
+ [](const sp<PlaybackThread>& thread) { return thread->usesHwAvSync(); });
break;
}
}
@@ -2006,7 +2032,10 @@
ALOGV("setAudioHwSyncForSession_l found ID %d for session %d", syncId, sessionId);
AudioParameter param = AudioParameter();
param.addInt(String8(AudioParameter::keyStreamHwAvSync), syncId);
- thread->setParameters(param.toString());
+ String8 keyValuePairs = param.toString();
+ thread->setParameters(keyValuePairs);
+ forwardParametersToDownstreamPatches_l(thread->id(), keyValuePairs,
+ [](const sp<PlaybackThread>& thread) { return thread->usesHwAvSync(); });
}
}
@@ -2096,6 +2125,7 @@
*output, thread.get());
}
mPlaybackThreads.add(*output, thread);
+ mPatchPanel.notifyStreamOpened(outHwDev, *output);
return thread;
}
}
@@ -2231,6 +2261,7 @@
const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
ioDesc->mIoHandle = output;
ioConfigChanged(AUDIO_OUTPUT_CLOSED, ioDesc);
+ mPatchPanel.notifyStreamClosed(output);
}
// The thread entity (active unit of execution) is no longer running here,
// but the ThreadBase container still exists.
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 0276cad..4a780ce 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -62,6 +62,7 @@
#include <media/LinearMap.h>
#include <media/VolumeShaper.h>
+#include <audio_utils/clock.h>
#include <audio_utils/SimpleLog.h>
#include <audio_utils/TimestampVerifier.h>
@@ -677,6 +678,9 @@
bool updateOrphanEffectChains(const sp<EffectModule>& effect);
void broacastParametersToRecordThreads_l(const String8& keyValuePairs);
+ void forwardParametersToDownstreamPatches_l(
+ audio_io_handle_t upStream, const String8& keyValuePairs,
+ std::function<bool(const sp<PlaybackThread>&)> useThread = nullptr);
// AudioStreamIn is immutable, so their fields are const.
// For emphasis, we could also make all pointers to them be "const *",
diff --git a/services/audioflinger/AudioHwDevice.h b/services/audioflinger/AudioHwDevice.h
index eb826c6..d4299b0 100644
--- a/services/audioflinger/AudioHwDevice.h
+++ b/services/audioflinger/AudioHwDevice.h
@@ -35,6 +35,9 @@
enum Flags {
AHWD_CAN_SET_MASTER_VOLUME = 0x1,
AHWD_CAN_SET_MASTER_MUTE = 0x2,
+ // Means that this isn't a terminal module, and software patches
+ // are used to transport audio data further.
+ AHWD_IS_INSERT = 0x4,
};
AudioHwDevice(audio_module_handle_t handle,
@@ -55,6 +58,10 @@
return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE));
}
+ bool isInsert() const {
+ return (0 != (mFlags & AHWD_IS_INSERT));
+ }
+
audio_module_handle_t handle() const { return mHandle; }
const char *moduleName() const { return mModuleName; }
sp<DeviceHalInterface> hwDevice() const { return mHwDevice; }
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 42a5a90..f044fb7 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -83,6 +83,16 @@
return mPatchPanel.listAudioPatches(num_patches, patches);
}
+status_t AudioFlinger::PatchPanel::SoftwarePatch::getLatencyMs_l(double *latencyMs) const
+{
+ const auto& iter = mPatchPanel.mPatches.find(mPatchHandle);
+ if (iter != mPatchPanel.mPatches.end()) {
+ return iter->second.getLatencyMs(latencyMs);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
/* List connected audio ports and their attributes */
status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
struct audio_port *ports __unused)
@@ -110,9 +120,7 @@
status_t status = NO_ERROR;
audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE;
- if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
- (patch->num_sinks == 0 && patch->num_sources != 2) ||
- patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ if (!audio_patch_is_valid(patch) || (patch->num_sinks == 0 && patch->num_sources != 2)) {
return BAD_VALUE;
}
// limit number of sources to 1 for now or 2 sources for special cross hw module case.
@@ -161,21 +169,21 @@
}
}
mPatches.erase(iter);
+ removeSoftwarePatchFromInsertedModules(*handle);
}
}
Patch newPatch{*patch};
+ audio_module_handle_t insertedModule = AUDIO_MODULE_HANDLE_NONE;
switch (patch->sources[0].type) {
case AUDIO_PORT_TYPE_DEVICE: {
audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
- ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
- if (index < 0) {
- ALOGW("%s() bad src hw module %d", __func__, srcModule);
+ AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(srcModule);
+ if (!audioHwDevice) {
status = BAD_VALUE;
goto exit;
}
- AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index);
for (unsigned int i = 0; i < patch->num_sinks; i++) {
// support only one sink if connection to a mix or across HW modules
if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
@@ -227,9 +235,19 @@
audio_devices_t device = patch->sinks[0].ext.device.type;
String8 address = String8(patch->sinks[0].ext.device.address);
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- audio_output_flags_t flags =
- patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
- patch->sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+ if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ config.sample_rate = patch->sinks[0].sample_rate;
+ }
+ if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ config.channel_mask = patch->sinks[0].channel_mask;
+ }
+ if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ config.format = patch->sinks[0].format;
+ }
+ if (patch->sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS) {
+ flags = patch->sinks[0].flags.output;
+ }
sp<ThreadBase> thread = mAudioFlinger.openOutput_l(
patch->sinks[0].ext.device.hw_module,
&output,
@@ -287,6 +305,9 @@
if (status != NO_ERROR) {
goto exit;
}
+ if (audioHwDevice->isInsert()) {
+ insertedModule = audioHwDevice->handle();
+ }
} else {
if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<ThreadBase> thread = mAudioFlinger.checkRecordThread_l(
@@ -366,6 +387,9 @@
*handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
newPatch.mHalHandle = halHandle;
mPatches.insert(std::make_pair(*handle, std::move(newPatch)));
+ if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
+ addSoftwarePatchToInsertedModules(insertedModule, *handle);
+ }
ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle);
} else {
newPatch.clearConnections(this);
@@ -447,12 +471,16 @@
audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
-
+ audio_stream_type_t streamType = AUDIO_STREAM_PATCH;
+ if (mAudioPatch.num_sources == 2 && mAudioPatch.sources[1].type == AUDIO_PORT_TYPE_MIX) {
+ // "reuse one existing output mix" case
+ streamType = mAudioPatch.sources[1].ext.mix.usecase.stream;
+ }
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
mPlayback.thread().get(),
- mAudioPatch.sources[1].ext.mix.usecase.stream,
+ streamType,
sampleRate,
outChannelMask,
format,
@@ -513,21 +541,17 @@
return OK;
}
-String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle)
+String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
{
- String8 result;
-
// TODO: Consider table dump form for patches, just like tracks.
- result.appendFormat("Patch %d: thread %p => thread %p",
- myHandle, mRecord.thread().get(), mPlayback.thread().get());
+ String8 result = String8::format("Patch %d: thread %p => thread %p",
+ myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get());
// add latency if it exists
double latencyMs;
if (getLatencyMs(&latencyMs) == OK) {
result.appendFormat(" latency: %.2lf", latencyMs);
}
-
- result.append("\n");
return result;
}
@@ -598,6 +622,7 @@
}
mPatches.erase(iter);
+ removeSoftwarePatchFromInsertedModules(handle);
return status;
}
@@ -609,35 +634,114 @@
return NO_ERROR;
}
-sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
+status_t AudioFlinger::PatchPanel::getDownstreamSoftwarePatches(
+ audio_io_handle_t stream,
+ std::vector<AudioFlinger::PatchPanel::SoftwarePatch> *patches) const
+{
+ for (const auto& module : mInsertedModules) {
+ if (module.second.streams.count(stream)) {
+ for (const auto& patchHandle : module.second.sw_patches) {
+ const auto& patch_iter = mPatches.find(patchHandle);
+ if (patch_iter != mPatches.end()) {
+ const Patch &patch = patch_iter->second;
+ patches->emplace_back(*this, patchHandle,
+ patch.mPlayback.const_thread()->id(),
+ patch.mRecord.const_thread()->id());
+ } else {
+ ALOGE("Stale patch handle in the cache: %d", patchHandle);
+ }
+ }
+ return OK;
+ }
+ }
+ // The stream is not associated with any of inserted modules.
+ return BAD_VALUE;
+}
+
+void AudioFlinger::PatchPanel::notifyStreamOpened(
+ AudioHwDevice *audioHwDevice, audio_io_handle_t stream)
+{
+ if (audioHwDevice->isInsert()) {
+ mInsertedModules[audioHwDevice->handle()].streams.insert(stream);
+ }
+}
+
+void AudioFlinger::PatchPanel::notifyStreamClosed(audio_io_handle_t stream)
+{
+ for (auto& module : mInsertedModules) {
+ module.second.streams.erase(stream);
+ }
+}
+
+AudioHwDevice* AudioFlinger::PatchPanel::findAudioHwDeviceByModule(audio_module_handle_t module)
{
if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr;
ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module);
if (index < 0) {
+ ALOGW("%s() bad hw module %d", __func__, module);
return nullptr;
}
- return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
+ return mAudioFlinger.mAudioHwDevs.valueAt(index);
}
-void AudioFlinger::PatchPanel::dump(int fd)
+sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
{
+ AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(module);
+ return audioHwDevice ? audioHwDevice->hwDevice() : nullptr;
+}
+
+void AudioFlinger::PatchPanel::addSoftwarePatchToInsertedModules(
+ audio_module_handle_t module, audio_patch_handle_t handle)
+{
+ mInsertedModules[module].sw_patches.insert(handle);
+}
+
+void AudioFlinger::PatchPanel::removeSoftwarePatchFromInsertedModules(
+ audio_patch_handle_t handle)
+{
+ for (auto& module : mInsertedModules) {
+ module.second.sw_patches.erase(handle);
+ }
+}
+
+void AudioFlinger::PatchPanel::dump(int fd) const
+{
+ String8 patchPanelDump;
+ const char *indent = " ";
+
// Only dump software patches.
bool headerPrinted = false;
- for (auto& iter : mPatches) {
+ for (const auto& iter : mPatches) {
if (iter.second.isSoftware()) {
if (!headerPrinted) {
- String8 header("\nSoftware patches:\n");
- write(fd, header.string(), header.size());
+ patchPanelDump += "\nSoftware patches:\n";
headerPrinted = true;
}
- String8 patchDump(" ");
- patchDump.append(iter.second.dump(iter.first));
- write(fd, patchDump.string(), patchDump.size());
+ patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
}
}
- if (headerPrinted) {
- String8 trailing("\n");
- write(fd, trailing.string(), trailing.size());
+
+ headerPrinted = false;
+ for (const auto& module : mInsertedModules) {
+ if (!module.second.streams.empty() || !module.second.sw_patches.empty()) {
+ if (!headerPrinted) {
+ patchPanelDump += "\nTracked inserted modules:\n";
+ headerPrinted = true;
+ }
+ String8 moduleDump = String8::format("Module %d: I/O handles: ", module.first);
+ for (const auto& stream : module.second.streams) {
+ moduleDump.appendFormat("%d ", stream);
+ }
+ moduleDump.append("; SW Patches: ");
+ for (const auto& patch : module.second.sw_patches) {
+ moduleDump.appendFormat("%d ", patch);
+ }
+ patchPanelDump.appendFormat("%s%s\n", indent, moduleDump.string());
+ }
+ }
+
+ if (!patchPanelDump.isEmpty()) {
+ write(fd, patchPanelDump.string(), patchPanelDump.size());
}
}
diff --git a/services/audioflinger/PatchPanel.h b/services/audioflinger/PatchPanel.h
index 5d6bf00..269a398 100644
--- a/services/audioflinger/PatchPanel.h
+++ b/services/audioflinger/PatchPanel.h
@@ -19,9 +19,31 @@
#error This header file should only be included from AudioFlinger.h
#endif
+
// PatchPanel is concealed within AudioFlinger, their lifetimes are the same.
class PatchPanel {
public:
+ class SoftwarePatch {
+ public:
+ SoftwarePatch(const PatchPanel &patchPanel, audio_patch_handle_t patchHandle,
+ audio_io_handle_t playbackThreadHandle, audio_io_handle_t recordThreadHandle)
+ : mPatchPanel(patchPanel), mPatchHandle(patchHandle),
+ mPlaybackThreadHandle(playbackThreadHandle),
+ mRecordThreadHandle(recordThreadHandle) {}
+ SoftwarePatch(const SoftwarePatch&) = default;
+ SoftwarePatch& operator=(const SoftwarePatch&) = default;
+
+ // Must be called under AudioFlinger::mLock
+ status_t getLatencyMs_l(double *latencyMs) const;
+ audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; };
+ audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; };
+ private:
+ const PatchPanel &mPatchPanel;
+ const audio_patch_handle_t mPatchHandle;
+ const audio_io_handle_t mPlaybackThreadHandle;
+ const audio_io_handle_t mRecordThreadHandle;
+ };
+
explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {}
/* List connected audio ports and their attributes */
@@ -42,7 +64,16 @@
status_t listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches);
- void dump(int fd);
+ // Retrieves all currently estrablished software patches for a stream
+ // opened on an intermediate module.
+ status_t getDownstreamSoftwarePatches(audio_io_handle_t stream,
+ std::vector<SoftwarePatch> *patches) const;
+
+ // Notifies patch panel about all opened and closed streams.
+ void notifyStreamOpened(AudioHwDevice *audioHwDevice, audio_io_handle_t stream);
+ void notifyStreamClosed(audio_io_handle_t stream);
+
+ void dump(int fd) const;
private:
template<typename ThreadType, typename TrackType>
@@ -65,6 +96,7 @@
audio_patch_handle_t handle() const { return mHandle; }
sp<ThreadType> thread() { return mThread; }
sp<TrackType> track() { return mTrack; }
+ sp<const ThreadType> const_thread() const { return mThread; }
sp<const TrackType> const_track() const { return mTrack; }
void closeConnections(PatchPanel *panel) {
@@ -122,7 +154,7 @@
// returns the latency of the patch (from record to playback).
status_t getLatencyMs(double *latencyMs) const;
- String8 dump(audio_patch_handle_t myHandle);
+ String8 dump(audio_patch_handle_t myHandle) const;
// Note that audio_patch::id is only unique within a HAL module
struct audio_patch mAudioPatch;
@@ -138,8 +170,38 @@
Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
};
+ AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
+ void addSoftwarePatchToInsertedModules(
+ audio_module_handle_t module, audio_patch_handle_t handle);
+ void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);
AudioFlinger &mAudioFlinger;
std::map<audio_patch_handle_t, Patch> mPatches;
+
+ // This map allows going from a thread to "downstream" software patches
+ // when a processing module inserted in between. Example:
+ //
+ // from map value.streams map key
+ // [Mixer thread] --> [Virtual output device] --> [Processing module] ---\
+ // [Harware module] <-- [Physical output device] <-- [S/W Patch] <--/
+ // from map value.sw_patches
+ //
+ // This allows the mixer thread to look up the threads of the software patch
+ // for propagating timing info, parameters, etc.
+ //
+ // The current assumptions are:
+ // 1) The processing module acts as a mixer with several outputs which
+ // represent differently downmixed and / or encoded versions of the same
+ // mixed stream. There is no 1:1 correspondence between the input streams
+ // and the software patches, but rather a N:N correspondence between
+ // a group of streams and a group of patches.
+ // 2) There are only a couple of inserted processing modules in the system,
+ // so when looking for a stream or patch handle we can iterate over
+ // all modules.
+ struct ModuleConnections {
+ std::set<audio_io_handle_t> streams;
+ std::set<audio_patch_handle_t> sw_patches;
+ };
+ std::map<audio_module_handle_t, ModuleConnections> mInsertedModules;
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 70af5c6..e3b83f9 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -857,7 +857,8 @@
if (mType == RECORD
|| mType == MIXER
|| mType == DUPLICATING
- || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) {
+ || mType == DIRECT
+ || mType == OFFLOAD) {
dprintf(fd, " Timestamp stats: %s\n", mTimestampVerifier.toString().c_str());
}
@@ -2323,15 +2324,13 @@
if (track->isExternalTrack()) {
TrackBase::track_state state = track->mState;
mLock.unlock();
- status = AudioSystem::startOutput(mId, track->streamType(),
- track->sessionId());
+ status = AudioSystem::startOutput(track->portId());
mLock.lock();
// abort track was stopped/paused while we released the lock
if (state != track->mState) {
if (status == NO_ERROR) {
mLock.unlock();
- AudioSystem::stopOutput(mId, track->streamType(),
- track->sessionId());
+ AudioSystem::stopOutput(track->portId());
mLock.lock();
}
return INVALID_OPERATION;
@@ -2482,6 +2481,11 @@
Mutex::Autolock _l(mLock);
// reject out of sequence requests
if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+ // Register discontinuity when HW drain is completed because that can cause
+ // the timestamp frame position to reset to 0 for direct and offload threads.
+ // (Out of sequence requests are ignored, since the discontinuity would be handled
+ // elsewhere, e.g. in flush).
+ mTimestampVerifier.discontinuity();
mDrainSequence &= ~1;
mWaitWorkCV.signal();
}
@@ -2806,15 +2810,13 @@
for (size_t i = 0 ; i < count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
if (track->isExternalTrack()) {
- AudioSystem::stopOutput(mId, track->streamType(),
- track->sessionId());
+ AudioSystem::stopOutput(track->portId());
#ifdef ADD_BATTERY_DATA
// to track the speaker usage
addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
#endif
if (track->isTerminated()) {
- AudioSystem::releaseOutput(mId, track->streamType(),
- track->sessionId());
+ AudioSystem::releaseOutput(track->portId());
}
}
}
@@ -3190,6 +3192,15 @@
checkSilentMode_l();
+ // DIRECT and OFFLOAD threads should reset frame count to zero on stop/flush
+ // TODO: add confirmation checks:
+ // 1) DIRECT threads and linear PCM format really resets to 0?
+ // 2) Is frame count really valid if not linear pcm?
+ // 3) Are all 64 bits of position returned, not just lowest 32 bits?
+ if (mType == OFFLOAD || mType == DIRECT) {
+ mTimestampVerifier.setDiscontinuityMode(mTimestampVerifier.DISCONTINUITY_MODE_ZERO);
+ }
+
while (!exitPending())
{
// Log merge requests are performed during AudioFlinger binder transactions, but
@@ -3216,7 +3227,8 @@
// Collect timestamp statistics for the Playback Thread types that support it.
if (mType == MIXER
|| mType == DUPLICATING
- || (mType == DIRECT && audio_is_linear_pcm(mHALFormat))) { // no indentation
+ || mType == DIRECT
+ || mType == OFFLOAD) { // no indentation
// Gather the framesReleased counters for all active tracks,
// and associate with the sink frames written out. We need
// this to convert the sink timestamp to the track timestamp.
@@ -5622,6 +5634,7 @@
mOutput->flush();
mHwPaused = false;
mFlushPending = false;
+ mTimestampVerifier.discontinuity(); // DIRECT and OFFLOADED flush resets frame count.
}
int64_t AudioFlinger::DirectOutputThread::computeWaitTimeNs_l() const {
@@ -5956,6 +5969,14 @@
track->presentationComplete(framesWritten, audioHALFrames);
track->reset();
tracksToRemove->add(track);
+ // DIRECT and OFFLOADED stop resets frame counts.
+ if (!mUseAsyncWrite) {
+ // If we don't get explicit drain notification we must
+ // register discontinuity regardless of whether this is
+ // the previous (!last) or the upcoming (last) track
+ // to avoid skipping the discontinuity.
+ mTimestampVerifier.discontinuity();
+ }
}
} else {
// No buffers for this track. Give it a few chances to
@@ -7040,6 +7061,12 @@
goto Exit;
}
+ if (!audio_is_linear_pcm(mFormat) && (*flags & AUDIO_INPUT_FLAG_DIRECT) == 0) {
+ ALOGE("createRecordTrack_l() on an encoded stream requires AUDIO_INPUT_FLAG_DIRECT");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
if (*pSampleRate == 0) {
*pSampleRate = mSampleRate;
}
@@ -7754,10 +7781,15 @@
{
status_t result = mInput->stream->getAudioProperties(&mSampleRate, &mChannelMask, &mHALFormat);
LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving audio properties from HAL: %d", result);
- mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
- LOG_ALWAYS_FATAL_IF(mChannelCount > FCC_8, "HAL channel count %d > %d", mChannelCount, FCC_8);
mFormat = mHALFormat;
- LOG_ALWAYS_FATAL_IF(!audio_is_linear_pcm(mFormat), "HAL format %#x is not linear pcm", mFormat);
+ mChannelCount = audio_channel_count_from_in_mask(mChannelMask);
+ if (audio_is_linear_pcm(mFormat)) {
+ LOG_ALWAYS_FATAL_IF(mChannelCount > FCC_8, "HAL channel count %d > %d",
+ mChannelCount, FCC_8);
+ } else {
+ // Can have more that FCC_8 channels in encoded streams.
+ ALOGI("HAL format %#x is not linear pcm", mFormat);
+ }
result = mInput->stream->getFrameSize(&mFrameSize);
LOG_ALWAYS_FATAL_IF(result != OK, "Error retrieving frame size from HAL: %d", result);
result = mInput->stream->getBufferSize(&mBufferSize);
@@ -8075,7 +8107,7 @@
}
// This will decrement references and may cause the destruction of this thread.
if (isOutput()) {
- AudioSystem::releaseOutput(mId, streamType(), mSessionId);
+ AudioSystem::releaseOutput(mPortId);
} else {
AudioSystem::releaseInput(mPortId);
}
@@ -8189,7 +8221,7 @@
bool silenced = false;
if (isOutput()) {
- ret = AudioSystem::startOutput(mId, streamType(), mSessionId);
+ ret = AudioSystem::startOutput(portId);
} else {
ret = AudioSystem::startInput(portId, &silenced);
}
@@ -8201,7 +8233,7 @@
if (mActiveTracks.size() != 0) {
mLock.unlock();
if (isOutput()) {
- AudioSystem::releaseOutput(mId, streamType(), mSessionId);
+ AudioSystem::releaseOutput(portId);
} else {
AudioSystem::releaseInput(portId);
}
@@ -8273,8 +8305,8 @@
mLock.unlock();
if (isOutput()) {
- AudioSystem::stopOutput(mId, streamType(), track->sessionId());
- AudioSystem::releaseOutput(mId, streamType(), track->sessionId());
+ AudioSystem::stopOutput(track->portId());
+ AudioSystem::releaseOutput(track->portId());
} else {
AudioSystem::stopInput(track->portId());
AudioSystem::releaseInput(track->portId());
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 064e291..0c833f1 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1224,6 +1224,23 @@
virtual bool hasFastMixer() const { return false; }
virtual int64_t computeWaitTimeNs_l() const override;
+
+ status_t threadloop_getHalTimestamp_l(ExtendedTimestamp *timestamp) const override {
+ // For DIRECT and OFFLOAD threads, query the output sink directly.
+ if (mOutput != nullptr) {
+ uint64_t uposition64;
+ struct timespec time;
+ if (mOutput->getPresentationPosition(
+ &uposition64, &time) == OK) {
+ timestamp->mPosition[ExtendedTimestamp::LOCATION_KERNEL]
+ = (int64_t)uposition64;
+ timestamp->mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]
+ = audio_utils_ns_from_timespec(&time);
+ return NO_ERROR;
+ }
+ }
+ return INVALID_OPERATION;
+ }
};
class OffloadThread : public DirectOutputThread {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 22e610e..8b9485f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -486,7 +486,7 @@
wasActive = playbackThread->destroyTrack_l(this);
}
if (isExternalTrack() && !wasActive) {
- AudioSystem::releaseOutput(mThreadIoHandle, mStreamType, mSessionId);
+ AudioSystem::releaseOutput(mPortId);
}
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 6e66d3e..5cdd63a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -202,27 +202,25 @@
return BAD_VALUE;
}
- // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
- // output is suspended before any tracks are moved to it
- checkA2dpSuspend();
- checkOutputForAllStrategies();
- // outputs must be closed after checkOutputForAllStrategies() is executed
- if (!outputs.isEmpty()) {
- for (audio_io_handle_t output : outputs) {
- sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
- // close unused outputs after device disconnection or direct outputs that have been
- // opened by checkOutputsForDevice() to query dynamic parameters
- if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
- (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
- (desc->mDirectOpenCount == 0))) {
- closeOutput(output);
+ checkForDeviceAndOutputChanges([&]() {
+ // outputs must be closed after checkOutputForAllStrategies() is executed
+ if (!outputs.isEmpty()) {
+ for (audio_io_handle_t output : outputs) {
+ sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(output);
+ // close unused outputs after device disconnection or direct outputs that have been
+ // opened by checkOutputsForDevice() to query dynamic parameters
+ if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
+ (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) &&
+ (desc->mDirectOpenCount == 0))) {
+ closeOutput(output);
+ }
}
+ // check A2DP again after closing A2DP output to reset mA2dpSuspended if needed
+ return true;
}
- // check again after closing A2DP output to reset mA2dpSuspended if needed
- checkA2dpSuspend();
- }
+ return false;
+ });
- updateDevicesAndOutputs();
if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/);
updateCallRouting(newDevice);
@@ -565,9 +563,7 @@
|| (is_state_in_call(state) && (state != oldState)));
// check for device and output changes triggered by new phone state
- checkA2dpSuspend();
- checkOutputForAllStrategies();
- updateDevicesAndOutputs();
+ checkForDeviceAndOutputChanges();
int delayMs = 0;
if (isStateInCall(state)) {
@@ -667,9 +663,7 @@
(usage == AUDIO_POLICY_FORCE_FOR_SYSTEM);
// check for device and output changes triggered by new force usage
- checkA2dpSuspend();
- checkOutputForAllStrategies();
- updateDevicesAndOutputs();
+ checkForDeviceAndOutputChanges();
//FIXME: workaround for truncated touch sounds
// to be removed when the problem is handled by system UI
@@ -801,39 +795,53 @@
stream_type_to_audio_attributes(*stream, &attributes);
}
- // TODO: check for existing client for this port ID
- if (*portId == AUDIO_PORT_HANDLE_NONE) {
- *portId = AudioPort::getNextUniqueId();
- }
-
- sp<SwAudioOutputDescriptor> desc;
- if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
- ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
- if (!audio_has_proportional_frames(config->format)) {
- return BAD_VALUE;
- }
- *stream = streamTypefromAttributesInt(&attributes);
- *output = desc->mIoHandle;
- ALOGV("getOutputForAttr() returns output %d", *output);
- return NO_ERROR;
- }
- if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
- ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
- return BAD_VALUE;
- }
-
ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x"
" session %d selectedDeviceId %d",
attributes.usage, attributes.content_type, attributes.tags, attributes.flags,
session, *selectedDeviceId);
- *stream = streamTypefromAttributesInt(&attributes);
+ // TODO: check for existing client for this port ID
+ if (*portId == AUDIO_PORT_HANDLE_NONE) {
+ *portId = AudioPort::getNextUniqueId();
+ }
- // Explicit routing?
+ // First check for explicit routing (eg. setPreferredDevice)
sp<DeviceDescriptor> deviceDesc;
if (*selectedDeviceId != AUDIO_PORT_HANDLE_NONE) {
deviceDesc = mAvailableOutputDevices.getDeviceFromId(*selectedDeviceId);
+ } else {
+ // If no explict route, is there a matching dynamic policy that applies?
+ sp<SwAudioOutputDescriptor> desc;
+ if (mPolicyMixes.getOutputForAttr(attributes, uid, desc) == NO_ERROR) {
+ ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
+ if (!audio_has_proportional_frames(config->format)) {
+ return BAD_VALUE;
+ }
+ *stream = streamTypefromAttributesInt(&attributes);
+ *output = desc->mIoHandle;
+ ALOGV("getOutputForAttr() returns output %d", *output);
+ return NO_ERROR;
+ }
+
+ // Virtual sources must always be dynamicaly or explicitly routed
+ if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+ ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
+ return BAD_VALUE;
+ }
}
+
+ // Virtual sources must always be dynamicaly or explicitly routed
+ if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
+ ALOGW("getOutputForAttr() no policy mix found for usage AUDIO_USAGE_VIRTUAL_SOURCE");
+ return BAD_VALUE;
+ }
+
+ *stream = streamTypefromAttributesInt(&attributes);
+
+ // TODO: Should this happen only if an explicit route is active?
+ // the previous code structure meant that this would always happen which
+ // would appear to result in adding a null deviceDesc when not using an
+ // explicit route. Is that the intended and necessary behavior?
mOutputRoutes.addRoute(session, *stream, SessionRoute::SOURCE_TYPE_NA, deviceDesc, uid);
routing_strategy strategy = (routing_strategy) getStrategyForAttr(&attributes);
@@ -2251,7 +2259,7 @@
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
- if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream) || isInCall())) {
+ if (!(streamsMatchForvolume(stream, (audio_stream_type_t)curStream))) {
continue;
}
if (!(desc->isStreamActive((audio_stream_type_t)curStream) || isInCall())) {
@@ -2272,13 +2280,15 @@
applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
stream, curStreamDevice);
}
-
+ // rescale index before applying to curStream as ranges may be different for
+ // stream and curStream
+ int idx = rescaleVolumeIndex(index, stream, (audio_stream_type_t)curStream);
if (applyVolume) {
//FIXME: workaround for truncated touch sounds
// delayed volume change for system stream to be removed when the problem is
// handled by system UI
status_t volStatus =
- checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
+ checkAndSetVolume((audio_stream_type_t)curStream, idx, desc, curDevice,
(stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
if (volStatus != NO_ERROR) {
status = volStatus;
@@ -2811,8 +2821,7 @@
}
ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
- if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX ||
- patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) {
+ if (!audio_patch_is_valid(patch)) {
return BAD_VALUE;
}
// only one source per audio patch supported for now
@@ -4632,6 +4641,21 @@
return true;
}
+void AudioPolicyManager::checkForDeviceAndOutputChanges()
+{
+ checkForDeviceAndOutputChanges([](){ return false; });
+}
+
+void AudioPolicyManager::checkForDeviceAndOutputChanges(std::function<bool()> onOutputsChecked)
+{
+ // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP
+ // output is suspended before any tracks are moved to it
+ checkA2dpSuspend();
+ checkOutputForAllStrategies();
+ if (onOutputsChecked()) checkA2dpSuspend();
+ updateDevicesAndOutputs();
+}
+
void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy)
{
audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/);
@@ -5486,6 +5510,21 @@
return volumeDB;
}
+int AudioPolicyManager::rescaleVolumeIndex(int srcIndex,
+ audio_stream_type_t srcStream,
+ audio_stream_type_t dstStream)
+{
+ if (srcStream == dstStream) {
+ return srcIndex;
+ }
+ float minSrc = (float)mVolumeCurves->getVolumeIndexMin(srcStream);
+ float maxSrc = (float)mVolumeCurves->getVolumeIndexMax(srcStream);
+ float minDst = (float)mVolumeCurves->getVolumeIndexMin(dstStream);
+ float maxDst = (float)mVolumeCurves->getVolumeIndexMax(dstStream);
+
+ return (int)(minDst + ((srcIndex - minSrc) * (maxDst - minDst)) / (maxSrc - minSrc));
+}
+
status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
int index,
const sp<AudioOutputDescriptor>& outputDesc,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 136e522..70ca39f 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -17,6 +17,7 @@
#pragma once
#include <atomic>
+#include <functional>
#include <memory>
#include <unordered_set>
@@ -353,6 +354,10 @@
int index,
audio_devices_t device);
+ // rescale volume index from srcStream within range of dstStream
+ int rescaleVolumeIndex(int srcIndex,
+ audio_stream_type_t srcStream,
+ audio_stream_type_t dstStream);
// check that volume change is permitted, compute and send new volume to audio hardware
virtual status_t checkAndSetVolume(audio_stream_type_t stream, int index,
const sp<AudioOutputDescriptor>& outputDesc,
@@ -406,6 +411,13 @@
// close an input.
void closeInput(audio_io_handle_t input);
+ // runs all the checks required for accomodating changes in devices and outputs
+ // if 'onOutputsChecked' callback is provided, it is executed after the outputs
+ // check via 'checkOutputForAllStrategies'. If the callback returns 'true',
+ // A2DP suspend status is rechecked.
+ void checkForDeviceAndOutputChanges();
+ void checkForDeviceAndOutputChanges(std::function<bool()> onOutputsChecked);
+
// checks and if necessary changes outputs used for all strategies.
// must be called every time a condition that affects the output choice for a given strategy
// changes: connected device, phone state, force use...
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index d2bc40d..859072b 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -17,12 +17,12 @@
#define LOG_TAG "AudioPolicyIntefaceImpl"
//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-#include <media/MediaAnalyticsItem.h>
-
#include "AudioPolicyService.h"
-#include <mediautils/ServiceUtilities.h>
#include "TypeConverter.h"
+#include <media/AudioPolicyHelper.h>
+#include <media/MediaAnalyticsItem.h>
+#include <mediautils/ServiceUtilities.h>
+#include <utils/Log.h>
namespace android {
@@ -208,93 +208,128 @@
config,
&flags, selectedDeviceId, portId);
}
+
+ if (result == NO_ERROR) {
+ sp <AudioPlaybackClient> client =
+ new AudioPlaybackClient(*attr, *output, uid, pid, session, *selectedDeviceId, *stream);
+ mAudioPlaybackClients.add(*portId, client);
+ }
return result;
}
-status_t AudioPolicyService::startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+status_t AudioPolicyService::startOutput(audio_port_handle_t portId)
{
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
- return BAD_VALUE;
- }
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
ALOGV("startOutput()");
+ sp<AudioPlaybackClient> client;
sp<AudioPolicyEffects>audioPolicyEffects;
{
Mutex::Autolock _l(mLock);
+ const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+ if (index < 0) {
+ ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
+ return INVALID_OPERATION;
+ }
+ client = mAudioPlaybackClients.valueAt(index);
audioPolicyEffects = mAudioPolicyEffects;
}
if (audioPolicyEffects != 0) {
// create audio processors according to stream
- status_t status = audioPolicyEffects->addOutputSessionEffects(output, stream, session);
+ status_t status = audioPolicyEffects->addOutputSessionEffects(
+ client->io, client->stream, client->session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to add effects on session %d", session);
+ ALOGW("Failed to add effects on session %d", client->session);
}
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->startOutput(output, stream, session);
+ status_t status = mAudioPolicyManager->startOutput(
+ client->io, client->stream, client->session);
+ if (status == NO_ERROR) {
+ client->active = true;
+ }
+ return status;
}
-status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+status_t AudioPolicyService::stopOutput(audio_port_handle_t portId)
{
- if (uint32_t(stream) >= AUDIO_STREAM_CNT) {
- return BAD_VALUE;
+ {
+ Mutex::Autolock _l(mLock);
+
+ const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+ if (index < 0) {
+ ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
+ return INVALID_OPERATION;
+ }
}
if (mAudioPolicyManager == NULL) {
return NO_INIT;
}
ALOGV("stopOutput()");
- mOutputCommandThread->stopOutputCommand(output, stream, session);
+ mOutputCommandThread->stopOutputCommand(portId);
return NO_ERROR;
}
-status_t AudioPolicyService::doStopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+status_t AudioPolicyService::doStopOutput(audio_port_handle_t portId)
{
- ALOGV("doStopOutput from tid %d", gettid());
+ ALOGV("doStopOutput");
+ sp<AudioPlaybackClient> client;
sp<AudioPolicyEffects>audioPolicyEffects;
{
Mutex::Autolock _l(mLock);
+
+ const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+ if (index < 0) {
+ ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
+ return INVALID_OPERATION;
+ }
+ client = mAudioPlaybackClients.valueAt(index);
audioPolicyEffects = mAudioPolicyEffects;
}
if (audioPolicyEffects != 0) {
// release audio processors from the stream
- status_t status = audioPolicyEffects->releaseOutputSessionEffects(output, stream, session);
+ status_t status = audioPolicyEffects->releaseOutputSessionEffects(
+ client->io, client->stream, client->session);
if (status != NO_ERROR && status != ALREADY_EXISTS) {
- ALOGW("Failed to release effects on session %d", session);
+ ALOGW("Failed to release effects on session %d", client->session);
}
}
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- return mAudioPolicyManager->stopOutput(output, stream, session);
+ status_t status = mAudioPolicyManager->stopOutput(
+ client->io, client->stream, client->session);
+ if (status == NO_ERROR) {
+ client->active = false;
+ }
+ return status;
}
-void AudioPolicyService::releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+void AudioPolicyService::releaseOutput(audio_port_handle_t portId)
{
if (mAudioPolicyManager == NULL) {
return;
}
ALOGV("releaseOutput()");
- mOutputCommandThread->releaseOutputCommand(output, stream, session);
+ mOutputCommandThread->releaseOutputCommand(portId);
}
-void AudioPolicyService::doReleaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId)
{
ALOGV("doReleaseOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
+ const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+ if (index < 0) {
+ ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
+ return;
+ }
+ sp<AudioPlaybackClient> client = mAudioPlaybackClients.valueAt(index);
+ mAudioRecordClients.removeItem(portId);
+
// called from internal thread: no need to clear caller identity
- mAudioPolicyManager->releaseOutput(output, stream, session);
+ mAudioPolicyManager->releaseOutput(
+ client->io, client->stream, client->session);
}
status_t AudioPolicyService::getInputForAttr(const audio_attributes_t *attr,
@@ -403,12 +438,8 @@
return status;
}
- sp<AudioRecordClient> client =
- new AudioRecordClient(*attr, *input, uid, pid, opPackageName, session);
- client->active = false;
- client->isConcurrent = false;
- client->isVirtualDevice = false; //TODO : update from APM->getInputForAttr()
- client->deviceId = *selectedDeviceId;
+ sp<AudioRecordClient> client = new AudioRecordClient(*attr, *input, uid, pid, session,
+ *selectedDeviceId, opPackageName);
mAudioRecordClients.add(*portId, client);
}
@@ -497,7 +528,7 @@
{
AutoCallerClear acc;
status = mAudioPolicyManager->startInput(
- client->input, client->session, *silenced, &concurrency);
+ client->io, client->session, *silenced, &concurrency);
}
@@ -610,7 +641,7 @@
// finish the recording app op
finishRecording(client->opPackageName, client->uid);
AutoCallerClear acc;
- return mAudioPolicyManager->stopInput(client->input, client->session);
+ return mAudioPolicyManager->stopInput(client->io, client->session);
}
void AudioPolicyService::releaseInput(audio_port_handle_t portId)
@@ -635,15 +666,15 @@
}
if (audioPolicyEffects != 0) {
// release audio processors from the input
- status_t status = audioPolicyEffects->releaseInputEffects(client->input, client->session);
+ status_t status = audioPolicyEffects->releaseInputEffects(client->io, client->session);
if(status != NO_ERROR) {
- ALOGW("Failed to release effects on input %d", client->input);
+ ALOGW("Failed to release effects on input %d", client->io);
}
}
{
Mutex::Autolock _l(mLock);
AutoCallerClear acc;
- mAudioPolicyManager->releaseInput(client->input, client->session);
+ mAudioPolicyManager->releaseInput(client->io, client->session);
}
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index ca3b6b6..8bca221 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -695,26 +695,26 @@
}break;
case STOP_OUTPUT: {
StopOutputData *data = (StopOutputData *)command->mParam.get();
- ALOGV("AudioCommandThread() processing stop output %d",
- data->mIO);
+ ALOGV("AudioCommandThread() processing stop output portId %d",
+ data->mPortId);
svc = mService.promote();
if (svc == 0) {
break;
}
mLock.unlock();
- svc->doStopOutput(data->mIO, data->mStream, data->mSession);
+ svc->doStopOutput(data->mPortId);
mLock.lock();
}break;
case RELEASE_OUTPUT: {
ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get();
- ALOGV("AudioCommandThread() processing release output %d",
- data->mIO);
+ ALOGV("AudioCommandThread() processing release output portId %d",
+ data->mPortId);
svc = mService.promote();
if (svc == 0) {
break;
}
mLock.unlock();
- svc->doReleaseOutput(data->mIO, data->mStream, data->mSession);
+ svc->doReleaseOutput(data->mPortId);
mLock.lock();
}break;
case CREATE_AUDIO_PATCH: {
@@ -925,33 +925,25 @@
return sendCommand(command, delayMs);
}
-void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_port_handle_t portId)
{
sp<AudioCommand> command = new AudioCommand();
command->mCommand = STOP_OUTPUT;
sp<StopOutputData> data = new StopOutputData();
- data->mIO = output;
- data->mStream = stream;
- data->mSession = session;
+ data->mPortId = portId;
command->mParam = data;
- ALOGV("AudioCommandThread() adding stop output %d", output);
+ ALOGV("AudioCommandThread() adding stop output portId %d", portId);
sendCommand(command);
}
-void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session)
+void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_port_handle_t portId)
{
sp<AudioCommand> command = new AudioCommand();
command->mCommand = RELEASE_OUTPUT;
sp<ReleaseOutputData> data = new ReleaseOutputData();
- data->mIO = output;
- data->mStream = stream;
- data->mSession = session;
+ data->mPortId = portId;
command->mParam = data;
- ALOGV("AudioCommandThread() adding release output %d", output);
+ ALOGV("AudioCommandThread() adding release output portId %d", portId);
sendCommand(command);
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index a1366bb..d41069e 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -81,15 +81,9 @@
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId);
- virtual status_t startOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- virtual status_t stopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- virtual void releaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
+ virtual status_t startOutput(audio_port_handle_t portId);
+ virtual status_t stopOutput(audio_port_handle_t portId);
+ virtual void releaseOutput(audio_port_handle_t portId);
virtual status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,
audio_session_t session,
@@ -205,12 +199,8 @@
bool reported);
virtual status_t setSurroundFormatEnabled(audio_format_t audioFormat, bool enabled);
- status_t doStopOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- void doReleaseOutput(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
+ status_t doStopOutput(audio_port_handle_t portId);
+ void doReleaseOutput(audio_port_handle_t portId);
status_t clientCreateAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
@@ -340,12 +330,8 @@
status_t parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
- void stopOutputCommand(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
- void releaseOutputCommand(audio_io_handle_t output,
- audio_stream_type_t stream,
- audio_session_t session);
+ void stopOutputCommand(audio_port_handle_t portId);
+ void releaseOutputCommand(audio_port_handle_t portId);
status_t sendCommand(sp<AudioCommand>& command, int delayMs = 0);
void insertCommand_l(sp<AudioCommand>& command, int delayMs = 0);
status_t createAudioPatchCommand(const struct audio_patch *patch,
@@ -413,16 +399,12 @@
class StopOutputData : public AudioCommandData {
public:
- audio_io_handle_t mIO;
- audio_stream_type_t mStream;
- audio_session_t mSession;
+ audio_port_handle_t mPortId;
};
class ReleaseOutputData : public AudioCommandData {
public:
- audio_io_handle_t mIO;
- audio_stream_type_t mStream;
- audio_session_t mSession;
+ audio_port_handle_t mPortId;
};
class CreateAudioPatchData : public AudioCommandData {
@@ -603,30 +585,56 @@
bool mAudioPortCallbacksEnabled;
};
+ class AudioClient : public virtual RefBase {
+ public:
+ AudioClient(const audio_attributes_t attributes,
+ const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_session_t session, const audio_port_handle_t deviceId) :
+ attributes(attributes), io(io), uid(uid), pid(pid),
+ session(session), deviceId(deviceId), active(false) {}
+ ~AudioClient() override = default;
+
+
+ const audio_attributes_t attributes; // source, flags ...
+ const audio_io_handle_t io; // audio HAL stream IO handle
+ const uid_t uid; // client UID
+ const pid_t pid; // client PID
+ const audio_session_t session; // audio session ID
+ const audio_port_handle_t deviceId; // selected input device port ID
+ bool active; // Playback/Capture is active or inactive
+ };
+
// --- AudioRecordClient ---
// Information about each registered AudioRecord client
// (between calls to getInputForAttr() and releaseInput())
- class AudioRecordClient : public RefBase {
+ class AudioRecordClient : public AudioClient {
public:
AudioRecordClient(const audio_attributes_t attributes,
- const audio_io_handle_t input, uid_t uid, pid_t pid,
- const String16& opPackageName, const audio_session_t session) :
- attributes(attributes),
- input(input), uid(uid), pid(pid),
- opPackageName(opPackageName), session(session),
- active(false), isConcurrent(false), isVirtualDevice(false) {}
- virtual ~AudioRecordClient() {}
+ const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_session_t session, const audio_port_handle_t deviceId,
+ const String16& opPackageName) :
+ AudioClient(attributes, io, uid, pid, session, deviceId),
+ opPackageName(opPackageName), isConcurrent(false), isVirtualDevice(false) {}
+ ~AudioRecordClient() override = default;
- const audio_attributes_t attributes; // source, flags ...
- const audio_io_handle_t input; // audio HAL input IO handle
- const uid_t uid; // client UID
- const pid_t pid; // client PID
const String16 opPackageName; // client package name
- const audio_session_t session; // audio session ID
- bool active; // Capture is active or inactive
bool isConcurrent; // is allowed to concurrent capture
bool isVirtualDevice; // uses virtual device: updated by APM::getInputForAttr()
- audio_port_handle_t deviceId; // selected input device port ID
+ };
+
+ // --- AudioPlaybackClient ---
+ // Information about each registered AudioTrack client
+ // (between calls to getOutputForAttr() and releaseOutput())
+ class AudioPlaybackClient : public AudioClient {
+ public:
+ AudioPlaybackClient(const audio_attributes_t attributes,
+ const audio_io_handle_t io, uid_t uid, pid_t pid,
+ const audio_session_t session, audio_port_handle_t deviceId,
+ audio_stream_type_t stream) :
+ AudioClient(attributes, io, uid, pid, session, deviceId), stream(stream) {}
+ ~AudioPlaybackClient() override = default;
+
+ const audio_stream_type_t stream;
};
// A class automatically clearing and restoring binder caller identity inside
@@ -670,6 +678,7 @@
sp<UidPolicy> mUidPolicy;
DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> > mAudioRecordClients;
+ DefaultKeyedVector< audio_port_handle_t, sp<AudioPlaybackClient> > mAudioPlaybackClients;
};
} // namespace android
diff --git a/services/audiopolicy/tests/Android.mk b/services/audiopolicy/tests/Android.mk
index cfa9ab1..b739b88 100644
--- a/services/audiopolicy/tests/Android.mk
+++ b/services/audiopolicy/tests/Android.mk
@@ -29,3 +29,26 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
include $(BUILD_NATIVE_TEST)
+
+# system/audio.h utilities test
+
+include $(CLEAR_VARS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+ libmedia_helper \
+ libutils
+
+LOCAL_SRC_FILES := \
+ systemaudio_tests.cpp \
+
+LOCAL_MODULE := systemaudio_tests
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_CFLAGS := -Werror -Wall
+
+LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+
+include $(BUILD_NATIVE_TEST)
diff --git a/services/audiopolicy/tests/systemaudio_tests.cpp b/services/audiopolicy/tests/systemaudio_tests.cpp
new file mode 100644
index 0000000..abaae52
--- /dev/null
+++ b/services/audiopolicy/tests/systemaudio_tests.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "SysAudio_Test"
+#include <log/log.h>
+#include <media/PatchBuilder.h>
+#include <system/audio.h>
+
+using namespace android;
+
+TEST(SystemAudioTest, PatchInvalid) {
+ audio_patch patch{};
+ ASSERT_FALSE(audio_patch_is_valid(&patch));
+ patch.num_sources = AUDIO_PATCH_PORTS_MAX + 1;
+ patch.num_sinks = 1;
+ ASSERT_FALSE(audio_patch_is_valid(&patch));
+ patch.num_sources = 1;
+ patch.num_sinks = AUDIO_PATCH_PORTS_MAX + 1;
+ ASSERT_FALSE(audio_patch_is_valid(&patch));
+ patch.num_sources = 0;
+ patch.num_sinks = 1;
+ ASSERT_FALSE(audio_patch_is_valid(&patch));
+}
+
+TEST(SystemAudioTest, PatchValid) {
+ const audio_port_config src = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE };
+ // It's OK not to have sinks.
+ ASSERT_TRUE(audio_patch_is_valid((PatchBuilder{}).addSource(src).patch()));
+ const audio_port_config sink = {
+ .id = 2, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE };
+ ASSERT_TRUE(audio_patch_is_valid((PatchBuilder{}).addSource(src).addSink(sink).patch()));
+ ASSERT_TRUE(audio_patch_is_valid(
+ (PatchBuilder{}).addSource(src).addSource(src).addSink(sink).patch()));
+ ASSERT_TRUE(audio_patch_is_valid(
+ (PatchBuilder{}).addSource(src).addSink(sink).addSink(sink).patch()));
+ ASSERT_TRUE(audio_patch_is_valid(
+ (PatchBuilder{}).addSource(src).addSource(src).
+ addSink(sink).addSink(sink).patch()));
+}
+
+TEST(SystemAudioTest, PatchHwAvSync) {
+ audio_port_config device_src_cfg = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE };
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_src_cfg));
+ device_src_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_src_cfg));
+ device_src_cfg.flags.input = AUDIO_INPUT_FLAG_HW_AV_SYNC;
+ ASSERT_TRUE(audio_port_config_has_hw_av_sync(&device_src_cfg));
+
+ audio_port_config device_sink_cfg = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE };
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_sink_cfg));
+ device_sink_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&device_sink_cfg));
+ device_sink_cfg.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+ ASSERT_TRUE(audio_port_config_has_hw_av_sync(&device_sink_cfg));
+
+ audio_port_config mix_sink_cfg = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_MIX };
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_sink_cfg));
+ mix_sink_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_sink_cfg));
+ mix_sink_cfg.flags.input = AUDIO_INPUT_FLAG_HW_AV_SYNC;
+ ASSERT_TRUE(audio_port_config_has_hw_av_sync(&mix_sink_cfg));
+
+ audio_port_config mix_src_cfg = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_MIX };
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_src_cfg));
+ mix_src_cfg.config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+ ASSERT_FALSE(audio_port_config_has_hw_av_sync(&mix_src_cfg));
+ mix_src_cfg.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+ ASSERT_TRUE(audio_port_config_has_hw_av_sync(&mix_src_cfg));
+}
+
+TEST(SystemAudioTest, PatchEqual) {
+ const audio_patch patch1{}, patch2{};
+ // Invalid patches are not equal.
+ ASSERT_FALSE(audio_patches_are_equal(&patch1, &patch2));
+ const audio_port_config src = {
+ .id = 1, .role = AUDIO_PORT_ROLE_SOURCE, .type = AUDIO_PORT_TYPE_DEVICE };
+ const audio_port_config sink = {
+ .id = 2, .role = AUDIO_PORT_ROLE_SINK, .type = AUDIO_PORT_TYPE_DEVICE };
+ ASSERT_FALSE(audio_patches_are_equal(
+ (PatchBuilder{}).addSource(src).patch(),
+ (PatchBuilder{}).addSource(src).addSink(sink).patch()));
+ ASSERT_TRUE(audio_patches_are_equal(
+ (PatchBuilder{}).addSource(src).addSink(sink).patch(),
+ (PatchBuilder{}).addSource(src).addSink(sink).patch()));
+ ASSERT_FALSE(audio_patches_are_equal(
+ (PatchBuilder{}).addSource(src).addSink(sink).patch(),
+ (PatchBuilder{}).addSource(src).addSource(src).addSink(sink).patch()));
+ audio_port_config sink_hw_av_sync = sink;
+ sink_hw_av_sync.config_mask |= AUDIO_PORT_CONFIG_FLAGS;
+ sink_hw_av_sync.flags.output = AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+ ASSERT_FALSE(audio_patches_are_equal(
+ (PatchBuilder{}).addSource(src).addSink(sink).patch(),
+ (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch()));
+ ASSERT_TRUE(audio_patches_are_equal(
+ (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch(),
+ (PatchBuilder{}).addSource(src).addSink(sink_hw_av_sync).patch()));
+}
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 98d0534..baf051a 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -661,7 +661,8 @@
}
sp<Surface> surface;
- res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer);
+ res = createSurfaceFromGbp(streamInfo, isStreamInfoValid, surface, bufferProducer,
+ physicalCameraId);
if (!res.isOk())
return res;
@@ -889,6 +890,8 @@
const std::vector<sp<IGraphicBufferProducer> >& bufferProducers =
outputConfiguration.getGraphicBufferProducers();
+ String8 physicalCameraId(outputConfiguration.getPhysicalCameraId());
+
auto producerCount = bufferProducers.size();
if (producerCount == 0) {
ALOGE("%s: bufferProducers must not be empty", __FUNCTION__);
@@ -942,7 +945,7 @@
OutputStreamInfo outInfo;
sp<Surface> surface;
res = createSurfaceFromGbp(outInfo, /*isStreamInfoValid*/ false, surface,
- newOutputsMap.valueAt(i));
+ newOutputsMap.valueAt(i), physicalCameraId);
if (!res.isOk())
return res;
@@ -1021,7 +1024,8 @@
binder::Status CameraDeviceClient::createSurfaceFromGbp(
OutputStreamInfo& streamInfo, bool isStreamInfoValid,
- sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp) {
+ sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
+ const String8& physicalId) {
// bufferProducer must be non-null
if (gbp == nullptr) {
@@ -1098,7 +1102,7 @@
// Round dimensions to the nearest dimensions available for this format
if (flexibleConsumer && isPublicFormat(format) &&
!CameraDeviceClient::roundBufferDimensionNearest(width, height,
- format, dataSpace, mDevice->info(), /*out*/&width, /*out*/&height)) {
+ format, dataSpace, mDevice->info(physicalId), /*out*/&width, /*out*/&height)) {
String8 msg = String8::format("Camera %s: No supported stream configurations with "
"format %#x defined, failed to create output stream",
mCameraIdStr.string(), format);
@@ -1468,6 +1472,7 @@
const std::vector<sp<IGraphicBufferProducer> >& bufferProducers =
outputConfiguration.getGraphicBufferProducers();
+ String8 physicalId(outputConfiguration.getPhysicalCameraId());
if (bufferProducers.size() == 0) {
ALOGE("%s: bufferProducers must not be empty", __FUNCTION__);
@@ -1521,7 +1526,7 @@
sp<Surface> surface;
res = createSurfaceFromGbp(mStreamInfoMap[streamId], true /*isStreamInfoValid*/,
- surface, bufferProducer);
+ surface, bufferProducer, physicalId);
if (!res.isOk())
return res;
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 5aaf5aa..c30561d 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -258,7 +258,8 @@
// Create a Surface from an IGraphicBufferProducer. Returns error if
// IGraphicBufferProducer's property doesn't match with streamInfo
binder::Status createSurfaceFromGbp(OutputStreamInfo& streamInfo, bool isStreamInfoValid,
- sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp);
+ sp<Surface>& surface, const sp<IGraphicBufferProducer>& gbp,
+ const String8& physicalCameraId);
// Utility method to insert the surface into SurfaceMap
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 0ba7403..98c1b5e 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -69,6 +69,10 @@
* The device's static characteristics metadata buffer
*/
virtual const CameraMetadata& info() const = 0;
+ /**
+ * The physical camera device's static characteristics metadata buffer
+ */
+ virtual const CameraMetadata& info(const String8& physicalId) const = 0;
struct PhysicalCameraSettings {
std::string cameraId;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 0ce4318..3be6399 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -336,6 +336,7 @@
const hardware::hidl_string& /*fqName*/,
const hardware::hidl_string& name,
bool /*preexisting*/) {
+ std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
{
std::lock_guard<std::mutex> lock(mInterfaceMutex);
@@ -458,6 +459,7 @@
}
status_t CameraProviderManager::removeProvider(const std::string& provider) {
+ std::lock_guard<std::mutex> providerLock(mProviderLifecycleLock);
std::unique_lock<std::mutex> lock(mInterfaceMutex);
std::vector<String8> removedDeviceIds;
status_t res = NAME_NOT_FOUND;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index b8b8b8c..c523c2d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -246,6 +246,9 @@
wp<StatusListener> mListener;
ServiceInteractionProxy* mServiceProxy;
+ // mProviderLifecycleLock is locked during onRegistration and removeProvider
+ mutable std::mutex mProviderLifecycleLock;
+
static HardwareServiceInteractionProxy sHardwareServiceInteractionProxy;
struct ProviderInfo :
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 543914e..3958d5b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -121,11 +121,25 @@
res = manager->getCameraCharacteristics(mId.string(), &mDeviceInfo);
if (res != OK) {
- SET_ERR_L("Could not retrive camera characteristics: %s (%d)", strerror(-res), res);
+ SET_ERR_L("Could not retrieve camera characteristics: %s (%d)", strerror(-res), res);
session->close();
return res;
}
+ std::vector<std::string> physicalCameraIds;
+ bool isLogical = CameraProviderManager::isLogicalCamera(mDeviceInfo, &physicalCameraIds);
+ if (isLogical) {
+ for (auto& physicalId : physicalCameraIds) {
+ res = manager->getCameraCharacteristics(physicalId, &mPhysicalDeviceInfoMap[physicalId]);
+ if (res != OK) {
+ SET_ERR_L("Could not retrieve camera %s characteristics: %s (%d)",
+ physicalId.c_str(), strerror(-res), res);
+ session->close();
+ return res;
+ }
+ }
+ }
+
std::shared_ptr<RequestMetadataQueue> queue;
auto requestQueueRet = session->getCaptureRequestMetadataQueue(
[&queue](const auto& descriptor) {
@@ -719,7 +733,7 @@
return OK;
}
-const CameraMetadata& Camera3Device::info() const {
+const CameraMetadata& Camera3Device::info(const String8& physicalId) const {
ALOGVV("%s: E", __FUNCTION__);
if (CC_UNLIKELY(mStatus == STATUS_UNINITIALIZED ||
mStatus == STATUS_ERROR)) {
@@ -727,7 +741,22 @@
mStatus == STATUS_ERROR ?
"when in error state" : "before init");
}
- return mDeviceInfo;
+ if (physicalId.isEmpty()) {
+ return mDeviceInfo;
+ } else {
+ std::string id(physicalId.c_str());
+ if (mPhysicalDeviceInfoMap.find(id) != mPhysicalDeviceInfoMap.end()) {
+ return mPhysicalDeviceInfoMap.at(id);
+ } else {
+ ALOGE("%s: Invalid physical camera id %s", __FUNCTION__, physicalId.c_str());
+ return mDeviceInfo;
+ }
+ }
+}
+
+const CameraMetadata& Camera3Device::info() const {
+ String8 emptyId;
+ return info(emptyId);
}
status_t Camera3Device::checkStatusOkToCaptureLocked() {
@@ -3889,18 +3918,17 @@
}
hardware::details::return_status err;
+ auto resultCallback =
+ [&status, &numRequestProcessed] (auto s, uint32_t n) {
+ status = s;
+ *numRequestProcessed = n;
+ };
if (hidlSession_3_4 != nullptr) {
err = hidlSession_3_4->processCaptureRequest_3_4(captureRequests_3_4, cachesToRemove,
- [&status, &numRequestProcessed] (auto s, uint32_t n) {
- status = s;
- *numRequestProcessed = n;
- });
+ resultCallback);
} else {
err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
- [&status, &numRequestProcessed] (auto s, uint32_t n) {
- status = s;
- *numRequestProcessed = n;
- });
+ resultCallback);
}
if (!err.isOk()) {
ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
@@ -4710,6 +4738,7 @@
status_t Camera3Device::RequestThread::prepareHalRequests() {
ATRACE_CALL();
+ bool batchedRequest = mNextRequests[0].captureRequest->mBatchSize > 1;
for (size_t i = 0; i < mNextRequests.size(); i++) {
auto& nextRequest = mNextRequests.editItemAt(i);
sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
@@ -4733,7 +4762,10 @@
mPrevTriggers = triggerCount;
// If the request is the same as last, or we had triggers last time
- bool newRequest = mPrevRequest != captureRequest || triggersMixedIn;
+ bool newRequest = (mPrevRequest != captureRequest || triggersMixedIn) &&
+ // Request settings are all the same within one batch, so only treat the first
+ // request in a batch as new
+ !(batchedRequest && i > 0);
if (newRequest) {
/**
* HAL workaround:
@@ -4882,7 +4914,7 @@
// preview), and the current request is not the last one in the batch,
// do not send callback to the app.
bool hasCallback = true;
- if (mNextRequests[0].captureRequest->mBatchSize > 1 && i != mNextRequests.size()-1) {
+ if (batchedRequest && i != mNextRequests.size()-1) {
hasCallback = false;
}
res = parent->registerInFlight(halRequest->frame_number,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index d8fe19f..ef3cbc4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -101,6 +101,7 @@
status_t disconnect() override;
status_t dump(int fd, const Vector<String16> &args) override;
const CameraMetadata& info() const override;
+ const CameraMetadata& info(const String8& physicalId) const override;
// Capture and setStreamingRequest will configure streams if currently in
// idle state
@@ -379,6 +380,7 @@
sp<HalInterface> mInterface;
CameraMetadata mDeviceInfo;
+ std::unordered_map<std::string, CameraMetadata> mPhysicalDeviceInfoMap;
CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index b3c3717..2c020a2 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -564,7 +564,7 @@
// Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
// let prepareNextBuffer handle the error.)
- if (res == NO_INIT && mState == STATE_CONFIGURED) {
+ if ((res == NO_INIT || res == DEAD_OBJECT) && mState == STATE_CONFIGURED) {
mState = STATE_ABANDONED;
}
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.cpp b/services/camera/libcameraservice/device3/DistortionMapper.cpp
index eef6658..4dafefd 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.cpp
+++ b/services/camera/libcameraservice/device3/DistortionMapper.cpp
@@ -49,7 +49,7 @@
};
// Only for capture result
-constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrect = {
+constexpr std::array<uint32_t, 2> DistortionMapper::kResultPointsToCorrectNoClamp = {
ANDROID_STATISTICS_FACE_RECTANGLES, // Says rectangles, is really points
ANDROID_STATISTICS_FACE_LANDMARKS,
};
@@ -79,12 +79,21 @@
array = deviceInfo.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
if (array.count != 4) return BAD_VALUE;
- mArrayWidth = array.data.i32[2];
- mArrayHeight = array.data.i32[3];
+ float arrayX = static_cast<float>(array.data.i32[0]);
+ float arrayY = static_cast<float>(array.data.i32[1]);
+ mArrayWidth = static_cast<float>(array.data.i32[2]);
+ mArrayHeight = static_cast<float>(array.data.i32[3]);
array = deviceInfo.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
- mActiveWidth = array.data.i32[2];
- mActiveHeight = array.data.i32[3];
+ if (array.count != 4) return BAD_VALUE;
+
+ float activeX = static_cast<float>(array.data.i32[0]);
+ float activeY = static_cast<float>(array.data.i32[1]);
+ mActiveWidth = static_cast<float>(array.data.i32[2]);
+ mActiveHeight = static_cast<float>(array.data.i32[3]);
+
+ mArrayDiffX = activeX - arrayX;
+ mArrayDiffY = activeY - arrayY;
return updateCalibration(deviceInfo);
}
@@ -111,22 +120,13 @@
if (weight == 0) {
continue;
}
- res = mapCorrectedToRaw(e.data.i32 + j, 2);
+ res = mapCorrectedToRaw(e.data.i32 + j, 2, /*clamp*/true);
if (res != OK) return res;
- for (size_t k = 0; k < 4; k+=2) {
- int32_t& x = e.data.i32[j + k];
- int32_t& y = e.data.i32[j + k + 1];
- // Clamp to within active array
- x = std::max(0, x);
- x = std::min(mActiveWidth - 1, x);
- y = std::max(0, y);
- y = std::min(mActiveHeight - 1, y);
- }
}
}
for (auto rect : kRequestRectsToCorrect) {
e = request->find(rect);
- res = mapCorrectedRectToRaw(e.data.i32, e.count / 4);
+ res = mapCorrectedRectToRaw(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
}
}
@@ -156,27 +156,18 @@
if (weight == 0) {
continue;
}
- res = mapRawToCorrected(e.data.i32 + j, 2);
+ res = mapRawToCorrected(e.data.i32 + j, 2, /*clamp*/true);
if (res != OK) return res;
- for (size_t k = 0; k < 4; k+=2) {
- int32_t& x = e.data.i32[j + k];
- int32_t& y = e.data.i32[j + k + 1];
- // Clamp to within active array
- x = std::max(0, x);
- x = std::min(mActiveWidth - 1, x);
- y = std::max(0, y);
- y = std::min(mActiveHeight - 1, y);
- }
}
}
for (auto rect : kResultRectsToCorrect) {
e = result->find(rect);
- res = mapRawRectToCorrected(e.data.i32, e.count / 4);
+ res = mapRawRectToCorrected(e.data.i32, e.count / 4, /*clamp*/true);
if (res != OK) return res;
}
- for (auto pts : kResultPointsToCorrect) {
+ for (auto pts : kResultPointsToCorrectNoClamp) {
e = result->find(pts);
- res = mapRawToCorrected(e.data.i32, e.count / 2);
+ res = mapRawToCorrected(e.data.i32, e.count / 2, /*clamp*/false);
if (res != OK) return res;
}
}
@@ -232,9 +223,12 @@
return OK;
}
-status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount) {
+status_t DistortionMapper::mapRawToCorrected(int32_t *coordPairs, int coordCount,
+ bool clamp, bool simple) {
if (!mValidMapping) return INVALID_OPERATION;
+ if (simple) return mapRawToCorrectedSimple(coordPairs, coordCount, clamp);
+
if (!mValidGrids) {
status_t res = buildGrids();
if (res != OK) return res;
@@ -275,6 +269,12 @@
// Interpolate along left edge of corrected quad (which are axis-aligned) for y
float corrY = corrQuad->coords[1] + v * (corrQuad->coords[7] - corrQuad->coords[1]);
+ // Clamp to within active array
+ if (clamp) {
+ corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ }
+
coordPairs[i] = static_cast<int32_t>(std::round(corrX));
coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
}
@@ -282,7 +282,30 @@
return OK;
}
-status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount) {
+status_t DistortionMapper::mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount,
+ bool clamp) const {
+ if (!mValidMapping) return INVALID_OPERATION;
+
+ float scaleX = mActiveWidth / mArrayWidth;
+ float scaleY = mActiveHeight / mArrayHeight;
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ float x = coordPairs[i];
+ float y = coordPairs[i + 1];
+ float corrX = x * scaleX;
+ float corrY = y * scaleY;
+ if (clamp) {
+ corrX = std::min(mActiveWidth - 1, std::max(0.f, corrX));
+ corrY = std::min(mActiveHeight - 1, std::max(0.f, corrY));
+ }
+ coordPairs[i] = static_cast<int32_t>(std::round(corrX));
+ coordPairs[i + 1] = static_cast<int32_t>(std::round(corrY));
+ }
+
+ return OK;
+}
+
+status_t DistortionMapper::mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
+ bool simple) {
if (!mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
// Map from (l, t, width, height) to (l, t, r, b)
@@ -293,7 +316,7 @@
rects[i + 1] + rects[i + 3]
};
- mapRawToCorrected(coords, 2);
+ mapRawToCorrected(coords, 2, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -305,14 +328,24 @@
return OK;
}
+status_t DistortionMapper::mapCorrectedToRaw(int32_t *coordPairs, int coordCount, bool clamp,
+ bool simple) const {
+ return mapCorrectedToRawImpl(coordPairs, coordCount, clamp, simple);
+}
+
template<typename T>
-status_t DistortionMapper::mapCorrectedToRaw(T *coordPairs, int coordCount) const {
+status_t DistortionMapper::mapCorrectedToRawImpl(T *coordPairs, int coordCount, bool clamp,
+ bool simple) const {
if (!mValidMapping) return INVALID_OPERATION;
+ if (simple) return mapCorrectedToRawImplSimple(coordPairs, coordCount, clamp);
+
+ float activeCx = mCx - mArrayDiffX;
+ float activeCy = mCy - mArrayDiffY;
for (int i = 0; i < coordCount * 2; i += 2) {
- // Move to normalized space
- float ywi = (coordPairs[i + 1] - mCy) * mInvFy;
- float xwi = (coordPairs[i] - mCx - mS * ywi) * mInvFx;
+ // Move to normalized space from active array space
+ float ywi = (coordPairs[i + 1] - activeCy) * mInvFy;
+ float xwi = (coordPairs[i] - activeCx - mS * ywi) * mInvFx;
// Apply distortion model to calculate raw image coordinates
float rSq = xwi * xwi + ywi * ywi;
float Fr = 1.f + (mK[0] * rSq) + (mK[1] * rSq * rSq) + (mK[2] * rSq * rSq * rSq);
@@ -321,6 +354,11 @@
// Move back to image space
float xr = mFx * xc + mS * yc + mCx;
float yr = mFy * yc + mCy;
+ // Clamp to within pre-correction active array
+ if (clamp) {
+ xr = std::min(mArrayWidth - 1, std::max(0.f, xr));
+ yr = std::min(mArrayHeight - 1, std::max(0.f, yr));
+ }
coordPairs[i] = static_cast<T>(std::round(xr));
coordPairs[i + 1] = static_cast<T>(std::round(yr));
@@ -329,10 +367,32 @@
return OK;
}
-template status_t DistortionMapper::mapCorrectedToRaw(int32_t*, int) const;
-template status_t DistortionMapper::mapCorrectedToRaw(float*, int) const;
+template<typename T>
+status_t DistortionMapper::mapCorrectedToRawImplSimple(T *coordPairs, int coordCount,
+ bool clamp) const {
+ if (!mValidMapping) return INVALID_OPERATION;
-status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount) const {
+ float scaleX = mArrayWidth / mActiveWidth;
+ float scaleY = mArrayHeight / mActiveHeight;
+ for (int i = 0; i < coordCount * 2; i += 2) {
+ float x = coordPairs[i];
+ float y = coordPairs[i + 1];
+ float rawX = x * scaleX;
+ float rawY = y * scaleY;
+ if (clamp) {
+ rawX = std::min(mArrayWidth - 1, std::max(0.f, rawX));
+ rawY = std::min(mArrayHeight - 1, std::max(0.f, rawY));
+ }
+ coordPairs[i] = static_cast<T>(std::round(rawX));
+ coordPairs[i + 1] = static_cast<T>(std::round(rawY));
+ }
+
+ return OK;
+}
+
+
+status_t DistortionMapper::mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
+ bool simple) const {
if (!mValidMapping) return INVALID_OPERATION;
for (int i = 0; i < rectCount * 4; i += 4) {
@@ -344,7 +404,7 @@
rects[i + 1] + rects[i + 3]
};
- mapCorrectedToRaw(coords, 2);
+ mapCorrectedToRaw(coords, 2, clamp, simple);
// Map back to (l, t, width, height)
rects[i] = coords[0];
@@ -380,7 +440,8 @@
};
mDistortedGrid[index].src = &mCorrectedGrid[index];
mDistortedGrid[index].coords = mCorrectedGrid[index].coords;
- status_t res = mapCorrectedToRaw(mDistortedGrid[index].coords.data(), 4);
+ status_t res = mapCorrectedToRawImpl(mDistortedGrid[index].coords.data(), 4,
+ /*clamp*/false, /*simple*/false);
if (res != OK) return res;
}
}
diff --git a/services/camera/libcameraservice/device3/DistortionMapper.h b/services/camera/libcameraservice/device3/DistortionMapper.h
index 00cbd32..4c0a1a6 100644
--- a/services/camera/libcameraservice/device3/DistortionMapper.h
+++ b/services/camera/libcameraservice/device3/DistortionMapper.h
@@ -73,8 +73,11 @@
*
* coordPairs: A pointer to an array of consecutive (x,y) points
* coordCount: Number of (x,y) pairs to transform
+ * clamp: Whether to clamp the result to the bounds of the active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawToCorrected(int32_t *coordPairs, int coordCount);
+ status_t mapRawToCorrected(int32_t *coordPairs, int coordCount, bool clamp,
+ bool simple = true);
/**
* Transform from distorted (original) to corrected (warped) coordinates.
@@ -82,8 +85,11 @@
*
* rects: A pointer to an array of consecutive (x,y, w, h) rectangles
* rectCount: Number of rectangles to transform
+ * clamp: Whether to clamp the result to the bounds of the active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapRawRectToCorrected(int32_t *rects, int rectCount);
+ status_t mapRawRectToCorrected(int32_t *rects, int rectCount, bool clamp,
+ bool simple = true);
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -91,9 +97,11 @@
*
* coordPairs: A pointer to an array of consecutive (x,y) points
* coordCount: Number of (x,y) pairs to transform
+ * clamp: Whether to clamp the result to the bounds of the precorrection active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- template<typename T>
- status_t mapCorrectedToRaw(T* coordPairs, int coordCount) const;
+ status_t mapCorrectedToRaw(int32_t* coordPairs, int coordCount, bool clamp,
+ bool simple = true) const;
/**
* Transform from corrected (warped) to distorted (original) coordinates.
@@ -101,8 +109,11 @@
*
* rects: A pointer to an array of consecutive (x,y, w, h) rectangles
* rectCount: Number of rectangles to transform
+ * clamp: Whether to clamp the result to the bounds of the precorrection active array
+ * simple: Whether to do complex correction or just a simple linear map
*/
- status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount) const;
+ status_t mapCorrectedRectToRaw(int32_t *rects, int rectCount, bool clamp,
+ bool simple = true) const;
struct GridQuad {
// Source grid quad, or null
@@ -150,8 +161,18 @@
// Only capture result
static const std::array<uint32_t, 1> kResultRectsToCorrect;
- // Only for capture results
- static const std::array<uint32_t, 2> kResultPointsToCorrect;
+ // Only for capture results; don't clamp
+ static const std::array<uint32_t, 2> kResultPointsToCorrectNoClamp;
+
+ // Single implementation for various mapCorrectedToRaw methods
+ template<typename T>
+ status_t mapCorrectedToRawImpl(T* coordPairs, int coordCount, bool clamp, bool simple) const;
+
+ // Simple linear interpolation option
+ template<typename T>
+ status_t mapCorrectedToRawImplSimple(T* coordPairs, int coordCount, bool clamp) const;
+
+ status_t mapRawToCorrectedSimple(int32_t *coordPairs, int coordCount, bool clamp) const;
// Utility to create reverse mapping grids
status_t buildGrids();
@@ -168,9 +189,11 @@
float mK[5];
// pre-correction active array dimensions
- int mArrayWidth, mArrayHeight;
+ float mArrayWidth, mArrayHeight;
// active array dimensions
- int mActiveWidth, mActiveHeight;
+ float mActiveWidth, mActiveHeight;
+ // corner offsets between pre-correction and active arrays
+ float mArrayDiffX, mArrayDiffY;
std::vector<GridQuad> mCorrectedGrid;
std::vector<GridQuad> mDistortedGrid;
diff --git a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
index b489931..2a689c6 100644
--- a/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
+++ b/services/camera/libcameraservice/tests/DistortionMapperTest.cpp
@@ -30,6 +30,7 @@
int32_t testActiveArray[] = {100, 100, 1000, 750};
+int32_t testPreCorrActiveArray[] = {90, 90, 1020, 770};
float testICal[] = { 1000.f, 1000.f, 500.f, 500.f, 0.f };
@@ -45,14 +46,19 @@
};
-void setupTestMapper(DistortionMapper *m, float distortion[5]) {
+void setupTestMapper(DistortionMapper *m,
+ float distortion[5], float intrinsics[5],
+ int32_t activeArray[4], int32_t preCorrectionActiveArray[4]) {
CameraMetadata deviceInfo;
deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
- testActiveArray, 4);
+ preCorrectionActiveArray, 4);
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
+ activeArray, 4);
deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
- testICal, 5);
+ intrinsics, 5);
deviceInfo.update(ANDROID_LENS_DISTORTION,
distortion, 5);
@@ -89,6 +95,9 @@
ASSERT_FALSE(m.calibrationValid());
deviceInfo.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ testPreCorrActiveArray, 4);
+
+ deviceInfo.update(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE,
testActiveArray, 4);
deviceInfo.update(ANDROID_LENS_INTRINSIC_CALIBRATION,
@@ -118,17 +127,19 @@
status_t res;
DistortionMapper m;
- setupTestMapper(&m, identityDistortion);
+ setupTestMapper(&m, identityDistortion, testICal,
+ /*activeArray*/ testActiveArray,
+ /*preCorrectionActiveArray*/ testActiveArray);
auto coords = basicCoords;
- res = m.mapCorrectedToRaw(coords.data(), 5);
+ res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_EQ(coords[i], basicCoords[i]);
}
- res = m.mapRawToCorrected(coords.data(), 5);
+ res = m.mapRawToCorrected(coords.data(), 5, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < coords.size(); i++) {
@@ -137,18 +148,18 @@
std::array<int32_t, 8> rects = {
0, 0, 100, 100,
- testActiveArray[2] - 100, testActiveArray[3]-100, 100, 100
+ testActiveArray[2] - 101, testActiveArray[3] - 101, 100, 100
};
auto rectsOrig = rects;
- res = m.mapCorrectedRectToRaw(rects.data(), 2);
+ res = m.mapCorrectedRectToRaw(rects.data(), 2, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
EXPECT_EQ(rects[i], rectsOrig[i]);
}
- res = m.mapRawRectToCorrected(rects.data(), 2);
+ res = m.mapRawRectToCorrected(rects.data(), 2, /*clamp*/true);
ASSERT_EQ(res, OK);
for (size_t i = 0; i < rects.size(); i++) {
@@ -156,23 +167,39 @@
}
}
-TEST(DistortionMapperTest, LargeTransform) {
+TEST(DistortionMapperTest, SimpleTransform) {
+ status_t res;
+
+ DistortionMapper m;
+ setupTestMapper(&m, identityDistortion, testICal,
+ /*activeArray*/ testActiveArray,
+ /*preCorrectionActiveArray*/ testPreCorrActiveArray);
+
+ auto coords = basicCoords;
+ res = m.mapCorrectedToRaw(coords.data(), 5, /*clamp*/true, /*simple*/true);
+ ASSERT_EQ(res, OK);
+
+ ASSERT_EQ(coords[0], 0); ASSERT_EQ(coords[1], 0);
+ ASSERT_EQ(coords[2], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[3], 0);
+ ASSERT_EQ(coords[4], testPreCorrActiveArray[2] - 1); ASSERT_EQ(coords[5], testPreCorrActiveArray[3] - 1);
+ ASSERT_EQ(coords[6], 0); ASSERT_EQ(coords[7], testPreCorrActiveArray[3] - 1);
+ ASSERT_EQ(coords[8], testPreCorrActiveArray[2] / 2); ASSERT_EQ(coords[9], testPreCorrActiveArray[3] / 2);
+}
+
+
+void RandomTransformTest(::testing::Test *test,
+ int32_t* activeArray, DistortionMapper &m, bool clamp, bool simple) {
status_t res;
constexpr int maxAllowedPixelError = 2; // Maximum per-pixel error allowed
constexpr int bucketsPerPixel = 3; // Histogram granularity
unsigned int seed = 1234; // Ensure repeatability for debugging
- const size_t coordCount = 1e6; // Number of random test points
-
- float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
-
- DistortionMapper m;
- setupTestMapper(&m, bigDistortion);
+ const size_t coordCount = 1e5; // Number of random test points
std::default_random_engine gen(seed);
- std::uniform_int_distribution<int> x_dist(0, testActiveArray[2] - 1);
- std::uniform_int_distribution<int> y_dist(0, testActiveArray[3] - 1);
+ std::uniform_int_distribution<int> x_dist(0, activeArray[2] - 1);
+ std::uniform_int_distribution<int> y_dist(0, activeArray[3] - 1);
std::vector<int32_t> randCoords(coordCount * 2);
@@ -186,12 +213,12 @@
auto origCoords = randCoords;
base::Timer correctedToRawTimer;
- res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2);
+ res = m.mapCorrectedToRaw(randCoords.data(), randCoords.size() / 2, clamp, simple);
auto correctedToRawDurationMs = correctedToRawTimer.duration();
EXPECT_EQ(res, OK);
base::Timer rawToCorrectedTimer;
- res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2);
+ res = m.mapRawToCorrected(randCoords.data(), randCoords.size() / 2, clamp, simple);
auto rawToCorrectedDurationMs = rawToCorrectedTimer.duration();
EXPECT_EQ(res, OK);
@@ -202,9 +229,9 @@
(std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(
rawToCorrectedDurationMs) / (randCoords.size() / 2) ).count();
- RecordProperty("CorrectedToRawDurationPerCoordUs",
+ test->RecordProperty("CorrectedToRawDurationPerCoordUs",
base::StringPrintf("%f", correctedToRawDurationPerCoordUs));
- RecordProperty("RawToCorrectedDurationPerCoordUs",
+ test->RecordProperty("RawToCorrectedDurationPerCoordUs",
base::StringPrintf("%f", rawToCorrectedDurationPerCoordUs));
// Calculate mapping errors after round trip
@@ -239,17 +266,61 @@
}
float rmsError = std::sqrt(totalErrorSq / randCoords.size());
- RecordProperty("RmsError", base::StringPrintf("%f", rmsError));
+ test->RecordProperty("RmsError", base::StringPrintf("%f", rmsError));
for (size_t i = 0; i < histogram.size(); i++) {
std::string label = base::StringPrintf("HistogramBin[%f,%f)",
(float)i/bucketsPerPixel, (float)(i + 1)/bucketsPerPixel);
- RecordProperty(label, histogram[i]);
+ test->RecordProperty(label, histogram[i]);
}
- RecordProperty("HistogramOutOfRange", outOfHistogram);
+ test->RecordProperty("HistogramOutOfRange", outOfHistogram);
+}
+
+// Test a realistic distortion function with matching calibration values, enforcing
+// clamping.
+TEST(DistortionMapperTest, DISABLED_SmallTransform) {
+ int32_t activeArray[] = {0, 8, 3278, 2450};
+ int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464};
+
+ float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431};
+ float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000};
+
+ DistortionMapper m;
+ setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray);
+
+ RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/false);
+}
+
+// Test a realistic distortion function with matching calibration values, enforcing
+// clamping, but using the simple linear transform
+TEST(DistortionMapperTest, SmallSimpleTransform) {
+ int32_t activeArray[] = {0, 8, 3278, 2450};
+ int32_t preCorrectionActiveArray[] = {0, 0, 3280, 2464};
+
+ float distortion[] = {0.06875723, -0.13922249, 0.02818312, -0.00032781, -0.00025431};
+ float intrinsics[] = {1812.50000000, 1812.50000000, 1645.59533691, 1229.23229980, 0.00000000};
+
+ DistortionMapper m;
+ setupTestMapper(&m, distortion, intrinsics, activeArray, preCorrectionActiveArray);
+
+ RandomTransformTest(this, activeArray, m, /*clamp*/true, /*simple*/true);
+}
+
+// Test a very large distortion function; the regions aren't valid for such a big transform,
+// so disable clamping. This test is just to verify round-trip math accuracy for big transforms
+TEST(DistortionMapperTest, LargeTransform) {
+ float bigDistortion[] = {0.1, -0.003, 0.004, 0.02, 0.01};
+
+ DistortionMapper m;
+ setupTestMapper(&m, bigDistortion, testICal,
+ /*activeArray*/testActiveArray,
+ /*preCorrectionActiveArray*/testPreCorrActiveArray);
+
+ RandomTransformTest(this, testActiveArray, m, /*clamp*/false, /*simple*/false);
}
// Compare against values calculated by OpenCV
// undistortPoints() method, which is the same as mapRawToCorrected
+// Ignore clamping
// See script DistortionMapperComp.py
#include "DistortionMapperTest_OpenCvData.h"
@@ -262,11 +333,14 @@
const int32_t maxSqError = 2;
DistortionMapper m;
- setupTestMapper(&m, bigDistortion);
+ setupTestMapper(&m, bigDistortion, testICal,
+ /*activeArray*/testActiveArray,
+ /*preCorrectionActiveArray*/testActiveArray);
using namespace openCvData;
- res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2);
+ res = m.mapRawToCorrected(rawCoords.data(), rawCoords.size() / 2, /*clamp*/false,
+ /*simple*/false);
for (size_t i = 0; i < rawCoords.size(); i+=2) {
int32_t dist = (rawCoords[i] - expCoords[i]) * (rawCoords[i] - expCoords[i]) +
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
index 2eeb7fa..5b20e61 100644
--- a/services/mediaanalytics/Android.mk
+++ b/services/mediaanalytics/Android.mk
@@ -34,8 +34,7 @@
$(TOP)/frameworks/av/include/camera \
$(TOP)/frameworks/native/include/media/openmax \
$(TOP)/frameworks/native/include/media/hardware \
- $(TOP)/external/tremolo/Tremolo \
- libcore/include
+ $(TOP)/external/tremolo/Tremolo
LOCAL_MODULE:= mediametrics
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 6ec8895..edf4dab 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -55,4 +55,8 @@
getdents64: 1
getrandom: 1
+# Used by UBSan diagnostic messages
+readlink: 1
+open: 1
+
@include /system/etc/seccomp_policy/crash_dump.arm.policy
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index bbbe552..6e6b276 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -55,4 +55,8 @@
getpid: 1
gettid: 1
+# Used by UBSan diagnostic messages
+readlink: 1
+open: 1
+
@include /system/etc/seccomp_policy/crash_dump.x86.policy