Merge "Only read timestamps from HAL if not FastCapture" into nyc-mr1-dev
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index c78fc5d..373b94e 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -20,8 +20,9 @@
#include <utils/Log.h>
#include <utils/Errors.h>
-#include <camera/CameraMetadata.h>
#include <binder/Parcel.h>
+#include <camera/CameraMetadata.h>
+#include <camera/VendorTagDescriptor.h>
namespace android {
@@ -277,6 +278,18 @@
return updateImpl(tag, (const void*)string.string(), string.size() + 1);
}
+status_t CameraMetadata::update(const camera_metadata_ro_entry &entry) {
+ status_t res;
+ if (mLocked) {
+ ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
+ return INVALID_OPERATION;
+ }
+ if ( (res = checkType(entry.tag, entry.type)) != OK) {
+ return res;
+ }
+ return updateImpl(entry.tag, (const void*)entry.data.u8, entry.count);
+}
+
status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
size_t data_count) {
status_t res;
@@ -681,4 +694,99 @@
mBuffer = otherBuf;
}
+status_t CameraMetadata::getTagFromName(const char *name,
+ const VendorTagDescriptor* vTags, uint32_t *tag) {
+
+ if (name == nullptr || tag == nullptr) return BAD_VALUE;
+
+ size_t nameLength = strlen(name);
+
+ const SortedVector<String8> *vendorSections;
+ size_t vendorSectionCount = 0;
+
+ if (vTags != NULL) {
+ vendorSections = vTags->getAllSectionNames();
+ vendorSectionCount = vendorSections->size();
+ }
+
+ // First, find the section by the longest string match
+ const char *section = NULL;
+ size_t sectionIndex = 0;
+ size_t sectionLength = 0;
+ size_t totalSectionCount = ANDROID_SECTION_COUNT + vendorSectionCount;
+ for (size_t i = 0; i < totalSectionCount; ++i) {
+
+ const char *str = (i < ANDROID_SECTION_COUNT) ? camera_metadata_section_names[i] :
+ (*vendorSections)[i - ANDROID_SECTION_COUNT].string();
+
+ ALOGV("%s: Trying to match against section '%s'", __FUNCTION__, str);
+
+ if (strstr(name, str) == name) { // name begins with the section name
+ size_t strLength = strlen(str);
+
+ ALOGV("%s: Name begins with section name", __FUNCTION__);
+
+ // section name is the longest we've found so far
+ if (section == NULL || sectionLength < strLength) {
+ section = str;
+ sectionIndex = i;
+ sectionLength = strLength;
+
+ ALOGV("%s: Found new best section (%s)", __FUNCTION__, section);
+ }
+ }
+ }
+
+ // TODO: Make above get_camera_metadata_section_from_name ?
+
+ if (section == NULL) {
+ return NAME_NOT_FOUND;
+ } else {
+ ALOGV("%s: Found matched section '%s' (%zu)",
+ __FUNCTION__, section, sectionIndex);
+ }
+
+ // Get the tag name component of the name
+ const char *nameTagName = name + sectionLength + 1; // x.y.z -> z
+ if (sectionLength + 1 >= nameLength) {
+ return BAD_VALUE;
+ }
+
+ // Match rest of name against the tag names in that section only
+ uint32_t candidateTag = 0;
+ if (sectionIndex < ANDROID_SECTION_COUNT) {
+ // Match built-in tags (typically android.*)
+ uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
+ tagBegin = camera_metadata_section_bounds[sectionIndex][0];
+ tagEnd = camera_metadata_section_bounds[sectionIndex][1];
+
+ for (candidateTag = tagBegin; candidateTag < tagEnd; ++candidateTag) {
+ const char *tagName = get_camera_metadata_tag_name(candidateTag);
+
+ if (strcmp(nameTagName, tagName) == 0) {
+ ALOGV("%s: Found matched tag '%s' (%d)",
+ __FUNCTION__, tagName, candidateTag);
+ break;
+ }
+ }
+
+ if (candidateTag == tagEnd) {
+ return NAME_NOT_FOUND;
+ }
+ } else if (vTags != NULL) {
+ // Match vendor tags (typically com.*)
+ const String8 sectionName(section);
+ const String8 tagName(nameTagName);
+
+ status_t res = OK;
+ if ((res = vTags->lookupTag(tagName, sectionName, &candidateTag)) != OK) {
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ *tag = candidateTag;
+ return OK;
+}
+
+
}; // namespace android
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 5538da9..02ece14 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -280,8 +280,8 @@
return res;
}
-SortedVector<String8> VendorTagDescriptor::getAllSectionNames() const {
- return mSections;
+const SortedVector<String8>* VendorTagDescriptor::getAllSectionNames() const {
+ return &mSections;
}
status_t VendorTagDescriptor::lookupTag(String8 name, String8 section, /*out*/uint32_t* tag) const {
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index c1dad2c..fea5a1d 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -3,5 +3,4 @@
user cameraserver
group audio camera input drmrpc
ioprio rt 4
- writepid /dev/cpuset/camera-daemon/tasks
- writepid /dev/stune/top-app/tasks
+ writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
diff --git a/include/camera/CameraMetadata.h b/include/camera/CameraMetadata.h
index 28f47a1..d284477 100644
--- a/include/camera/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -18,12 +18,15 @@
#define ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
#include "system/camera_metadata.h"
+
#include <utils/String8.h>
#include <utils/Vector.h>
#include <binder/Parcelable.h>
namespace android {
+class VendorTagDescriptor;
+
/**
* A convenience wrapper around the C-based camera_metadata_t library.
*/
@@ -137,6 +140,8 @@
const camera_metadata_rational_t *data, size_t data_count);
status_t update(uint32_t tag,
const String8 &string);
+ status_t update(const camera_metadata_ro_entry &entry);
+
template<typename T>
status_t update(uint32_t tag, Vector<T> data) {
@@ -206,6 +211,15 @@
static status_t writeToParcel(Parcel &parcel,
const camera_metadata_t* metadata);
+ /**
+ * Find tag id for a given tag name, also checking vendor tags if available.
+ * On success, returns OK and writes the tag id into tag.
+ *
+ * This is a slow method.
+ */
+ static status_t getTagFromName(const char *name,
+ const VendorTagDescriptor* vTags, uint32_t *tag);
+
private:
camera_metadata_t *mBuffer;
mutable bool mLocked;
diff --git a/include/camera/VendorTagDescriptor.h b/include/camera/VendorTagDescriptor.h
index 4c1cab6..60e2d2d 100644
--- a/include/camera/VendorTagDescriptor.h
+++ b/include/camera/VendorTagDescriptor.h
@@ -81,8 +81,10 @@
/**
* Convenience method to get a vector containing all vendor tag
* sections, or an empty vector if none are defined.
+ * The pointer is valid for the lifetime of the VendorTagDescriptor,
+ * or until readParcel or copyFrom is invoked.
*/
- SortedVector<String8> getAllSectionNames() const;
+ const SortedVector<String8>* getAllSectionNames() const;
/**
* Lookup the tag id for a given tag name and section.
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 2409157..80f78b6 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -4,4 +4,4 @@
# media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 32f86df..d790821 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1845,7 +1845,7 @@
mCallbackData->setOutput(this);
}
delete newcbd;
- return OK;
+ return updateTrack();
}
}
@@ -1867,17 +1867,26 @@
mFrameSize = t->frameSize();
mTrack = t;
+ return updateTrack();
+}
+
+status_t MediaPlayerService::AudioOutput::updateTrack() {
+ if (mTrack == NULL) {
+ return NO_ERROR;
+ }
+
status_t res = NO_ERROR;
// Note some output devices may give us a direct track even though we don't specify it.
// Example: Line application b/17459982.
- if ((t->getFlags() & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
- res = t->setPlaybackRate(mPlaybackRate);
+ if ((mTrack->getFlags()
+ & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT)) == 0) {
+ res = mTrack->setPlaybackRate(mPlaybackRate);
if (res == NO_ERROR) {
- t->setAuxEffectSendLevel(mSendLevel);
- res = t->attachAuxEffect(mAuxEffectId);
+ mTrack->setAuxEffectSendLevel(mSendLevel);
+ res = mTrack->attachAuxEffect(mAuxEffectId);
}
}
- ALOGV("open() DONE status %d", res);
+ ALOGV("updateTrack() DONE status %d", res);
return res;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 01977f5..4643f20 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -135,6 +135,7 @@
int event, void *me, void *info);
void deleteRecycledTrack_l();
void close_l();
+ status_t updateTrack();
sp<AudioTrack> mTrack;
sp<AudioTrack> mRecycledTrack;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 1b7dff5..8a305de 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -318,10 +318,16 @@
size_t numTracks = mTracks.size();
size_t preparedCount, underflowCount, overflowCount, startCount;
preparedCount = underflowCount = overflowCount = startCount = 0;
- for (size_t i = 0; i < numTracks; ++i) {
+
+ size_t count = numTracks;
+ for (size_t i = 0; i < count; ++i) {
status_t finalResult;
TrackInfo *info = &mTracks.editItemAt(i);
sp<AnotherPacketSource> src = info->mSource;
+ if (src == NULL) {
+ --numTracks;
+ continue;
+ }
int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult);
// isFinished when duration is 0 checks for EOS result only
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index bbc75d6..21a5faa 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -372,6 +372,7 @@
mInitCheck(NO_INIT),
mHasVideo(false),
mHeaderTimescale(0),
+ mIsQT(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData),
@@ -915,6 +916,7 @@
case FOURCC('s', 'i', 'n', 'f'):
case FOURCC('s', 'c', 'h', 'i'):
case FOURCC('e', 'd', 't', 's'):
+ case FOURCC('w', 'a', 'v', 'e'):
{
if (chunk_type == FOURCC('m', 'o', 'o', 'f') && !mMoofFound) {
// store the offset of the first segment
@@ -1372,6 +1374,13 @@
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
{
+ if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')
+ && depth >= 1 && mPath[depth - 1] == FOURCC('w', 'a', 'v', 'e')) {
+ // Ignore mp4a embedded in QT wave atom
+ *offset += chunk_size;
+ break;
+ }
+
uint8_t buffer[8 + 20];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
// Basic AudioSampleEntry size.
@@ -1384,6 +1393,7 @@
}
uint16_t data_ref_index __unused = U16_AT(&buffer[6]);
+ uint16_t version = U16_AT(&buffer[8]);
uint32_t num_channels = U16_AT(&buffer[16]);
uint16_t sample_size = U16_AT(&buffer[18]);
@@ -1392,6 +1402,42 @@
if (mLastTrack == NULL)
return ERROR_MALFORMED;
+ off64_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+
+ if (mIsQT && chunk_type == FOURCC('m', 'p', '4', 'a')) {
+ if (version == 1) {
+ if (mDataSource->readAt(*offset, buffer, 16) < 16) {
+ return ERROR_IO;
+ }
+
+#if 0
+ U32_AT(buffer); // samples per packet
+ U32_AT(&buffer[4]); // bytes per packet
+ U32_AT(&buffer[8]); // bytes per frame
+ U32_AT(&buffer[12]); // bytes per sample
+#endif
+ *offset += 16;
+ } else if (version == 2) {
+ uint8_t v2buffer[36];
+ if (mDataSource->readAt(*offset, v2buffer, 36) < 36) {
+ return ERROR_IO;
+ }
+
+#if 0
+ U32_AT(v2buffer); // size of struct only
+ sample_rate = (uint32_t)U64_AT(&v2buffer[4]); // audio sample rate
+ num_channels = U32_AT(&v2buffer[12]); // num audio channels
+ U32_AT(&v2buffer[16]); // always 0x7f000000
+ sample_size = (uint16_t)U32_AT(&v2buffer[20]); // const bits per channel
+ U32_AT(&v2buffer[24]); // format specifc flags
+ U32_AT(&v2buffer[28]); // const bytes per audio packet
+ U32_AT(&v2buffer[32]); // const LPCM frames per audio packet
+#endif
+ *offset += 36;
+ }
+ }
+
if (chunk_type != FOURCC('e', 'n', 'c', 'a')) {
// if the chunk type is enca, we'll get the type from the sinf/frma box later
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
@@ -1402,8 +1448,6 @@
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
- off64_t stop_offset = *offset + chunk_size;
- *offset = data_offset + sizeof(buffer);
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
@@ -2240,6 +2284,38 @@
return UNKNOWN_ERROR; // stop parsing after sidx
}
+ case FOURCC('f', 't', 'y', 'p'):
+ {
+ if (chunk_data_size < 8 || depth != 0) {
+ return ERROR_MALFORMED;
+ }
+
+ off64_t stop_offset = *offset + chunk_size;
+ uint32_t numCompatibleBrands = (chunk_data_size - 8) / 4;
+ for (size_t i = 0; i < numCompatibleBrands + 2; ++i) {
+ if (i == 1) {
+ // Skip this index, it refers to the minorVersion,
+ // not a brand.
+ continue;
+ }
+
+ uint32_t brand;
+ if (mDataSource->readAt(data_offset + 4 * i, &brand, 4) < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ brand = ntohl(brand);
+ if (brand == FOURCC('q', 't', ' ', ' ')) {
+ mIsQT = true;
+ break;
+ }
+ }
+
+ *offset = stop_offset;
+
+ break;
+ }
+
default:
{
// check if we're parsing 'ilst' for meta keys
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index ec534ef..66c4ffd 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -30,6 +30,8 @@
#include <utils/Log.h>
+#include <functional>
+
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -122,18 +124,18 @@
};
// A helper class to handle faster write box with table entries
- template<class TYPE>
+ template<class TYPE, unsigned ENTRY_SIZE>
+ // ENTRY_SIZE: # of values in each entry
struct ListTableEntries {
- ListTableEntries(uint32_t elementCapacity, uint32_t entryCapacity)
+ static_assert(ENTRY_SIZE > 0, "ENTRY_SIZE must be positive");
+ ListTableEntries(uint32_t elementCapacity)
: mElementCapacity(elementCapacity),
- mEntryCapacity(entryCapacity),
mTotalNumTableEntries(0),
mNumValuesInCurrEntry(0),
mCurrTableEntriesElement(NULL) {
CHECK_GT(mElementCapacity, 0);
- CHECK_GT(mEntryCapacity, 0);
// Ensure no integer overflow on allocation in add().
- CHECK_LT(mEntryCapacity, UINT32_MAX / mElementCapacity);
+ CHECK_LT(ENTRY_SIZE, UINT32_MAX / mElementCapacity);
}
// Free the allocated memory.
@@ -150,10 +152,10 @@
// @arg value must be in network byte order
// @arg pos location the value must be in.
void set(const TYPE& value, uint32_t pos) {
- CHECK_LT(pos, mTotalNumTableEntries * mEntryCapacity);
+ CHECK_LT(pos, mTotalNumTableEntries * ENTRY_SIZE);
typename List<TYPE *>::iterator it = mTableEntryList.begin();
- uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
+ uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
while (it != mTableEntryList.end() && iterations > 0) {
++it;
--iterations;
@@ -161,7 +163,7 @@
CHECK(it != mTableEntryList.end());
CHECK_EQ(iterations, 0);
- (*it)[(pos % (mElementCapacity * mEntryCapacity))] = value;
+ (*it)[(pos % (mElementCapacity * ENTRY_SIZE))] = value;
}
// Get the value at the given position by the given value.
@@ -169,12 +171,12 @@
// @arg pos location the value must be in.
// @return true if a value is found.
bool get(TYPE& value, uint32_t pos) const {
- if (pos >= mTotalNumTableEntries * mEntryCapacity) {
+ if (pos >= mTotalNumTableEntries * ENTRY_SIZE) {
return false;
}
typename List<TYPE *>::iterator it = mTableEntryList.begin();
- uint32_t iterations = (pos / (mElementCapacity * mEntryCapacity));
+ uint32_t iterations = (pos / (mElementCapacity * ENTRY_SIZE));
while (it != mTableEntryList.end() && iterations > 0) {
++it;
--iterations;
@@ -182,27 +184,42 @@
CHECK(it != mTableEntryList.end());
CHECK_EQ(iterations, 0);
- value = (*it)[(pos % (mElementCapacity * mEntryCapacity))];
+ value = (*it)[(pos % (mElementCapacity * ENTRY_SIZE))];
return true;
}
+ // adjusts all values by |adjust(value)|
+ void adjustEntries(
+ std::function<void(size_t /* ix */, TYPE(& /* entry */)[ENTRY_SIZE])> update) {
+ size_t nEntries = mTotalNumTableEntries + mNumValuesInCurrEntry / ENTRY_SIZE;
+ size_t ix = 0;
+ for (TYPE *entryArray : mTableEntryList) {
+ size_t num = std::min(nEntries, (size_t)mElementCapacity);
+ for (size_t i = 0; i < num; ++i) {
+ update(ix++, (TYPE(&)[ENTRY_SIZE])(*entryArray));
+ entryArray += ENTRY_SIZE;
+ }
+ nEntries -= num;
+ }
+ }
+
// Store a single value.
// @arg value must be in network byte order.
void add(const TYPE& value) {
CHECK_LT(mNumValuesInCurrEntry, mElementCapacity);
uint32_t nEntries = mTotalNumTableEntries % mElementCapacity;
- uint32_t nValues = mNumValuesInCurrEntry % mEntryCapacity;
+ uint32_t nValues = mNumValuesInCurrEntry % ENTRY_SIZE;
if (nEntries == 0 && nValues == 0) {
- mCurrTableEntriesElement = new TYPE[mEntryCapacity * mElementCapacity];
+ mCurrTableEntriesElement = new TYPE[ENTRY_SIZE * mElementCapacity];
CHECK(mCurrTableEntriesElement != NULL);
mTableEntryList.push_back(mCurrTableEntriesElement);
}
- uint32_t pos = nEntries * mEntryCapacity + nValues;
+ uint32_t pos = nEntries * ENTRY_SIZE + nValues;
mCurrTableEntriesElement[pos] = value;
++mNumValuesInCurrEntry;
- if ((mNumValuesInCurrEntry % mEntryCapacity) == 0) {
+ if ((mNumValuesInCurrEntry % ENTRY_SIZE) == 0) {
++mTotalNumTableEntries;
mNumValuesInCurrEntry = 0;
}
@@ -213,17 +230,17 @@
// 2. followed by the values in the table enties in order
// @arg writer the writer to actual write to the storage
void write(MPEG4Writer *writer) const {
- CHECK_EQ(mNumValuesInCurrEntry % mEntryCapacity, 0);
+ CHECK_EQ(mNumValuesInCurrEntry % ENTRY_SIZE, 0);
uint32_t nEntries = mTotalNumTableEntries;
writer->writeInt32(nEntries);
for (typename List<TYPE *>::iterator it = mTableEntryList.begin();
it != mTableEntryList.end(); ++it) {
CHECK_GT(nEntries, 0);
if (nEntries >= mElementCapacity) {
- writer->write(*it, sizeof(TYPE) * mEntryCapacity, mElementCapacity);
+ writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, mElementCapacity);
nEntries -= mElementCapacity;
} else {
- writer->write(*it, sizeof(TYPE) * mEntryCapacity, nEntries);
+ writer->write(*it, sizeof(TYPE) * ENTRY_SIZE, nEntries);
break;
}
}
@@ -234,9 +251,8 @@
private:
uint32_t mElementCapacity; // # entries in an element
- uint32_t mEntryCapacity; // # of values in each entry
uint32_t mTotalNumTableEntries;
- uint32_t mNumValuesInCurrEntry; // up to mEntryCapacity
+ uint32_t mNumValuesInCurrEntry; // up to ENTRY_SIZE
TYPE *mCurrTableEntriesElement;
mutable List<TYPE *> mTableEntryList;
@@ -271,14 +287,14 @@
List<MediaBuffer *> mChunkSamples;
bool mSamplesHaveSameSize;
- ListTableEntries<uint32_t> *mStszTableEntries;
+ ListTableEntries<uint32_t, 1> *mStszTableEntries;
- ListTableEntries<uint32_t> *mStcoTableEntries;
- ListTableEntries<off64_t> *mCo64TableEntries;
- ListTableEntries<uint32_t> *mStscTableEntries;
- ListTableEntries<uint32_t> *mStssTableEntries;
- ListTableEntries<uint32_t> *mSttsTableEntries;
- ListTableEntries<uint32_t> *mCttsTableEntries;
+ ListTableEntries<uint32_t, 1> *mStcoTableEntries;
+ ListTableEntries<off64_t, 1> *mCo64TableEntries;
+ ListTableEntries<uint32_t, 3> *mStscTableEntries;
+ ListTableEntries<uint32_t, 1> *mStssTableEntries;
+ ListTableEntries<uint32_t, 2> *mSttsTableEntries;
+ ListTableEntries<uint32_t, 2> *mCttsTableEntries;
int64_t mMinCttsOffsetTimeUs;
int64_t mMaxCttsOffsetTimeUs;
@@ -1524,13 +1540,13 @@
mTrackDurationUs(0),
mEstimatedTrackSizeBytes(0),
mSamplesHaveSameSize(true),
- mStszTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mStcoTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mCo64TableEntries(new ListTableEntries<off64_t>(1000, 1)),
- mStscTableEntries(new ListTableEntries<uint32_t>(1000, 3)),
- mStssTableEntries(new ListTableEntries<uint32_t>(1000, 1)),
- mSttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
- mCttsTableEntries(new ListTableEntries<uint32_t>(1000, 2)),
+ mStszTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mStcoTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mCo64TableEntries(new ListTableEntries<off64_t, 1>(1000)),
+ mStscTableEntries(new ListTableEntries<uint32_t, 3>(1000)),
+ mStssTableEntries(new ListTableEntries<uint32_t, 1>(1000)),
+ mSttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
+ mCttsTableEntries(new ListTableEntries<uint32_t, 2>(1000)),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
mGotAllCodecSpecificData(false),
@@ -3382,10 +3398,12 @@
mOwner->beginBox("ctts");
mOwner->writeInt32(0); // version=0, flags=0
- uint32_t duration;
- CHECK(mCttsTableEntries->get(duration, 1));
- duration = htonl(duration); // Back host byte order
- mCttsTableEntries->set(htonl(duration + getStartTimeOffsetScaledTime() - mMinCttsOffsetTimeUs), 1);
+ uint32_t delta = mMinCttsOffsetTimeUs - getStartTimeOffsetScaledTime();
+ mCttsTableEntries->adjustEntries([delta](size_t /* ix */, uint32_t (&value)[2]) {
+ // entries are <count, ctts> pairs; adjust only ctts
+ uint32_t duration = htonl(value[1]); // back to host byte order
+ value[1] = htonl(duration - delta);
+ });
mCttsTableEntries->write(mOwner);
mOwner->endBox(); // ctts
}
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 18b14e1..37c35e3 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -95,6 +95,7 @@
status_t mInitCheck;
bool mHasVideo;
uint32_t mHeaderTimescale;
+ bool mIsQT;
Track *mFirstTrack, *mLastTrack;
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 60c1e2e..13afd45 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -469,6 +469,13 @@
CHECK_EQ((int)port->mTransition, (int)PortInfo::NONE);
CHECK(port->mDef.bEnabled == !enable);
+ if (port->mDef.eDir != OMX_DirOutput) {
+ ALOGE("Port enable/disable allowed only on output ports.");
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ android_errorWriteLog(0x534e4554, "29421804");
+ return;
+ }
+
if (!enable) {
port->mDef.bEnabled = OMX_FALSE;
port->mTransition = PortInfo::DISABLING;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 47573c3..8b0331a 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -214,11 +214,13 @@
char key[32];
snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
-
- CHECK(findAttribute(index, key, desc));
-
- snprintf(key, sizeof(key), "a=fmtp:%lu", x);
- if (!findAttribute(index, key, params)) {
+ if (findAttribute(index, key, desc)) {
+ snprintf(key, sizeof(key), "a=fmtp:%lu", x);
+ if (!findAttribute(index, key, params)) {
+ params->clear();
+ }
+ } else {
+ desc->clear();
params->clear();
}
}
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index b777d5c..f6c325c 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -3,4 +3,4 @@
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 149b492..79f4a66 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -3054,7 +3054,7 @@
}
} else {
if (fd >= 0) {
- dprintf(fd, "unable to rotate tees in %.*s: %s\n", teePathLen, teePath,
+ dprintf(fd, "unable to rotate tees in %.*s: %s\n", (int) teePathLen, teePath,
strerror(errno));
}
}
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
index d202169..873a9ad 100644
--- a/services/audioflinger/FastCapture.cpp
+++ b/services/audioflinger/FastCapture.cpp
@@ -31,7 +31,7 @@
/*static*/ const FastCaptureState FastCapture::sInitial;
-FastCapture::FastCapture() : FastThread(),
+FastCapture::FastCapture() : FastThread("cycleC_ms", "loadC_us"),
mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0),
mReadBuffer(NULL), mReadBufferState(-1), mFormat(Format_Invalid), mSampleRate(0),
// mDummyDumpState
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 01f3939..b0780a4 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -46,7 +46,7 @@
/*static*/ const FastMixerState FastMixer::sInitial;
-FastMixer::FastMixer() : FastThread(),
+FastMixer::FastMixer() : FastThread("cycle_ms", "load_us"),
// mFastTrackNames
// mGenerations
mOutputSink(NULL),
diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp
index 5ca579b..8da54b0 100644
--- a/services/audioflinger/FastThread.cpp
+++ b/services/audioflinger/FastThread.cpp
@@ -35,7 +35,7 @@
namespace android {
-FastThread::FastThread() : Thread(false /*canCallJava*/),
+FastThread::FastThread(const char *cycleMs, const char *loadUs) : Thread(false /*canCallJava*/),
// re-initialized to &sInitial by subclass constructor
mPrevious(NULL), mCurrent(NULL),
/* mOldTs({0, 0}), */
@@ -72,11 +72,15 @@
frameCount(0),
#endif
mAttemptedWrite(false)
+ // mCycleMs(cycleMs)
+ // mLoadUs(loadUs)
{
mOldTs.tv_sec = 0;
mOldTs.tv_nsec = 0;
mMeasuredWarmupTs.tv_sec = 0;
mMeasuredWarmupTs.tv_nsec = 0;
+ strlcpy(mCycleMs, cycleMs, sizeof(mCycleMs));
+ strlcpy(mLoadUs, loadUs, sizeof(mLoadUs));
}
FastThread::~FastThread()
@@ -336,8 +340,8 @@
// this store #4 is not atomic with respect to stores #1, #2, #3 above, but
// the newest open & oldest closed halves are atomic with respect to each other
mDumpState->mBounds = mBounds;
- ATRACE_INT("cycle_ms", monotonicNs / 1000000);
- ATRACE_INT("load_us", loadNs / 1000);
+ ATRACE_INT(mCycleMs, monotonicNs / 1000000);
+ ATRACE_INT(mLoadUs, loadNs / 1000);
}
#endif
} else {
diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h
index 2efb6de..816b666 100644
--- a/services/audioflinger/FastThread.h
+++ b/services/audioflinger/FastThread.h
@@ -30,7 +30,7 @@
class FastThread : public Thread {
public:
- FastThread();
+ FastThread(const char *cycleMs, const char *loadUs);
virtual ~FastThread();
private:
@@ -88,6 +88,9 @@
FastThreadState::Command mCommand;
bool mAttemptedWrite;
+ char mCycleMs[16]; // cycle_ms + suffix
+ char mLoadUs[16]; // load_us + suffix
+
}; // class FastThread
} // android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index bb5c97f..a3dcdcf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4878,9 +4878,12 @@
}
} else if (track->isResumePending()) {
track->resumeAck();
- if (last && mHwPaused) {
- doHwResume = true;
- mHwPaused = false;
+ if (last) {
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ }
}
}
@@ -4906,8 +4909,10 @@
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- // make sure processVolume_l() will apply new volume even if 0
- mLeftVolFloat = mRightVolFloat = -1.0;
+ if (last) {
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ }
if (!mHwSupportsPause) {
track->resumeAck();
}
@@ -5460,6 +5465,8 @@
// enable write to audio HAL
mSleepTimeUs = 0;
+ mLeftVolFloat = mRightVolFloat = -1.0;
+
// Do not handle new data in this iteration even if track->framesReady()
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -5468,8 +5475,10 @@
ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
- // make sure processVolume_l() will apply new volume even if 0
- mLeftVolFloat = mRightVolFloat = -1.0;
+ if (last) {
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ }
}
if (last) {
@@ -6036,7 +6045,7 @@
// sleep with mutex unlocked
if (sleepUs > 0) {
- ATRACE_BEGIN("sleep");
+ ATRACE_BEGIN("sleepC");
mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)sleepUs));
ATRACE_END();
sleepUs = 0;
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index ebe65e4..8d7f71c 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -51,7 +51,8 @@
device3/Camera3BufferManager.cpp \
gui/RingBufferConsumer.cpp \
utils/CameraTraces.cpp \
- utils/AutoConditionLock.cpp
+ utils/AutoConditionLock.cpp \
+ utils/TagMonitor.cpp
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index bbe7317..3b51239 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -530,12 +530,26 @@
mId, __FUNCTION__);
bool dumpTemplates = false;
+
String16 templatesOption("-t");
+ String16 monitorOption("-m");
int n = args.size();
for (int i = 0; i < n; i++) {
if (args[i] == templatesOption) {
dumpTemplates = true;
}
+ if (args[i] == monitorOption) {
+ if (i + 1 < n) {
+ String8 monitorTags = String8(args[i + 1]);
+ if (monitorTags == "off") {
+ mTagMonitor.disableMonitoring();
+ } else {
+ mTagMonitor.parseTagsToMonitor(monitorTags);
+ }
+ } else {
+ mTagMonitor.disableMonitoring();
+ }
+ }
}
String8 lines;
@@ -622,6 +636,8 @@
}
}
+ mTagMonitor.dumpMonitoredMetadata(fd);
+
if (mHal3Device != NULL) {
lines = String8(" HAL device dump:\n");
write(fd, lines.string(), lines.size());
@@ -2346,13 +2362,16 @@
captureResult.mMetadata.sort();
// Check that there's a timestamp in the result metadata
- camera_metadata_entry entry = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
- if (entry.count == 0) {
+ camera_metadata_entry timestamp = captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP);
+ if (timestamp.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
frameNumber);
return;
}
+ mTagMonitor.monitorMetadata(TagMonitor::RESULT,
+ frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
+
insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
}
@@ -2721,6 +2740,11 @@
}
+void Camera3Device::monitorMetadata(TagMonitor::eventSource source,
+ int64_t frameNumber, nsecs_t timestamp, const CameraMetadata& metadata) {
+ mTagMonitor.monitorMetadata(source, frameNumber, timestamp, metadata);
+}
+
/**
* RequestThread inner class methods
*/
@@ -3147,6 +3171,12 @@
camera_metadata_t* cloned = clone_camera_metadata(nextRequest.halRequest.settings);
mLatestRequest.acquire(cloned);
+
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent != NULL) {
+ parent->monitorMetadata(TagMonitor::REQUEST, nextRequest.halRequest.frame_number,
+ 0, mLatestRequest);
+ }
}
if (nextRequest.halRequest.settings != NULL) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index fe5f217..bbb6563 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -30,6 +30,7 @@
#include "common/CameraDeviceBase.h"
#include "device3/StatusTracker.h"
#include "device3/Camera3BufferManager.h"
+#include "utils/TagMonitor.h"
/**
* Function pointer types with C calling convention to
@@ -855,6 +856,16 @@
/**** End scope for mInFlightLock ****/
+ // Debug tracker for metadata tag value changes
+ // - Enabled with the -m <taglist> option to dumpsys, such as
+ // dumpsys -m android.control.aeState,android.control.aeMode
+ // - Disabled with -m off
+ // - dumpsys -m 3a is a shortcut for ae/af/awbMode, State, and Triggers
+ TagMonitor mTagMonitor;
+
+ void monitorMetadata(TagMonitor::eventSource source, int64_t frameNumber,
+ nsecs_t timestamp, const CameraMetadata& metadata);
+
/**
* Static callback forwarding methods from HAL to instance
*/
diff --git a/services/camera/libcameraservice/utils/TagMonitor.cpp b/services/camera/libcameraservice/utils/TagMonitor.cpp
new file mode 100644
index 0000000..f1b65bd
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Camera3-TagMonitor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "TagMonitor.h"
+
+#include <inttypes.h>
+#include <utils/Log.h>
+#include <camera/VendorTagDescriptor.h>
+
+namespace android {
+
+TagMonitor::TagMonitor():
+ mMonitoringEnabled(false),
+ mMonitoringEvents(kMaxMonitorEvents)
+{}
+
+const char* TagMonitor::k3aTags =
+ "android.control.aeMode, android.control.afMode, android.control.awbMode,"
+ "android.control.aeState, android.control.afState, android.control.awbState,"
+ "android.control.aePrecaptureTrigger, android.control.afTrigger,"
+ "android.control.aeRegions, android.control.awbRegions, android.control.afRegions,"
+ "android.control.aeExposureCompensation, android.control.aeLock, android.control.awbLock,"
+ "android.control.aeAntibandingMode, android.control.aeTargetFpsRange,"
+ "android.control.effectMode, android.control.mode, android.control.sceneMode,"
+ "android.control.videoStabilizationMode";
+
+void TagMonitor::parseTagsToMonitor(String8 tagNames) {
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ // Expand shorthands
+ if (ssize_t idx = tagNames.find("3a") != -1) {
+ ssize_t end = tagNames.find(",", idx);
+ char* start = tagNames.lockBuffer(tagNames.size());
+ start[idx] = '\0';
+ char* rest = (end != -1) ? (start + end) : (start + tagNames.size());
+ tagNames = String8::format("%s%s%s", start, k3aTags, rest);
+ }
+
+ sp<VendorTagDescriptor> vTags =
+ VendorTagDescriptor::getGlobalVendorTagDescriptor();
+
+ bool gotTag = false;
+
+ char *tokenized = tagNames.lockBuffer(tagNames.size());
+ char *savePtr;
+ char *nextTagName = strtok_r(tokenized, ", ", &savePtr);
+ while (nextTagName != nullptr) {
+ uint32_t tag;
+ status_t res = CameraMetadata::getTagFromName(nextTagName, vTags.get(), &tag);
+ if (res != OK) {
+ ALOGW("%s: Unknown tag %s, ignoring", __FUNCTION__, nextTagName);
+ } else {
+ if (!gotTag) {
+ mMonitoredTagList.clear();
+ gotTag = true;
+ }
+ mMonitoredTagList.push_back(tag);
+ }
+ nextTagName = strtok_r(nullptr, ", ", &savePtr);
+ }
+
+ tagNames.unlockBuffer();
+
+ if (gotTag) {
+ // Got at least one new tag
+ mMonitoringEnabled = true;
+ }
+}
+
+void TagMonitor::disableMonitoring() {
+ mMonitoringEnabled = false;
+ mLastMonitoredRequestValues.clear();
+ mLastMonitoredResultValues.clear();
+}
+
+void TagMonitor::monitorMetadata(eventSource source, int64_t frameNumber, nsecs_t timestamp,
+ const CameraMetadata& metadata) {
+ if (!mMonitoringEnabled) return;
+
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ if (timestamp == 0) {
+ timestamp = systemTime(SYSTEM_TIME_BOOTTIME);
+ }
+
+ for (auto tag : mMonitoredTagList) {
+ camera_metadata_ro_entry entry = metadata.find(tag);
+ CameraMetadata &lastValues = (source == REQUEST) ?
+ mLastMonitoredRequestValues : mLastMonitoredResultValues;
+ camera_metadata_entry lastEntry = lastValues.find(tag);
+
+ if (entry.count > 0) {
+ bool isDifferent = false;
+ if (lastEntry.count > 0) {
+ // Have a last value, compare to see if changed
+ if (lastEntry.type == entry.type &&
+ lastEntry.count == entry.count) {
+ // Same type and count, compare values
+ size_t bytesPerValue = camera_metadata_type_size[lastEntry.type];
+ size_t entryBytes = bytesPerValue * lastEntry.count;
+ int cmp = memcmp(entry.data.u8, lastEntry.data.u8, entryBytes);
+ if (cmp != 0) {
+ isDifferent = true;
+ }
+ } else {
+ // Count or type has changed
+ isDifferent = true;
+ }
+ } else {
+ // No last entry, so always consider to be different
+ isDifferent = true;
+ }
+
+ if (isDifferent) {
+ ALOGV("%s: Tag %s changed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+ lastValues.update(entry);
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+ }
+ } else if (lastEntry.count > 0) {
+ // Value has been removed
+ ALOGV("%s: Tag %s removed", __FUNCTION__, get_camera_metadata_tag_name(tag));
+ lastValues.erase(tag);
+ entry.tag = tag;
+ entry.type = get_camera_metadata_tag_type(tag);
+ entry.count = 0;
+ mMonitoringEvents.emplace(source, frameNumber, timestamp, entry);
+ }
+ }
+}
+
+void TagMonitor::dumpMonitoredMetadata(int fd) {
+ std::lock_guard<std::mutex> lock(mMonitorMutex);
+
+ if (mMonitoringEnabled) {
+ dprintf(fd, " Tag monitoring enabled for tags:\n");
+ for (uint32_t tag : mMonitoredTagList) {
+ dprintf(fd, " %s.%s\n",
+ get_camera_metadata_section_name(tag),
+ get_camera_metadata_tag_name(tag));
+ }
+ } else {
+ dprintf(fd, " Tag monitoring disabled (enable with -m <name1,..,nameN>)\n");
+ }
+ if (mMonitoringEvents.size() > 0) {
+ dprintf(fd, " Monitored tag event log:\n");
+ for (const auto& event : mMonitoringEvents) {
+ int indentation = (event.source == REQUEST) ? 15 : 30;
+ dprintf(fd, " f%d:%" PRId64 "ns: %*s%s.%s: ",
+ event.frameNumber, event.timestamp,
+ indentation,
+ event.source == REQUEST ? "REQ:" : "RES:",
+ get_camera_metadata_section_name(event.tag),
+ get_camera_metadata_tag_name(event.tag));
+ if (event.newData.size() == 0) {
+ dprintf(fd, " (Removed)\n");
+ } else {
+ printData(fd, event.newData.data(), event.tag,
+ event.type, event.newData.size() / camera_metadata_type_size[event.type],
+ indentation + 18);
+ }
+ }
+ }
+
+}
+
+// TODO: Consolidate with printData from camera_metadata.h
+
+#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29
+
+void TagMonitor::printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+ int type, int count, int indentation) {
+ static int values_per_line[NUM_TYPES] = {
+ [TYPE_BYTE] = 16,
+ [TYPE_INT32] = 8,
+ [TYPE_FLOAT] = 8,
+ [TYPE_INT64] = 4,
+ [TYPE_DOUBLE] = 4,
+ [TYPE_RATIONAL] = 4,
+ };
+ size_t type_size = camera_metadata_type_size[type];
+ char value_string_tmp[CAMERA_METADATA_ENUM_STRING_MAX_SIZE];
+ uint32_t value;
+
+ int lines = count / values_per_line[type];
+ if (count % values_per_line[type] != 0) lines++;
+
+ int index = 0;
+ int j, k;
+ for (j = 0; j < lines; j++) {
+ dprintf(fd, "%*s[", (j != 0) ? indentation + 4 : 0, "");
+ for (k = 0;
+ k < values_per_line[type] && count > 0;
+ k++, count--, index += type_size) {
+
+ switch (type) {
+ case TYPE_BYTE:
+ value = *(data_ptr + index);
+ if (camera_metadata_enum_snprint(tag,
+ value,
+ value_string_tmp,
+ sizeof(value_string_tmp))
+ == OK) {
+ dprintf(fd, "%s ", value_string_tmp);
+ } else {
+ dprintf(fd, "%hhu ",
+ *(data_ptr + index));
+ }
+ break;
+ case TYPE_INT32:
+ value =
+ *(int32_t*)(data_ptr + index);
+ if (camera_metadata_enum_snprint(tag,
+ value,
+ value_string_tmp,
+ sizeof(value_string_tmp))
+ == OK) {
+ dprintf(fd, "%s ", value_string_tmp);
+ } else {
+ dprintf(fd, "%" PRId32 " ",
+ *(int32_t*)(data_ptr + index));
+ }
+ break;
+ case TYPE_FLOAT:
+ dprintf(fd, "%0.8f ",
+ *(float*)(data_ptr + index));
+ break;
+ case TYPE_INT64:
+ dprintf(fd, "%" PRId64 " ",
+ *(int64_t*)(data_ptr + index));
+ break;
+ case TYPE_DOUBLE:
+ dprintf(fd, "%0.8f ",
+ *(double*)(data_ptr + index));
+ break;
+ case TYPE_RATIONAL: {
+ int32_t numerator = *(int32_t*)(data_ptr + index);
+ int32_t denominator = *(int32_t*)(data_ptr + index + 4);
+ dprintf(fd, "(%d / %d) ",
+ numerator, denominator);
+ break;
+ }
+ default:
+ dprintf(fd, "??? ");
+ }
+ }
+ dprintf(fd, "]\n");
+ }
+}
+
+template<typename T>
+TagMonitor::MonitorEvent::MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+ const T &value) :
+ source(src),
+ frameNumber(frameNumber),
+ timestamp(timestamp),
+ tag(value.tag),
+ type(value.type),
+ newData(value.data.u8, value.data.u8 + camera_metadata_type_size[value.type] * value.count) {
+}
+
+TagMonitor::MonitorEvent::~MonitorEvent() {
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/utils/TagMonitor.h b/services/camera/libcameraservice/utils/TagMonitor.h
new file mode 100644
index 0000000..d7aa419
--- /dev/null
+++ b/services/camera/libcameraservice/utils/TagMonitor.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_TAGMONITOR_H
+#define ANDROID_SERVERS_CAMERA_TAGMONITOR_H
+
+#include <vector>
+#include <atomic>
+#include <mutex>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/Timers.h>
+
+#include <media/RingBuffer.h>
+#include <system/camera_metadata.h>
+#include <camera/CameraMetadata.h>
+
+namespace android {
+
+/**
+ * A monitor for camera metadata values.
+ * Tracks changes to specified metadata values over time, keeping a circular
+ * buffer log that can be dumped at will. */
+class TagMonitor {
+ public:
+ enum eventSource {
+ REQUEST,
+ RESULT
+ };
+
+ TagMonitor();
+
+ // Parse tag name list (comma-separated) and if valid, enable monitoring
+ // If invalid, do nothing.
+ // Recognizes "3a" as a shortcut for enabling tracking 3A state, mode, and
+ // triggers
+ void parseTagsToMonitor(String8 tagNames);
+
+ // Disable monitoring; does not clear the event log
+ void disableMonitoring();
+
+ // Scan through the metadata and update the monitoring information
+ void monitorMetadata(eventSource source, int64_t frameNumber,
+ nsecs_t timestamp, const CameraMetadata& metadata);
+
+ // Dump current event log to the provided fd
+ void dumpMonitoredMetadata(int fd);
+
+ private:
+
+ static void printData(int fd, const uint8_t *data_ptr, uint32_t tag,
+ int type, int count, int indentation);
+
+ std::atomic<bool> mMonitoringEnabled;
+ std::mutex mMonitorMutex;
+
+ // Current tags to monitor and record changes to
+ std::vector<uint32_t> mMonitoredTagList;
+
+ // Latest-seen values of tracked tags
+ CameraMetadata mLastMonitoredRequestValues;
+ CameraMetadata mLastMonitoredResultValues;
+
+ /**
+ * A monitoring event
+ * Stores a new metadata field value and the timestamp at which it changed.
+ * Copies the source metadata value array and frees it on destruct.
+ */
+ struct MonitorEvent {
+ template<typename T>
+ MonitorEvent(eventSource src, uint32_t frameNumber, nsecs_t timestamp,
+ const T &newValue);
+ ~MonitorEvent();
+
+ eventSource source;
+ uint32_t frameNumber;
+ nsecs_t timestamp;
+ uint32_t tag;
+ uint8_t type;
+ std::vector<uint8_t> newData;
+ };
+
+ // A ring buffer for tracking the last kMaxMonitorEvents metadata changes
+ static const int kMaxMonitorEvents = 100;
+ RingBuffer<MonitorEvent> mMonitoringEvents;
+
+ // 3A fields to use with the "3a" option
+ static const char *k3aTags;
+};
+
+} // namespace android
+
+#endif