Merge "RTSPSource: relax error condition." 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 8f6737f..fea5a1d 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -1,7 +1,6 @@
service cameraserver /system/bin/cameraserver
class main
user cameraserver
- group audio camera input drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct
+ 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/include/media/IDataSource.h b/include/media/IDataSource.h
index 838e29f..655f337 100644
--- a/include/media/IDataSource.h
+++ b/include/media/IDataSource.h
@@ -25,6 +25,7 @@
namespace android {
class IMemory;
+class DecryptHandle;
// A binder interface for implementing a stagefright DataSource remotely.
class IDataSource : public IInterface {
@@ -47,6 +48,8 @@
virtual uint32_t getFlags() = 0;
// get a description of the source, e.g. the url or filename it is based on
virtual String8 toString() = 0;
+ // Initialize DRM and return a DecryptHandle.
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime) = 0;
private:
DISALLOW_EVIL_CONSTRUCTORS(IDataSource);
diff --git a/include/media/IMediaSource.h b/include/media/IMediaSource.h
index 524e7aa..2ff42ec 100644
--- a/include/media/IMediaSource.h
+++ b/include/media/IMediaSource.h
@@ -18,14 +18,17 @@
#define IMEDIA_SOURCE_BASE_H_
+#include <map>
+
#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaErrors.h>
namespace android {
struct MediaSource;
class MetaData;
-class MediaBuffer;
class MediaBufferGroup;
class IMediaSource : public IInterface {
@@ -56,7 +59,7 @@
// a) not request a seek
// b) not be late, i.e. lateness_us = 0
struct ReadOptions {
- enum SeekMode {
+ enum SeekMode : int32_t {
SEEK_PREVIOUS_SYNC,
SEEK_NEXT_SYNC,
SEEK_CLOSEST_SYNC,
@@ -72,6 +75,7 @@
void clearSeekTo();
bool getSeekTo(int64_t *time_us, SeekMode *mode) const;
+ // TODO: remove this if unused.
void setLateBy(int64_t lateness_us);
int64_t getLateBy() const;
@@ -79,6 +83,11 @@
void clearNonBlocking();
bool getNonBlocking() const;
+ // Used to clear all non-persistent options for multiple buffer reads.
+ void clearNonPersistent() {
+ clearSeekTo();
+ }
+
private:
enum Options {
kSeekTo_Option = 1,
@@ -98,25 +107,34 @@
// A result of INFO_FORMAT_CHANGED indicates that the format of this
// MediaSource has changed mid-stream, the client can continue reading
// but should be prepared for buffers of the new configuration.
+ //
+ // TODO: consider removing read() in favor of readMultiple().
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
- // Returns a vector of new buffers of data. The vector size could be
- // <= |maxNumBuffers|. Used for buffers with small size
- // since all buffer data are passed back by binder, not shared memory.
+ // Returns a vector of new buffers of data, where the new buffers are added
+ // to the end of the vector.
// Call blocks until an error is encountered, or the end of the stream is
// reached, or format change is hit, or |kMaxNumReadMultiple| buffers have
// been read.
- // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+ // End of stream is signaled by a result of ERROR_END_OF_STREAM.
// A result of INFO_FORMAT_CHANGED indicates that the format of this
// MediaSource has changed mid-stream, the client can continue reading
// but should be prepared for buffers of the new configuration.
+ //
+ // ReadOptions may be specified. Persistent options apply to all reads;
+ // non-persistent options (e.g. seek) apply only to the first read.
virtual status_t readMultiple(
- Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1) = 0;
+ Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers = 1,
+ const ReadOptions *options = nullptr) = 0;
// Returns true if |readMultiple| is supported, otherwise false.
virtual bool supportReadMultiple() = 0;
+ // Returns true if |read| supports nonblocking option, otherwise false.
+ // |readMultiple| if supported, always allows the nonblocking option.
+ virtual bool supportNonblockingRead() = 0;
+
// Causes this source to suspend pulling data from its upstream source
// until a subsequent read-with-seek. Currently only supported by
// OMXCodec.
@@ -147,21 +165,102 @@
return ERROR_UNSUPPORTED;
}
+ // TODO: Implement this for local media sources.
virtual status_t readMultiple(
- Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */) {
+ Vector<MediaBuffer *> * /* buffers */, uint32_t /* maxNumBuffers = 1 */,
+ const ReadOptions * /* options = nullptr */) {
return ERROR_UNSUPPORTED;
}
virtual bool supportReadMultiple() {
return false;
}
+
+ // Override in source if nonblocking reads are supported.
+ virtual bool supportNonblockingRead() {
+ return false;
+ }
+
+ static const size_t kBinderMediaBuffers = 4; // buffers managed by BnMediaSource
+ static const size_t kTransferSharedAsSharedThreshold = 4 * 1024; // if >= shared, else inline
+ static const size_t kTransferInlineAsSharedThreshold = 64 * 1024; // if >= shared, else inline
+ static const size_t kInlineMaxTransfer = 256 * 1024; // Binder size limited to BINDER_VM_SIZE.
+
protected:
virtual ~BnMediaSource();
private:
- MediaBufferGroup *mGroup;
-};
+ uint32_t mBuffersSinceStop; // Buffer tracking variable
+ std::unique_ptr<MediaBufferGroup> mGroup;
+
+ // To prevent marshalling IMemory with each read transaction, we cache the IMemory pointer
+ // into a map.
+ //
+ // This is converted into an index, which is used to identify the associated memory
+ // on the receiving side. We hold a reference to the IMemory here to ensure it doesn't
+ // change underneath us.
+
+ struct IndexCache {
+ IndexCache() : mIndex(0) { }
+
+ // Returns the index of the IMemory stored in cache or 0 if not found.
+ uint64_t lookup(const sp<IMemory> &mem) {
+ auto p = mMemoryToIndex.find(mem.get());
+ if (p == mMemoryToIndex.end()) {
+ return 0;
+ }
+ if (MediaBuffer::isDeadObject(p->second.first)) {
+ // this object's dead
+ ALOGW("Attempting to lookup a dead IMemory");
+ (void)mMemoryToIndex.erase(p);
+ return 0;
+ }
+ ALOGW_IF(p->second.first.get() != mem.get(), "Mismatched buffers without reset");
+ return p->second.second;
+ }
+
+ // Returns the index of the IMemory stored in the index cache.
+ uint64_t insert(const sp<IMemory> &mem) {
+ auto p = mMemoryToIndex.find(mem.get());
+ if (p == mMemoryToIndex.end()) {
+ if (mIndex == UINT64_MAX) {
+ ALOGE("Index overflow");
+ mIndex = 1; // skip overflow condition and hope for the best
+ } else {
+ ++mIndex;
+ }
+ (void)mMemoryToIndex.emplace(// C++11 mem.get(), std::make_pair(mem, mIndex))
+ std::piecewise_construct,
+ std::forward_as_tuple(mem.get()), std::forward_as_tuple(mem, mIndex));
+ return mIndex;
+ }
+ ALOGW("IMemory already inserted into cache");
+ return p->second.second;
+ }
+
+ void reset() {
+ mMemoryToIndex.clear();
+ mIndex = 0;
+ }
+
+ void gc() {
+ for (auto it = mMemoryToIndex.begin(); it != mMemoryToIndex.end(); ) {
+ if (MediaBuffer::isDeadObject(it->second.first)) {
+ it = mMemoryToIndex.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ private:
+ uint64_t mIndex;
+ // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+ // Could key on uintptr_t instead of IMemory *
+ std::map<IMemory *, std::pair<sp<IMemory>, uint64_t>> mMemoryToIndex;
+ } mIndexCache;
+};
} // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 4f2517c..25f7173 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -371,6 +371,10 @@
int32_t width, int32_t height,
OMX_VIDEO_CODINGTYPE compressionFormat, float frameRate = -1.0);
+ // sets |portIndex| port buffer numbers to be |bufferNum|. NOTE: Component could reject
+ // this setting if the |bufferNum| is less than the minimum buffer num of the port.
+ status_t setPortBufferNum(OMX_U32 portIndex, int bufferNum);
+
// gets index or sets it to 0 on error. Returns error from codec.
status_t initDescribeColorAspectsIndex();
@@ -480,13 +484,19 @@
status_t getIntraRefreshPeriod(uint32_t *intraRefreshPeriod);
status_t setIntraRefreshPeriod(uint32_t intraRefreshPeriod, bool inConfigure);
+ // Configures temporal layering based on |msg|. |inConfigure| shall be true iff this is called
+ // during configure() call. on success the configured layering is set in |outputFormat|. If
+ // |outputFormat| is mOutputFormat, it is copied to trigger an output format changed event.
+ status_t configureTemporalLayers(
+ const sp<AMessage> &msg, bool inConfigure, sp<AMessage> &outputFormat);
+
status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg);
status_t setupH263EncoderParameters(const sp<AMessage> &msg);
status_t setupAVCEncoderParameters(const sp<AMessage> &msg);
status_t setupHEVCEncoderParameters(const sp<AMessage> &msg);
- status_t setupVPXEncoderParameters(const sp<AMessage> &msg);
+ status_t setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat);
status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level);
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index cbd642c..8f0eaa7 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -188,6 +188,7 @@
// Acquire lock before calling these methods
off64_t addSample_l(MediaBuffer *buffer);
off64_t addLengthPrefixedSample_l(MediaBuffer *buffer);
+ off64_t addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer);
bool exceedsFileSizeLimit();
bool use32BitFileOffset() const;
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 18b80e3..55b1f58 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
#define MEDIA_BUFFER_H_
+#include <atomic>
+#include <list>
#include <media/stagefright/foundation/MediaBufferBase.h>
#include <pthread.h>
@@ -60,6 +62,12 @@
MediaBuffer(const sp<ABuffer> &buffer);
+ MediaBuffer(const sp<IMemory> &mem) :
+ MediaBuffer((uint8_t *)mem->pointer() + sizeof(SharedControl), mem->size()) {
+ // delegate and override mMemory
+ mMemory = mem;
+ }
+
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
virtual void release();
@@ -91,9 +99,44 @@
int refcount() const;
+ bool isDeadObject() const {
+ return isDeadObject(mMemory);
+ }
+
+ static bool isDeadObject(const sp<IMemory> &memory) {
+ if (memory.get() == nullptr || memory->pointer() == nullptr) return false;
+ return reinterpret_cast<SharedControl *>(memory->pointer())->isDeadObject();
+ }
+
protected:
+ // MediaBuffer remote releases are handled through a
+ // pending release count variable stored in a SharedControl block
+ // at the start of the IMemory.
+
+ // Returns old value of pending release count.
+ inline int32_t addPendingRelease(int32_t value) {
+ return getSharedControl()->addPendingRelease(value);
+ }
+
+ // Issues all pending releases (works in parallel).
+ // Assumes there is a MediaBufferObserver.
+ inline void resolvePendingRelease() {
+ if (mMemory.get() == nullptr) return;
+ while (addPendingRelease(-1) > 0) {
+ release();
+ }
+ addPendingRelease(1);
+ }
+
+ // true if MediaBuffer is observed (part of a MediaBufferGroup).
+ inline bool isObserved() const {
+ return mObserver != nullptr;
+ }
+
virtual ~MediaBuffer();
+ sp<IMemory> mMemory;
+
private:
friend class MediaBufferGroup;
friend class OMXDecoder;
@@ -105,7 +148,6 @@
void claim();
MediaBufferObserver *mObserver;
- MediaBuffer *mNextBuffer;
int mRefCount;
void *mData;
@@ -119,12 +161,57 @@
MediaBuffer *mOriginal;
- void setNextBuffer(MediaBuffer *buffer);
- MediaBuffer *nextBuffer();
-
MediaBuffer(const MediaBuffer &);
MediaBuffer &operator=(const MediaBuffer &);
- sp<IMemory> mMemory;
+
+ // SharedControl block at the start of IMemory.
+ struct SharedControl {
+ enum {
+ FLAG_DEAD_OBJECT = (1 << 0),
+ };
+
+ // returns old value
+ inline int32_t addPendingRelease(int32_t value) {
+ return std::atomic_fetch_add_explicit(
+ &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
+ }
+
+ inline int32_t getPendingRelease() const {
+ return std::atomic_load_explicit(&mPendingRelease, std::memory_order_seq_cst);
+ }
+
+ inline void setPendingRelease(int32_t value) {
+ std::atomic_store_explicit(
+ &mPendingRelease, (int_least32_t)value, std::memory_order_seq_cst);
+ }
+
+ inline bool isDeadObject() const {
+ return (std::atomic_load_explicit(
+ &mFlags, std::memory_order_seq_cst) & FLAG_DEAD_OBJECT) != 0;
+ }
+
+ inline void setDeadObject() {
+ (void)std::atomic_fetch_or_explicit(
+ &mFlags, (int_least32_t)FLAG_DEAD_OBJECT, std::memory_order_seq_cst);
+ }
+
+ inline void clear() {
+ std::atomic_store_explicit(
+ &mFlags, (int_least32_t)0, std::memory_order_seq_cst);
+ std::atomic_store_explicit(
+ &mPendingRelease, (int_least32_t)0, std::memory_order_seq_cst);
+ }
+
+ private:
+ // Caution: atomic_int_fast32_t is 64 bits on LP64.
+ std::atomic_int_least32_t mFlags;
+ std::atomic_int_least32_t mPendingRelease;
+ int32_t unused[6]; // additional buffer space
+ };
+
+ inline SharedControl *getSharedControl() const {
+ return reinterpret_cast<SharedControl *>(mMemory->pointer());
+ }
};
} // namespace android
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
index 7ca3fa1..dfa31b2 100644
--- a/include/media/stagefright/MediaBufferGroup.h
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -29,11 +29,17 @@
class MediaBufferGroup : public MediaBufferObserver {
public:
- MediaBufferGroup();
+ MediaBufferGroup(size_t growthLimit = 0);
+
+ // create a media buffer group with preallocated buffers
+ MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit = 0);
+
~MediaBufferGroup();
void add_buffer(MediaBuffer *buffer);
+ bool has_buffers();
+
// If nonBlocking is false, it blocks until a buffer is available and
// passes it to the caller in *buffer, while returning OK.
// The returned buffer will have a reference count of 1.
@@ -45,6 +51,11 @@
status_t acquire_buffer(
MediaBuffer **buffer, bool nonBlocking = false, size_t requestedSize = 0);
+ size_t buffers() const { return mBuffers.size(); }
+
+ // freeBuffers is the number of free buffers allowed to remain.
+ void gc(size_t freeBuffers = 0);
+
protected:
virtual void signalBufferReturned(MediaBuffer *buffer);
@@ -53,8 +64,8 @@
Mutex mLock;
Condition mCondition;
-
- MediaBuffer *mFirstBuffer, *mLastBuffer;
+ size_t mGrowthLimit; // Do not automatically grow group larger than this.
+ std::list<MediaBuffer *> mBuffers;
MediaBufferGroup(const MediaBufferGroup &);
MediaBufferGroup &operator=(const MediaBufferGroup &);
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 87c32a6..4b2b868 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -123,6 +123,9 @@
bool findBuffer(const char *name, sp<ABuffer> *buffer) const;
bool findMessage(const char *name, sp<AMessage> *obj) const;
+ // finds any numeric type cast to a float
+ bool findAsFloat(const char *name, float *value) const;
+
bool findRect(
const char *name,
int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
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/common_time/Android.mk b/media/common_time/Android.mk
index 632acbc..aaa0db2 100644
--- a/media/common_time/Android.mk
+++ b/media/common_time/Android.mk
@@ -19,4 +19,6 @@
libutils \
liblog
+LOCAL_CFLAGS := -Wall -Werror
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/common_time/cc_helper.cpp b/media/common_time/cc_helper.cpp
index 8d8556c..222b7ce 100644
--- a/media/common_time/cc_helper.cpp
+++ b/media/common_time/cc_helper.cpp
@@ -80,7 +80,7 @@
}
}
-void CCHelper::CommonClockListener::onTimelineChanged(uint64_t timelineID) {
+void CCHelper::CommonClockListener::onTimelineChanged(uint64_t timelineID __unused) {
// do nothing; listener is only really used as a token so the server can
// find out when clients die.
}
diff --git a/media/libmedia/IDataSource.cpp b/media/libmedia/IDataSource.cpp
index 7aeba5a..51c9938 100644
--- a/media/libmedia/IDataSource.cpp
+++ b/media/libmedia/IDataSource.cpp
@@ -23,6 +23,7 @@
#include <binder/IMemory.h>
#include <binder/Parcel.h>
+#include <drm/drm_framework_common.h>
#include <media/stagefright/foundation/ADebug.h>
namespace android {
@@ -34,6 +35,7 @@
CLOSE,
GET_FLAGS,
TO_STRING,
+ DRM_INITIALIZATION,
};
struct BpDataSource : public BpInterface<IDataSource> {
@@ -84,6 +86,47 @@
remote()->transact(TO_STRING, data, &reply);
return reply.readString8();
}
+
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDataSource::getInterfaceDescriptor());
+ if (mime == NULL) {
+ data.writeInt32(0);
+ } else {
+ data.writeInt32(1);
+ data.writeCString(mime);
+ }
+ remote()->transact(DRM_INITIALIZATION, data, &reply);
+ sp<DecryptHandle> handle;
+ if (reply.dataAvail() != 0) {
+ handle = new DecryptHandle();
+ handle->decryptId = reply.readInt32();
+ handle->mimeType = reply.readString8();
+ handle->decryptApiType = reply.readInt32();
+ handle->status = reply.readInt32();
+
+ const int bufferLength = data.readInt32();
+ if (bufferLength != -1) {
+ handle->decryptInfo = new DecryptInfo();
+ handle->decryptInfo->decryptBufferLength = bufferLength;
+ }
+
+ size_t size = data.readInt32();
+ for (size_t i = 0; i < size; ++i) {
+ DrmCopyControl key = (DrmCopyControl)data.readInt32();
+ int value = data.readInt32();
+ handle->copyControlVector.add(key, value);
+ }
+
+ size = data.readInt32();
+ for (size_t i = 0; i < size; ++i) {
+ String8 key = data.readString8();
+ String8 value = data.readString8();
+ handle->extendedData.add(key, value);
+ }
+ }
+ return handle;
+ }
};
IMPLEMENT_META_INTERFACE(DataSource, "android.media.IDataSource");
@@ -126,6 +169,42 @@
reply->writeString8(toString());
return NO_ERROR;
} break;
+ case DRM_INITIALIZATION: {
+ CHECK_INTERFACE(IDataSource, data, reply);
+ const char *mime = NULL;
+ const int32_t flag = data.readInt32();
+ if (flag != 0) {
+ mime = data.readCString();
+ }
+ sp<DecryptHandle> handle = DrmInitialization(mime);
+ if (handle != NULL) {
+ reply->writeInt32(handle->decryptId);
+ reply->writeString8(handle->mimeType);
+ reply->writeInt32(handle->decryptApiType);
+ reply->writeInt32(handle->status);
+
+ if (handle->decryptInfo != NULL) {
+ reply->writeInt32(handle->decryptInfo->decryptBufferLength);
+ } else {
+ reply->writeInt32(-1);
+ }
+
+ size_t size = handle->copyControlVector.size();
+ reply->writeInt32(size);
+ for (size_t i = 0; i < size; ++i) {
+ reply->writeInt32(handle->copyControlVector.keyAt(i));
+ reply->writeInt32(handle->copyControlVector.valueAt(i));
+ }
+
+ size = handle->extendedData.size();
+ reply->writeInt32(size);
+ for (size_t i = 0; i < size; ++i) {
+ reply->writeString8(handle->extendedData.keyAt(i));
+ reply->writeString8(handle->extendedData.valueAt(i));
+ }
+ }
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index d2b4291..dd94ccf 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -36,79 +36,39 @@
STOP,
PAUSE,
GETFORMAT,
- READ,
+ // READ, deprecated
READMULTIPLE,
- RELEASE_BUFFER
+ RELEASE_BUFFER,
+ SUPPORT_NONBLOCKING_READ,
};
enum {
NULL_BUFFER,
SHARED_BUFFER,
- INLINE_BUFFER
+ INLINE_BUFFER,
+ SHARED_BUFFER_INDEX,
};
-class RemoteMediaBufferReleaser : public BBinder {
-public:
- RemoteMediaBufferReleaser(MediaBuffer *buf, sp<BnMediaSource> owner) {
- mBuf = buf;
- mOwner = owner;
- }
- ~RemoteMediaBufferReleaser() {
- if (mBuf) {
- ALOGW("RemoteMediaBufferReleaser dtor called while still holding buffer");
- mBuf->release();
- }
- }
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0) {
- if (code == RELEASE_BUFFER) {
- mBuf->release();
- mBuf = NULL;
- return OK;
- } else {
- return BBinder::onTransact(code, data, reply, flags);
- }
- }
-private:
- MediaBuffer *mBuf;
- // Keep a ref to ensure MediaBuffer is released before the owner, i.e., BnMediaSource,
- // because BnMediaSource needs to delete MediaBufferGroup in its dtor and
- // MediaBufferGroup dtor requires all MediaBuffer's have 0 ref count.
- sp<BnMediaSource> mOwner;
-};
-
-
class RemoteMediaBufferWrapper : public MediaBuffer {
public:
- RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source);
+ RemoteMediaBufferWrapper(const sp<IMemory> &mem)
+ : MediaBuffer(mem) {
+ ALOGV("RemoteMediaBufferWrapper: creating %p", this);
+ }
+
protected:
- virtual ~RemoteMediaBufferWrapper();
-private:
- sp<IMemory> mMemory;
- sp<IBinder> mRemoteSource;
+ virtual ~RemoteMediaBufferWrapper() {
+ // Indicate to MediaBufferGroup to release.
+ int32_t old = addPendingRelease(1);
+ ALOGV("RemoteMediaBufferWrapper: releasing %p, old %d", this, old);
+ mMemory.clear(); // don't set the dead object flag.
+ }
};
-RemoteMediaBufferWrapper::RemoteMediaBufferWrapper(sp<IMemory> mem, sp<IBinder> source)
-: MediaBuffer(mem->pointer(), mem->size()) {
- mMemory = mem;
- mRemoteSource = source;
-}
-
-RemoteMediaBufferWrapper::~RemoteMediaBufferWrapper() {
- mMemory.clear();
- // Explicitly ask the remote side to release the buffer. We could also just clear
- // mRemoteSource, but that doesn't immediately release the reference on the remote side.
- Parcel data, reply;
- mRemoteSource->transact(RELEASE_BUFFER, data, &reply);
- mRemoteSource.clear();
-}
-
class BpMediaSource : public BpInterface<IMediaSource> {
public:
BpMediaSource(const sp<IBinder>& impl)
- : BpInterface<IMediaSource>(impl)
+ : BpInterface<IMediaSource>(impl), mBuffersSinceStop(0)
{
}
@@ -135,7 +95,10 @@
ALOGV("stop");
Parcel data, reply;
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
- return remote()->transact(STOP, data, &reply);
+ status_t status = remote()->transact(STOP, data, &reply);
+ mMemoryCache.reset();
+ mBuffersSinceStop = 0;
+ return status;
}
virtual sp<MetaData> getFormat() {
@@ -151,46 +114,16 @@
}
virtual status_t read(MediaBuffer **buffer, const ReadOptions *options) {
- ALOGV("read");
- Parcel data, reply;
- data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
- if (options) {
- data.writeByteArray(sizeof(*options), (uint8_t*) options);
- }
- status_t ret = remote()->transact(READ, data, &reply);
- if (ret != NO_ERROR) {
- return ret;
- }
- // wrap the returned data in a MediaBuffer
- ret = reply.readInt32();
- int32_t buftype = reply.readInt32();
- if (buftype == SHARED_BUFFER) {
- sp<IBinder> remote = reply.readStrongBinder();
- sp<IBinder> binder = reply.readStrongBinder();
- sp<IMemory> mem = interface_cast<IMemory>(binder);
- if (mem == NULL) {
- ALOGE("received NULL IMemory for shared buffer");
- }
- size_t offset = reply.readInt32();
- size_t length = reply.readInt32();
- MediaBuffer *buf = new RemoteMediaBufferWrapper(mem, remote);
- buf->set_range(offset, length);
- buf->meta_data()->updateFromParcel(reply);
- *buffer = buf;
- } else if (buftype == NULL_BUFFER) {
- ALOGV("got status %d and NULL buffer", ret);
- *buffer = NULL;
- } else {
- int32_t len = reply.readInt32();
- ALOGV("got status %d and len %d", ret, len);
- *buffer = new MediaBuffer(len);
- reply.read((*buffer)->data(), len);
- (*buffer)->meta_data()->updateFromParcel(reply);
- }
+ Vector<MediaBuffer *> buffers;
+ status_t ret = readMultiple(&buffers, 1 /* maxNumBuffers */, options);
+ *buffer = buffers.size() == 0 ? nullptr : buffers[0];
+ ALOGV("read status %d, bufferCount %u, sinceStop %u",
+ ret, *buffer != nullptr, mBuffersSinceStop);
return ret;
}
- virtual status_t readMultiple(Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers) {
+ virtual status_t readMultiple(
+ Vector<MediaBuffer *> *buffers, uint32_t maxNumBuffers, const ReadOptions *options) {
ALOGV("readMultiple");
if (buffers == NULL || !buffers->isEmpty()) {
return BAD_VALUE;
@@ -198,33 +131,78 @@
Parcel data, reply;
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
data.writeUint32(maxNumBuffers);
+ if (options != nullptr) {
+ data.writeByteArray(sizeof(*options), (uint8_t*) options);
+ }
status_t ret = remote()->transact(READMULTIPLE, data, &reply);
+ mMemoryCache.gc();
if (ret != NO_ERROR) {
return ret;
}
// wrap the returned data in a vector of MediaBuffers
- int32_t bufCount = 0;
- while (1) {
- if (reply.readInt32() == 0) {
- break;
+ int32_t buftype;
+ uint32_t bufferCount = 0;
+ while ((buftype = reply.readInt32()) != NULL_BUFFER) {
+ LOG_ALWAYS_FATAL_IF(bufferCount >= maxNumBuffers,
+ "Received %u+ buffers and requested %u buffers",
+ bufferCount + 1, maxNumBuffers);
+ MediaBuffer *buf;
+ if (buftype == SHARED_BUFFER || buftype == SHARED_BUFFER_INDEX) {
+ uint64_t index = reply.readUint64();
+ ALOGV("Received %s index %llu",
+ buftype == SHARED_BUFFER ? "SHARED_BUFFER" : "SHARED_BUFFER_INDEX",
+ (unsigned long long) index);
+ sp<IMemory> mem;
+ if (buftype == SHARED_BUFFER) {
+ sp<IBinder> binder = reply.readStrongBinder();
+ mem = interface_cast<IMemory>(binder);
+ LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+ "Received NULL IMemory for shared buffer");
+ mMemoryCache.insert(index, mem);
+ } else {
+ mem = mMemoryCache.lookup(index);
+ LOG_ALWAYS_FATAL_IF(mem.get() == nullptr,
+ "Received invalid IMemory index for shared buffer: %llu",
+ (unsigned long long)index);
+ }
+ size_t offset = reply.readInt32();
+ size_t length = reply.readInt32();
+ buf = new RemoteMediaBufferWrapper(mem);
+ buf->set_range(offset, length);
+ buf->meta_data()->updateFromParcel(reply);
+ } else { // INLINE_BUFFER
+ int32_t len = reply.readInt32();
+ ALOGV("INLINE_BUFFER status %d and len %d", ret, len);
+ buf = new MediaBuffer(len);
+ reply.read(buf->data(), len);
+ buf->meta_data()->updateFromParcel(reply);
}
- int32_t len = reply.readInt32();
- ALOGV("got len %d", len);
- MediaBuffer *buf = new MediaBuffer(len);
- reply.read(buf->data(), len);
- buf->meta_data()->updateFromParcel(reply);
buffers->push_back(buf);
- ++bufCount;
+ ++bufferCount;
+ ++mBuffersSinceStop;
}
ret = reply.readInt32();
- ALOGV("got status %d, bufCount %d", ret, bufCount);
+ ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+ ret, bufferCount, mBuffersSinceStop);
return ret;
}
- bool supportReadMultiple() {
+ // Binder proxy adds readMultiple support.
+ virtual bool supportReadMultiple() {
return true;
}
+ virtual bool supportNonblockingRead() {
+ ALOGV("supportNonblockingRead");
+ Parcel data, reply;
+ data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
+ status_t ret = remote()->transact(SUPPORT_NONBLOCKING_READ, data, &reply);
+ if (ret == NO_ERROR) {
+ return reply.readInt32() != 0;
+ }
+ return false;
+ }
+
virtual status_t pause() {
ALOGV("pause");
Parcel data, reply;
@@ -238,10 +216,51 @@
}
private:
+
+ uint32_t mBuffersSinceStop; // Buffer tracking variable
+
// NuPlayer passes pointers-to-metadata around, so we use this to keep the metadata alive
// XXX: could we use this for caching, or does metadata change on the fly?
sp<MetaData> mMetaData;
+ // Cache all IMemory objects received from MediaExtractor.
+ // We gc IMemory objects that are no longer active (referenced by a MediaBuffer).
+
+ struct MemoryCache {
+ sp<IMemory> lookup(uint64_t index) {
+ auto p = mIndexToMemory.find(index);
+ if (p == mIndexToMemory.end()) {
+ ALOGE("cannot find index!");
+ return nullptr;
+ }
+ return p->second;
+ }
+
+ void insert(uint64_t index, const sp<IMemory> &mem) {
+ if (mIndexToMemory.find(index) != mIndexToMemory.end()) {
+ ALOGE("index %llu already present", (unsigned long long)index);
+ return;
+ }
+ (void)mIndexToMemory.emplace(index, mem);
+ }
+
+ void reset() {
+ mIndexToMemory.clear();
+ }
+
+ void gc() {
+ for (auto it = mIndexToMemory.begin(); it != mIndexToMemory.end(); ) {
+ if (MediaBuffer::isDeadObject(it->second)) {
+ it = mIndexToMemory.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ private:
+ // C++14 unordered_map erase on iterator is stable; C++11 has no guarantee.
+ std::map<uint64_t, sp<IMemory>> mIndexToMemory;
+ } mMemoryCache;
};
IMPLEMENT_META_INTERFACE(MediaSource, "android.media.IMediaSource");
@@ -250,12 +269,11 @@
#define LOG_TAG "BnMediaSource"
BnMediaSource::BnMediaSource()
- : mGroup(NULL) {
+ : mBuffersSinceStop(0)
+ , mGroup(new MediaBufferGroup(kBinderMediaBuffers /* growthLimit */)) {
}
BnMediaSource::~BnMediaSource() {
- delete mGroup;
- mGroup = NULL;
}
status_t BnMediaSource::onTransact(
@@ -278,7 +296,11 @@
case STOP: {
ALOGV("stop");
CHECK_INTERFACE(IMediaSource, data, reply);
- return stop();
+ status_t status = stop();
+ mGroup->gc();
+ mIndexCache.reset();
+ mBuffersSinceStop = 0;
+ return status;
}
case PAUSE: {
ALOGV("pause");
@@ -295,116 +317,129 @@
}
return UNKNOWN_ERROR;
}
- case READ: {
- ALOGV("read");
- CHECK_INTERFACE(IMediaSource, data, reply);
- status_t ret;
- MediaBuffer *buf = NULL;
- ReadOptions opts;
- uint32_t len;
- if (data.readUint32(&len) == NO_ERROR &&
- len == sizeof(opts) && data.read((void*)&opts, len) == NO_ERROR) {
- ret = read(&buf, &opts);
- } else {
- ret = read(&buf, NULL);
- }
-
- reply->writeInt32(ret);
- if (buf != NULL) {
- size_t usedSize = buf->range_length();
- // even if we're using shared memory, we might not want to use it, since for small
- // sizes it's faster to copy data through the Binder transaction
- // On the other hand, if the data size is large enough, it's better to use shared
- // memory. When data is too large, binder can't handle it.
- if (usedSize >= MediaBuffer::kSharedMemThreshold) {
- ALOGV("use shared memory: %zu", usedSize);
-
- MediaBuffer *transferBuf = buf;
- size_t offset = buf->range_offset();
- if (transferBuf->mMemory == NULL) {
- if (mGroup == NULL) {
- mGroup = new MediaBufferGroup;
- size_t allocateSize = usedSize;
- if (usedSize < SIZE_MAX / 3) {
- allocateSize = usedSize * 3 / 2;
- }
- mGroup->add_buffer(new MediaBuffer(allocateSize));
- }
-
- MediaBuffer *newBuf = NULL;
- ret = mGroup->acquire_buffer(
- &newBuf, false /* nonBlocking */, usedSize);
- if (ret != OK || newBuf == NULL || newBuf->mMemory == NULL) {
- ALOGW("failed to acquire shared memory, ret %d", ret);
- buf->release();
- if (newBuf != NULL) {
- newBuf->release();
- }
- reply->writeInt32(NULL_BUFFER);
- return NO_ERROR;
- }
- transferBuf = newBuf;
- memcpy(transferBuf->data(), (uint8_t*)buf->data() + buf->range_offset(),
- buf->range_length());
- offset = 0;
- }
-
- reply->writeInt32(SHARED_BUFFER);
- RemoteMediaBufferReleaser *wrapper =
- new RemoteMediaBufferReleaser(transferBuf, this);
- reply->writeStrongBinder(wrapper);
- reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
- reply->writeInt32(offset);
- reply->writeInt32(usedSize);
- buf->meta_data()->writeToParcel(*reply);
- if (buf->mMemory == NULL) {
- buf->release();
- }
- } else {
- // buffer is small: copy it
- if (buf->mMemory != NULL) {
- ALOGV("%zu shared mem available, but only %zu used", buf->mMemory->size(), buf->range_length());
- }
- reply->writeInt32(INLINE_BUFFER);
- reply->writeByteArray(buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
- buf->meta_data()->writeToParcel(*reply);
- buf->release();
- }
- } else {
- ALOGV("ret %d, buf %p", ret, buf);
- reply->writeInt32(NULL_BUFFER);
- }
- return NO_ERROR;
- }
case READMULTIPLE: {
- ALOGV("readmultiple");
+ ALOGV("readMultiple");
CHECK_INTERFACE(IMediaSource, data, reply);
+
+ // Get max number of buffers to read.
uint32_t maxNumBuffers;
data.readUint32(&maxNumBuffers);
- status_t ret = NO_ERROR;
- uint32_t bufferCount = 0;
if (maxNumBuffers > kMaxNumReadMultiple) {
maxNumBuffers = kMaxNumReadMultiple;
}
- while (bufferCount < maxNumBuffers) {
- if (reply->dataSize() >= MediaBuffer::kSharedMemThreshold) {
+
+ // Get read options, if any.
+ ReadOptions opts;
+ uint32_t len;
+ const bool useOptions =
+ data.readUint32(&len) == NO_ERROR
+ && len == sizeof(opts)
+ && data.read((void *)&opts, len) == NO_ERROR;
+
+ mGroup->gc(kBinderMediaBuffers /* freeBuffers */);
+ mIndexCache.gc();
+ size_t inlineTransferSize = 0;
+ status_t ret = NO_ERROR;
+ uint32_t bufferCount = 0;
+ for (; bufferCount < maxNumBuffers; ++bufferCount, ++mBuffersSinceStop) {
+ MediaBuffer *buf = nullptr;
+ ret = read(&buf, useOptions ? &opts : nullptr);
+ opts.clearNonPersistent(); // Remove options that only apply to first buffer.
+ if (ret != NO_ERROR || buf == nullptr) {
break;
}
- MediaBuffer *buf = NULL;
- ret = read(&buf, NULL);
- if (ret != NO_ERROR || buf == NULL) {
- break;
+ // Even if we're using shared memory, we might not want to use it, since for small
+ // sizes it's faster to copy data through the Binder transaction
+ // On the other hand, if the data size is large enough, it's better to use shared
+ // memory. When data is too large, binder can't handle it.
+ //
+ // TODO: reduce MediaBuffer::kSharedMemThreshold
+ MediaBuffer *transferBuf = nullptr;
+ const size_t length = buf->range_length();
+ size_t offset = buf->range_offset();
+ if (length >= (supportNonblockingRead() && buf->mMemory != nullptr ?
+ kTransferSharedAsSharedThreshold : kTransferInlineAsSharedThreshold)) {
+ if (buf->mMemory != nullptr) {
+ ALOGV("Use shared memory: %zu", length);
+ transferBuf = buf;
+ } else {
+ ALOGD("Large buffer %zu without IMemory!", length);
+ ret = mGroup->acquire_buffer(
+ &transferBuf, false /* nonBlocking */, length);
+ if (ret != OK
+ || transferBuf == nullptr
+ || transferBuf->mMemory == nullptr) {
+ ALOGW("Failed to acquire shared memory, size %zu, ret %d",
+ length, ret);
+ if (transferBuf != nullptr) {
+ transferBuf->release();
+ transferBuf = nullptr;
+ }
+ // Current buffer transmit inline; no more additional buffers.
+ maxNumBuffers = 0;
+ } else {
+ memcpy(transferBuf->data(), (uint8_t*)buf->data() + offset, length);
+ offset = 0;
+ if (!mGroup->has_buffers()) {
+ maxNumBuffers = 0; // No more MediaBuffers, stop readMultiple.
+ }
+ }
+ }
}
- ++bufferCount;
- reply->writeInt32(1); // indicate one more MediaBuffer.
- reply->writeByteArray(
- buf->range_length(), (uint8_t*)buf->data() + buf->range_offset());
- buf->meta_data()->writeToParcel(*reply);
- buf->release();
+ if (transferBuf != nullptr) { // Using shared buffers.
+ if (!transferBuf->isObserved()) {
+ // Transfer buffer must be part of a MediaBufferGroup.
+ ALOGV("adding shared memory buffer %p to local group", transferBuf);
+ mGroup->add_buffer(transferBuf);
+ transferBuf->add_ref(); // We have already acquired buffer.
+ }
+ uint64_t index = mIndexCache.lookup(transferBuf->mMemory);
+ if (index == 0) {
+ index = mIndexCache.insert(transferBuf->mMemory);
+ reply->writeInt32(SHARED_BUFFER);
+ reply->writeUint64(index);
+ reply->writeStrongBinder(IInterface::asBinder(transferBuf->mMemory));
+ ALOGV("SHARED_BUFFER(%p) %llu",
+ transferBuf, (unsigned long long)index);
+ } else {
+ reply->writeInt32(SHARED_BUFFER_INDEX);
+ reply->writeUint64(index);
+ ALOGV("SHARED_BUFFER_INDEX(%p) %llu",
+ transferBuf, (unsigned long long)index);
+ }
+ reply->writeInt32(offset);
+ reply->writeInt32(length);
+ buf->meta_data()->writeToParcel(*reply);
+ if (transferBuf != buf) {
+ buf->release();
+ } else if (!supportNonblockingRead()) {
+ maxNumBuffers = 0; // stop readMultiple with one shared buffer.
+ }
+ } else {
+ ALOGV_IF(buf->mMemory != nullptr,
+ "INLINE(%p) %zu shared mem available, but only %zu used",
+ buf, buf->mMemory->size(), length);
+ reply->writeInt32(INLINE_BUFFER);
+ reply->writeByteArray(length, (uint8_t*)buf->data() + offset);
+ buf->meta_data()->writeToParcel(*reply);
+ buf->release();
+ inlineTransferSize += length;
+ if (inlineTransferSize > kInlineMaxTransfer) {
+ maxNumBuffers = 0; // stop readMultiple if inline transfer is too large.
+ }
+ }
}
- reply->writeInt32(0); // indicate no more MediaBuffer.
+ reply->writeInt32(NULL_BUFFER); // Indicate no more MediaBuffers.
reply->writeInt32(ret);
+ ALOGV("readMultiple status %d, bufferCount %u, sinceStop %u",
+ ret, bufferCount, mBuffersSinceStop);
+ return NO_ERROR;
+ }
+ case SUPPORT_NONBLOCKING_READ: {
+ ALOGV("supportNonblockingRead");
+ CHECK_INTERFACE(IMediaSource, data, reply);
+ reply->writeInt32((int32_t)supportNonblockingRead());
return NO_ERROR;
}
default:
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 34445e0..f352d5b 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -877,6 +877,7 @@
ALOGV("Delete Track: %p", mpAudioTrack.get());
mpAudioTrack.clear();
}
+ clearWaveGens();
}
////////////////////////////////////////////////////////////////////////////////
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/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 32c4b8a..24ca582 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -22,6 +22,8 @@
#include "WebmWriter.h"
#include "StagefrightRecorder.h"
+#include <algorithm>
+
#include <android/hardware/ICamera.h>
#include <binder/IPCThreadState.h>
@@ -57,6 +59,11 @@
namespace android {
+static const float kTypicalDisplayRefreshingRate = 60.f;
+// display refresh rate drops on battery saver
+static const float kMinTypicalDisplayRefreshingRate = kTypicalDisplayRefreshingRate / 2;
+static const int kMaxNumVideoTemporalLayers = 8;
+
// To collect the encoder usage for the battery app
static void addBatteryData(uint32_t params) {
sp<IBinder> binder =
@@ -1565,9 +1572,44 @@
format->setInt32("level", mVideoEncoderLevel);
}
+ uint32_t tsLayers = 1;
+ bool preferBFrames = true; // we like B-frames as it produces better quality per bitrate
format->setInt32("priority", 0 /* realtime */);
+ float maxPlaybackFps = mFrameRate; // assume video is only played back at normal speed
+
if (mCaptureFpsEnable) {
format->setFloat("operating-rate", mCaptureFps);
+
+ // enable layering for all time lapse and high frame rate recordings
+ if (mFrameRate / mCaptureFps >= 1.9) { // time lapse
+ preferBFrames = false;
+ tsLayers = 2; // use at least two layers as resulting video will likely be sped up
+ } else if (mCaptureFps > maxPlaybackFps) { // slow-mo
+ maxPlaybackFps = mCaptureFps; // assume video will be played back at full capture speed
+ preferBFrames = false;
+ }
+ }
+
+ for (uint32_t tryLayers = 1; tryLayers <= kMaxNumVideoTemporalLayers; ++tryLayers) {
+ if (tryLayers > tsLayers) {
+ tsLayers = tryLayers;
+ }
+ // keep going until the base layer fps falls below the typical display refresh rate
+ float baseLayerFps = maxPlaybackFps / (1 << (tryLayers - 1));
+ if (baseLayerFps < kMinTypicalDisplayRefreshingRate / 0.9) {
+ break;
+ }
+ }
+
+ if (tsLayers > 1) {
+ uint32_t bLayers = std::min(2u, tsLayers - 1); // use up-to 2 B-layers
+ uint32_t pLayers = tsLayers - bLayers;
+ format->setString(
+ "ts-schema", AStringPrintf("android.generic.%u+%u", pLayers, bLayers));
+
+ // TODO: some encoders do not support B-frames with temporal layering, and we have a
+ // different preference based on use-case. We could move this into camera profiles.
+ format->setInt32("android._prefer-b-frames", preferBFrames);
}
if (mMetaDataStoredInVideoBuffers != kMetadataBufferTypeInvalid) {
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 3d836c8..af2d0f3 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1298,6 +1298,13 @@
}
#endif
+ if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
+ int32_t layerId;
+ if (mb->meta_data()->findInt32(kKeyTemporalLayerId, &layerId)) {
+ meta->setInt32("temporal-layer-id", layerId);
+ }
+ }
+
if (trackType == MEDIA_TRACK_TYPE_TIMEDTEXT) {
const char *mime;
CHECK(mTimedTextTrack.mSource != NULL
@@ -1378,7 +1385,7 @@
if (mIsWidevine) {
maxBuffers = 2;
} else {
- maxBuffers = 4;
+ maxBuffers = 8; // too large of a number may influence seeks
}
break;
case MEDIA_TRACK_TYPE_AUDIO:
@@ -1410,25 +1417,24 @@
MediaSource::ReadOptions options;
bool seeking = false;
-
if (seekTimeUs >= 0) {
options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
seeking = true;
}
- if (mIsWidevine) {
+ const bool couldReadMultiple = (!mIsWidevine && track->mSource->supportReadMultiple());
+
+ if (mIsWidevine || couldReadMultiple) {
options.setNonBlocking();
}
- bool couldReadMultiple =
- (!mIsWidevine && trackType == MEDIA_TRACK_TYPE_AUDIO
- && track->mSource->supportReadMultiple());
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
Vector<MediaBuffer *> mediaBuffers;
status_t err = NO_ERROR;
- if (!seeking && couldReadMultiple) {
- err = track->mSource->readMultiple(&mediaBuffers, (maxBuffers - numBuffers));
+ if (couldReadMultiple) {
+ err = track->mSource->readMultiple(
+ &mediaBuffers, maxBuffers - numBuffers, &options);
} else {
MediaBuffer *mbuf = NULL;
err = track->mSource->read(&mbuf, &options);
@@ -1437,7 +1443,7 @@
}
}
- options.clearSeekTo();
+ options.clearNonPersistent();
size_t id = 0;
size_t count = mediaBuffers.size();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 4504b58..134da14 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -784,12 +784,9 @@
}
if (mVideoDecoder != NULL) {
- float rate = getFrameRate();
- if (rate > 0) {
- sp<AMessage> params = new AMessage();
- params->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
- mVideoDecoder->setParameters(params);
- }
+ sp<AMessage> params = new AMessage();
+ params->setFloat("playback-speed", mPlaybackSettings.mSpeed);
+ mVideoDecoder->setParameters(params);
}
sp<AMessage> response = new AMessage;
@@ -1680,6 +1677,27 @@
return err;
}
}
+
+ if (!audio) {
+ sp<AMessage> params = new AMessage();
+ float rate = getFrameRate();
+ if (rate > 0) {
+ params->setFloat("frame-rate-total", rate);
+ }
+
+ sp<MetaData> fileMeta = getFileMeta();
+ if (fileMeta != NULL) {
+ int32_t videoTemporalLayerCount;
+ if (fileMeta->findInt32(kKeyTemporalLayerCount, &videoTemporalLayerCount)
+ && videoTemporalLayerCount > 0) {
+ params->setInt32("temporal-layer-count", videoTemporalLayerCount);
+ }
+ }
+
+ if (params->countEntries() > 0) {
+ (*decoder)->setParameters(params);
+ }
+ }
return OK;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 4678956..cf38efc 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -19,6 +19,8 @@
#include <utils/Log.h>
#include <inttypes.h>
+#include <algorithm>
+
#include "NuPlayerCCDecoder.h"
#include "NuPlayerDecoder.h"
#include "NuPlayerRenderer.h"
@@ -41,6 +43,12 @@
namespace android {
+static float kDisplayRefreshingRate = 60.f; // TODO: get this from the display
+
+// The default total video frame rate of a stream when that info is not available from
+// the source.
+static float kDefaultVideoFrameRateTotal = 30.f;
+
static inline bool getAudioDeepBufferSetting() {
return property_get_bool("media.stagefright.audio.deep", false /* default_value */);
}
@@ -69,11 +77,17 @@
mIsSecure(false),
mFormatChangePending(false),
mTimeChangePending(false),
+ mFrameRateTotal(kDefaultVideoFrameRateTotal),
+ mPlaybackSpeed(1.0f),
+ mNumVideoTemporalLayerTotal(1), // decode all layers
+ mNumVideoTemporalLayerAllowed(1),
+ mCurrentMaxVideoTemporalLayerId(0),
mResumePending(false),
mComponentName("decoder") {
mCodecLooper = new ALooper;
mCodecLooper->setName("NPDecoder-CL");
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ mVideoTemporalLayerAggregateFps[0] = mFrameRateTotal;
}
NuPlayer::Decoder::~Decoder() {
@@ -329,11 +343,68 @@
}
void NuPlayer::Decoder::onSetParameters(const sp<AMessage> ¶ms) {
- if (mCodec == NULL) {
- ALOGW("onSetParameters called before codec is created.");
- return;
+ bool needAdjustLayers = false;
+ float frameRateTotal;
+ if (params->findFloat("frame-rate-total", &frameRateTotal)
+ && mFrameRateTotal != frameRateTotal) {
+ needAdjustLayers = true;
+ mFrameRateTotal = frameRateTotal;
}
- mCodec->setParameters(params);
+
+ int32_t numVideoTemporalLayerTotal;
+ if (params->findInt32("temporal-layer-count", &numVideoTemporalLayerTotal)
+ && numVideoTemporalLayerTotal >= 0
+ && numVideoTemporalLayerTotal <= kMaxNumVideoTemporalLayers
+ && mNumVideoTemporalLayerTotal != numVideoTemporalLayerTotal) {
+ needAdjustLayers = true;
+ mNumVideoTemporalLayerTotal = std::max(numVideoTemporalLayerTotal, 1);
+ }
+
+ if (needAdjustLayers && mNumVideoTemporalLayerTotal > 1) {
+ // TODO: For now, layer fps is calculated for some specific architectures.
+ // But it really should be extracted from the stream.
+ mVideoTemporalLayerAggregateFps[0] =
+ mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - 1));
+ for (int32_t i = 1; i < mNumVideoTemporalLayerTotal; ++i) {
+ mVideoTemporalLayerAggregateFps[i] =
+ mFrameRateTotal / (float)(1ll << (mNumVideoTemporalLayerTotal - i))
+ + mVideoTemporalLayerAggregateFps[i - 1];
+ }
+ }
+
+ float playbackSpeed;
+ if (params->findFloat("playback-speed", &playbackSpeed)
+ && mPlaybackSpeed != playbackSpeed) {
+ needAdjustLayers = true;
+ mPlaybackSpeed = playbackSpeed;
+ }
+
+ if (needAdjustLayers) {
+ float decodeFrameRate = mFrameRateTotal;
+ // enable temporal layering optimization only if we know the layering depth
+ if (mNumVideoTemporalLayerTotal > 1) {
+ int32_t layerId;
+ for (layerId = 0; layerId < mNumVideoTemporalLayerTotal - 1; ++layerId) {
+ if (mVideoTemporalLayerAggregateFps[layerId] * mPlaybackSpeed
+ >= kDisplayRefreshingRate * 0.9) {
+ break;
+ }
+ }
+ mNumVideoTemporalLayerAllowed = layerId + 1;
+ decodeFrameRate = mVideoTemporalLayerAggregateFps[layerId];
+ }
+ ALOGV("onSetParameters: allowed layers=%d, decodeFps=%g",
+ mNumVideoTemporalLayerAllowed, decodeFrameRate);
+
+ if (mCodec == NULL) {
+ ALOGW("onSetParameters called before codec is created.");
+ return;
+ }
+
+ sp<AMessage> codecParams = new AMessage();
+ codecParams->setFloat("operating-rate", decodeFrameRate * mPlaybackSpeed);
+ mCodec->setParameters(codecParams);
+ }
}
void NuPlayer::Decoder::onSetRenderer(const sp<Renderer> &renderer) {
@@ -742,13 +813,33 @@
}
dropAccessUnit = false;
- if (!mIsAudio
- && !mIsSecure
- && mRenderer->getVideoLateByUs() > 100000ll
- && mIsVideoAVC
- && !IsAVCReferenceFrame(accessUnit)) {
- dropAccessUnit = true;
- ++mNumInputFramesDropped;
+ if (!mIsAudio && !mIsSecure) {
+ int32_t layerId = 0;
+ bool haveLayerId = accessUnit->meta()->findInt32("temporal-layer-id", &layerId);
+ if (mRenderer->getVideoLateByUs() > 100000ll
+ && mIsVideoAVC
+ && !IsAVCReferenceFrame(accessUnit)) {
+ dropAccessUnit = true;
+ } else if (haveLayerId && mNumVideoTemporalLayerTotal > 1) {
+ // Add only one layer each time.
+ if (layerId > mCurrentMaxVideoTemporalLayerId + 1
+ || layerId >= mNumVideoTemporalLayerAllowed) {
+ dropAccessUnit = true;
+ ALOGV("dropping layer(%d), speed=%g, allowed layer count=%d, max layerId=%d",
+ layerId, mPlaybackSpeed, mNumVideoTemporalLayerAllowed,
+ mCurrentMaxVideoTemporalLayerId);
+ } else if (layerId > mCurrentMaxVideoTemporalLayerId) {
+ mCurrentMaxVideoTemporalLayerId = layerId;
+ } else if (layerId == 0 && mNumVideoTemporalLayerTotal > 1 && IsIDR(accessUnit)) {
+ mCurrentMaxVideoTemporalLayerId = mNumVideoTemporalLayerTotal - 1;
+ }
+ }
+ if (dropAccessUnit) {
+ if (layerId <= mCurrentMaxVideoTemporalLayerId && layerId > 0) {
+ mCurrentMaxVideoTemporalLayerId = layerId - 1;
+ }
+ ++mNumInputFramesDropped;
+ }
}
} while (dropAccessUnit);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index ae08b4b..0c619ed 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -57,6 +57,10 @@
kWhatSetVideoSurface = 'sSur'
};
+ enum {
+ kMaxNumVideoTemporalLayers = 32,
+ };
+
sp<Surface> mSurface;
sp<Source> mSource;
@@ -90,6 +94,12 @@
bool mIsSecure;
bool mFormatChangePending;
bool mTimeChangePending;
+ float mFrameRateTotal;
+ float mPlaybackSpeed;
+ int32_t mNumVideoTemporalLayerTotal;
+ int32_t mNumVideoTemporalLayerAllowed;
+ int32_t mCurrentMaxVideoTemporalLayerId;
+ float mVideoTemporalLayerAggregateFps[kMaxNumVideoTemporalLayers];
bool mResumePending;
AString mComponentName;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index 2de829b..0c6f652 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -24,7 +24,7 @@
namespace android {
-struct MemoryDealer;
+class MemoryDealer;
struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
NuPlayerStreamListener(
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/ACodec.cpp b/media/libstagefright/ACodec.cpp
index d97d5b1..3dea270 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2462,6 +2462,109 @@
return OK;
}
+status_t ACodec::configureTemporalLayers(
+ const sp<AMessage> &msg, bool inConfigure, sp<AMessage> &outputFormat) {
+ if (!mIsVideo || !mIsEncoder) {
+ return INVALID_OPERATION;
+ }
+
+ AString tsSchema;
+ if (!msg->findString("ts-schema", &tsSchema)) {
+ return OK;
+ }
+
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ int tags;
+ char dummy;
+ OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE pattern =
+ OMX_VIDEO_AndroidTemporalLayeringPatternNone;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ && numLayers > 0) {
+ pattern = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
+ } else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy))
+ && (tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
+ numLayers += numBLayers;
+ pattern = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
+ } else {
+ ALOGI("Ignoring unsupported ts-schema [%s]", tsSchema.c_str());
+ return BAD_VALUE;
+ }
+
+ OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE layerParams;
+ InitOMXParams(&layerParams);
+ layerParams.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+
+ if (err != OK) {
+ return err;
+ } else if (!(layerParams.eSupportedPatterns & pattern)) {
+ return BAD_VALUE;
+ }
+
+ numLayers = min(numLayers, layerParams.nLayerCountMax);
+ numBLayers = min(numBLayers, layerParams.nBLayerCountMax);
+
+ if (!inConfigure) {
+ OMX_VIDEO_CONFIG_ANDROID_TEMPORALLAYERINGTYPE layerConfig;
+ InitOMXParams(&layerConfig);
+ layerConfig.nPortIndex = kPortIndexOutput;
+ layerConfig.ePattern = pattern;
+ layerConfig.nPLayerCountActual = numLayers - numBLayers;
+ layerConfig.nBLayerCountActual = numBLayers;
+ layerConfig.bBitrateRatiosSpecified = OMX_FALSE;
+
+ err = mOMX->setConfig(
+ mNode, (OMX_INDEXTYPE)OMX_IndexConfigAndroidVideoTemporalLayering,
+ &layerConfig, sizeof(layerConfig));
+ } else {
+ layerParams.ePattern = pattern;
+ layerParams.nPLayerCountActual = numLayers - numBLayers;
+ layerParams.nBLayerCountActual = numBLayers;
+ layerParams.bBitrateRatiosSpecified = OMX_FALSE;
+
+ err = mOMX->setParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+ }
+
+ AString configSchema;
+ if (pattern == OMX_VIDEO_AndroidTemporalLayeringPatternAndroid) {
+ configSchema = AStringPrintf("android.generic.%u+%u", numLayers - numBLayers, numBLayers);
+ } else if (pattern == OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC) {
+ configSchema = AStringPrintf("webrtc.vp8.%u", numLayers);
+ }
+
+ if (err != OK) {
+ ALOGW("Failed to set temporal layers to %s (requested %s)",
+ configSchema.c_str(), tsSchema.c_str());
+ return err;
+ }
+
+ err = mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layerParams, sizeof(layerParams));
+
+ if (err == OK) {
+ ALOGD("Temporal layers requested:%s configured:%s got:%s(%u: P=%u, B=%u)",
+ tsSchema.c_str(), configSchema.c_str(),
+ asString(layerParams.ePattern), layerParams.ePattern,
+ layerParams.nPLayerCountActual, layerParams.nBLayerCountActual);
+
+ if (outputFormat.get() == mOutputFormat.get()) {
+ mOutputFormat = mOutputFormat->dup(); // trigger an output format change event
+ }
+ // assume we got what we configured
+ outputFormat->setString("ts-schema", configSchema);
+ }
+ return err;
+}
+
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -3146,6 +3249,29 @@
return ERROR_UNSUPPORTED;
}
+status_t ACodec::setPortBufferNum(OMX_U32 portIndex, int bufferNum) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+ status_t err;
+ ALOGD("Setting [%s] %s port buffer number: %d", mComponentName.c_str(),
+ portIndex == kPortIndexInput ? "input" : "output", bufferNum);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ return err;
+ }
+ def.nBufferCountActual = bufferNum;
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ if (err != OK) {
+ // Component could reject this request.
+ ALOGW("Fail to set [%s] %s port buffer number: %d", mComponentName.c_str(),
+ portIndex == kPortIndexInput ? "input" : "output", bufferNum);
+ }
+ return OK;
+}
+
status_t ACodec::setupVideoDecoder(
const char *mime, const sp<AMessage> &msg, bool haveNativeWindow,
bool usingSwRenderer, sp<AMessage> &outputFormat) {
@@ -3202,6 +3328,24 @@
return err;
}
+ // Set the component input buffer number to be |tmp|. If succeed,
+ // component will set input port buffer number to be |tmp|. If fail,
+ // component will keep the same buffer number as before.
+ if (msg->findInt32("android._num-input-buffers", &tmp)) {
+ err = setPortBufferNum(kPortIndexInput, tmp);
+ if (err != OK)
+ return err;
+ }
+
+ // Set the component output buffer number to be |tmp|. If succeed,
+ // component will set output port buffer number to be |tmp|. If fail,
+ // component will keep the same buffer number as before.
+ if (msg->findInt32("android._num-output-buffers", &tmp)) {
+ err = setPortBufferNum(kPortIndexOutput, tmp);
+ if (err != OK)
+ return err;
+ }
+
int32_t frameRateInt;
float frameRateFloat;
if (!msg->findFloat("frame-rate", &frameRateFloat)) {
@@ -3728,13 +3872,17 @@
case OMX_VIDEO_CodingVP8:
case OMX_VIDEO_CodingVP9:
- err = setupVPXEncoderParameters(msg);
+ err = setupVPXEncoderParameters(msg, outputFormat);
break;
default:
break;
}
+ if (err != OK) {
+ return err;
+ }
+
// Set up color aspects on input, but propagate them to the output format, as they will
// not be read back from encoder.
err = setColorAspectsForVideoEncoder(msg, outputFormat, inputFormat);
@@ -3753,6 +3901,29 @@
err = OK;
}
+ if (err != OK) {
+ return err;
+ }
+
+ switch (compressionFormat) {
+ case OMX_VIDEO_CodingAVC:
+ case OMX_VIDEO_CodingHEVC:
+ err = configureTemporalLayers(msg, true /* inConfigure */, outputFormat);
+ if (err != OK) {
+ err = OK; // ignore failure
+ }
+ break;
+
+ case OMX_VIDEO_CodingVP8:
+ case OMX_VIDEO_CodingVP9:
+ // TODO: do we need to support android.generic layering? webrtc layering is
+ // already set up in setupVPXEncoderParameters.
+ break;
+
+ default:
+ break;
+ }
+
if (err == OK) {
ALOGI("setupVideoEncoder succeeded");
}
@@ -3797,14 +3968,31 @@
return err;
}
-static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
- if (iFramesInterval < 0) {
- return 0xFFFFFFFF;
- } else if (iFramesInterval == 0) {
+static OMX_U32 setPFramesSpacing(
+ float iFramesInterval /* seconds */, int32_t frameRate, uint32_t BFramesSpacing = 0) {
+ // BFramesSpacing is the number of B frames between I/P frames
+ // PFramesSpacing (the value to be returned) is the number of P frames between I frames
+ //
+ // keyFrameInterval = ((PFramesSpacing + 1) * BFramesSpacing) + PFramesSpacing + 1
+ // ^^^ ^^^ ^^^
+ // number of B frames number of P I frame
+ //
+ // = (PFramesSpacing + 1) * (BFramesSpacing + 1)
+ //
+ // E.g.
+ // I P I : I-interval: 8, nPFrames 1, nBFrames 3
+ // BBB BBB
+
+ if (iFramesInterval < 0) { // just 1 key frame
+ return 0xFFFFFFFE; // don't use maxint as key-frame-interval calculation will add 1
+ } else if (iFramesInterval == 0) { // just key frames
return 0;
}
- OMX_U32 ret = frameRate * iFramesInterval;
- return ret;
+
+ // round down as key-frame-interval is an upper limit
+ uint32_t keyFrameInterval = uint32_t(frameRate * iFramesInterval);
+ OMX_U32 ret = keyFrameInterval / (BFramesSpacing + 1);
+ return ret > 0 ? ret - 1 : 0;
}
static OMX_VIDEO_CONTROLRATETYPE getBitrateMode(const sp<AMessage> &msg) {
@@ -3817,9 +4005,10 @@
}
status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -3852,11 +4041,11 @@
mpeg4type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
- mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ mpeg4type.nBFrames = 0;
+ mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, mpeg4type.nBFrames);
if (mpeg4type.nPFrames == 0) {
mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
- mpeg4type.nBFrames = 0;
mpeg4type.nIDCVLCThreshold = 0;
mpeg4type.bACPred = OMX_TRUE;
mpeg4type.nMaxPacketSize = 256;
@@ -3898,9 +4087,10 @@
}
status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -3929,11 +4119,11 @@
h263type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
- h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h263type.nBFrames = 0;
+ h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h263type.nBFrames);
if (h263type.nPFrames == 0) {
h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
- h263type.nBFrames = 0;
int32_t profile;
if (msg->findInt32("profile", &profile)) {
@@ -4026,9 +4216,10 @@
}
status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -4084,8 +4275,15 @@
h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile);
h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level);
} else {
- // Use baseline profile for AVC recording if profile is not specified.
+ // Use largest supported profile for AVC recording if profile is not specified.
h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+ for (OMX_VIDEO_AVCPROFILETYPE profile : {
+ OMX_VIDEO_AVCProfileHigh, OMX_VIDEO_AVCProfileMain }) {
+ if (verifySupportForProfileAndLevel(profile, 0) == OK) {
+ h264type.eProfile = profile;
+ break;
+ }
+ }
}
ALOGI("setupAVCEncoderParameters with [profile: %s] [level: %s]",
@@ -4096,7 +4294,7 @@
h264type.bUseHadamard = OMX_TRUE;
h264type.nRefFrames = 1;
h264type.nBFrames = 0;
- h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
if (h264type.nPFrames == 0) {
h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
}
@@ -4114,7 +4312,7 @@
h264type.bUseHadamard = OMX_TRUE;
h264type.nRefFrames = 2;
h264type.nBFrames = 1;
- h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
h264type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB;
h264type.nRefIdx10ActiveMinus1 = 0;
@@ -4146,13 +4344,42 @@
return err;
}
+ // TRICKY: if we are enabling temporal layering as well, some codecs may not support layering
+ // when B-frames are enabled. Detect this now so we can disable B frames if temporal layering
+ // is preferred.
+ AString tsSchema;
+ int32_t preferBFrames = (int32_t)false;
+ if (msg->findString("ts-schema", &tsSchema)
+ && (!msg->findInt32("android._prefer-b-frames", &preferBFrames) || !preferBFrames)) {
+ OMX_VIDEO_PARAM_ANDROID_TEMPORALLAYERINGTYPE layering;
+ InitOMXParams(&layering);
+ layering.nPortIndex = kPortIndexOutput;
+ if (mOMX->getParameter(
+ mNode, (OMX_INDEXTYPE)OMX_IndexParamAndroidVideoTemporalLayering,
+ &layering, sizeof(layering)) == OK
+ && layering.eSupportedPatterns
+ && layering.nBLayerCountMax == 0) {
+ h264type.nBFrames = 0;
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
+ h264type.nAllowedPictureTypes &= ~OMX_VIDEO_PictureTypeB;
+ ALOGI("disabling B-frames");
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+ }
+ }
+
return configureBitrate(bitrate, bitrateMode);
}
status_t ACodec::setupHEVCEncoderParameters(const sp<AMessage> &msg) {
- int32_t bitrate, iFrameInterval;
+ int32_t bitrate;
+ float iFrameInterval;
if (!msg->findInt32("bitrate", &bitrate)
- || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ || !msg->findAsFloat("i-frame-interval", &iFrameInterval)) {
return INVALID_OPERATION;
}
@@ -4194,7 +4421,7 @@
hevcType.eLevel = static_cast<OMX_VIDEO_HEVCLEVELTYPE>(level);
}
// TODO: finer control?
- hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate);
+ hevcType.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
err = mOMX->setParameter(
mNode, (OMX_INDEXTYPE)OMX_IndexParamVideoHevc, &hevcType, sizeof(hevcType));
@@ -4205,9 +4432,9 @@
return configureBitrate(bitrate, bitrateMode);
}
-status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg) {
+status_t ACodec::setupVPXEncoderParameters(const sp<AMessage> &msg, sp<AMessage> &outputFormat) {
int32_t bitrate;
- int32_t iFrameInterval = 0;
+ float iFrameInterval = 0;
size_t tsLayers = 0;
OMX_VIDEO_ANDROID_VPXTEMPORALLAYERPATTERNTYPE pattern =
OMX_VIDEO_VPXTemporalLayerPatternNone;
@@ -4221,7 +4448,7 @@
if (!msg->findInt32("bitrate", &bitrate)) {
return INVALID_OPERATION;
}
- msg->findInt32("i-frame-interval", &iFrameInterval);
+ msg->findAsFloat("i-frame-interval", &iFrameInterval);
OMX_VIDEO_CONTROLRATETYPE bitrateMode = getBitrateMode(msg);
@@ -4235,19 +4462,31 @@
}
AString tsSchema;
+ OMX_VIDEO_ANDROID_TEMPORALLAYERINGPATTERNTYPE tsType =
+ OMX_VIDEO_AndroidTemporalLayeringPatternNone;
+
if (msg->findString("ts-schema", &tsSchema)) {
- if (tsSchema == "webrtc.vp8.1-layer") {
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ int tags;
+ char dummy;
+ if (sscanf(tsSchema.c_str(), "webrtc.vp8.%u-layer%c", &numLayers, &dummy) == 1
+ && numLayers > 0) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 1;
- } else if (tsSchema == "webrtc.vp8.2-layer") {
+ tsType = OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC;
+ tsLayers = numLayers;
+ } else if ((tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy))
+ && (tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers) {
pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 2;
- } else if (tsSchema == "webrtc.vp8.3-layer") {
- pattern = OMX_VIDEO_VPXTemporalLayerPatternWebRTC;
- tsLayers = 3;
+ // VPX does not have a concept of B-frames, so just count all layers
+ tsType = OMX_VIDEO_AndroidTemporalLayeringPatternAndroid;
+ tsLayers = numLayers + numBLayers;
} else {
- ALOGW("Unsupported ts-schema [%s]", tsSchema.c_str());
+ ALOGW("Ignoring unsupported ts-schema [%s]", tsSchema.c_str());
}
+ tsLayers = min(tsLayers, (size_t)OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS);
}
OMX_VIDEO_PARAM_ANDROID_VP8ENCODERTYPE vp8type;
@@ -4259,7 +4498,7 @@
if (err == OK) {
if (iFrameInterval > 0) {
- vp8type.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate);
+ vp8type.nKeyFrameInterval = setPFramesSpacing(iFrameInterval, frameRate) + 1;
}
vp8type.eTemporalPattern = pattern;
vp8type.nTemporalLayerCount = tsLayers;
@@ -4279,6 +4518,12 @@
&vp8type, sizeof(vp8type));
if (err != OK) {
ALOGW("Extended VP8 parameters set failed: %d", err);
+ } else if (tsType == OMX_VIDEO_AndroidTemporalLayeringPatternWebRTC) {
+ // advertise even single layer WebRTC layering, as it is defined
+ outputFormat->setString("ts-schema", AStringPrintf("webrtc.vp8.%u-layer", tsLayers));
+ } else if (tsLayers > 0) {
+ // tsType == OMX_VIDEO_AndroidTemporalLayeringPatternAndroid
+ outputFormat->setString("ts-schema", AStringPrintf("android.generic.%u", tsLayers));
}
}
@@ -4809,32 +5054,21 @@
sizeof(vp8type));
if (err == OK) {
- AString tsSchema = "none";
- if (vp8type.eTemporalPattern
- == OMX_VIDEO_VPXTemporalLayerPatternWebRTC) {
- switch (vp8type.nTemporalLayerCount) {
- case 1:
- {
- tsSchema = "webrtc.vp8.1-layer";
- break;
- }
- case 2:
- {
- tsSchema = "webrtc.vp8.2-layer";
- break;
- }
- case 3:
- {
- tsSchema = "webrtc.vp8.3-layer";
- break;
- }
- default:
- {
- break;
- }
+ if (vp8type.eTemporalPattern == OMX_VIDEO_VPXTemporalLayerPatternWebRTC
+ && vp8type.nTemporalLayerCount > 0
+ && vp8type.nTemporalLayerCount
+ <= OMX_VIDEO_ANDROID_MAXVP8TEMPORALLAYERS) {
+ // advertise as android.generic if we configured for android.generic
+ AString origSchema;
+ if (notify->findString("ts-schema", &origSchema)
+ && origSchema.startsWith("android.generic")) {
+ notify->setString("ts-schema", AStringPrintf(
+ "android.generic.%u", vp8type.nTemporalLayerCount));
+ } else {
+ notify->setString("ts-schema", AStringPrintf(
+ "webrtc.vp8.%u-layer", vp8type.nTemporalLayerCount));
}
}
- notify->setString("ts-schema", tsSchema);
}
// Fall through to set up mime.
}
@@ -7301,7 +7535,12 @@
}
}
- return OK;
+ status_t err = configureTemporalLayers(params, false /* inConfigure */, mOutputFormat);
+ if (err != OK) {
+ err = OK; // ignore failure
+ }
+
+ return err;
}
void ACodec::onSignalEndOfInputStream() {
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
index 1458802..0e98db8 100644
--- a/media/libstagefright/AMRExtractor.cpp
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -100,7 +100,10 @@
static status_t getFrameSizeByOffset(const sp<DataSource> &source,
off64_t offset, bool isWide, size_t *frameSize) {
uint8_t header;
- if (source->readAt(offset, &header, 1) < 1) {
+ ssize_t count = source->readAt(offset, &header, 1);
+ if (count == 0) {
+ return ERROR_END_OF_STREAM;
+ } else if (count < 0) {
return ERROR_IO;
}
@@ -140,7 +143,10 @@
if (mDataSource->getSize(&streamSize) == OK) {
while (offset < streamSize) {
- if (getFrameSizeByOffset(source, offset, mIsWide, &frameSize) != OK) {
+ status_t status = getFrameSizeByOffset(source, offset, mIsWide, &frameSize);
+ if (status == ERROR_END_OF_STREAM) {
+ break;
+ } else if (status != OK) {
return;
}
diff --git a/media/libstagefright/CallbackDataSource.cpp b/media/libstagefright/CallbackDataSource.cpp
index f14d34d..0df7da4 100644
--- a/media/libstagefright/CallbackDataSource.cpp
+++ b/media/libstagefright/CallbackDataSource.cpp
@@ -109,6 +109,10 @@
}
}
+sp<DecryptHandle> CallbackDataSource::DrmInitialization(const char *mime) {
+ return mIDataSource->DrmInitialization(mime);
+}
+
TinyCacheSource::TinyCacheSource(const sp<DataSource>& source)
: mSource(source), mCachedOffset(0), mCachedSize(0) {
mName = String8::format("TinyCacheSource(%s)", mSource->toString().string());
@@ -146,12 +150,19 @@
}
}
+
// Fill the cache and copy to the caller.
const ssize_t numRead = mSource->readAt(offset, mCache, kCacheSize);
if (numRead <= 0) {
+ // Flush cache on error
+ mCachedSize = 0;
+ mCachedOffset = 0;
return numRead;
}
if ((size_t)numRead > kCacheSize) {
+ // Flush cache on error
+ mCachedSize = 0;
+ mCachedOffset = 0;
return ERROR_OUT_OF_RANGE;
}
@@ -172,4 +183,11 @@
return mSource->flags();
}
+sp<DecryptHandle> TinyCacheSource::DrmInitialization(const char *mime) {
+ // flush cache when DrmInitialization occurs since decrypted
+ // data may differ from what is in cache.
+ mCachedOffset = 0;
+ mCachedSize = 0;
+ return mSource->DrmInitialization(mime);
+}
} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 58448010..21a5faa 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -75,6 +75,7 @@
virtual sp<MetaData> getFormat();
virtual status_t read(MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual bool supportNonblockingRead() { return true; }
virtual status_t fragmentedRead(MediaBuffer **buffer, const ReadOptions *options = NULL);
protected:
@@ -371,6 +372,7 @@
mInitCheck(NO_INIT),
mHasVideo(false),
mHeaderTimescale(0),
+ mIsQT(false),
mFirstTrack(NULL),
mLastTrack(NULL),
mFileMetaData(new MetaData),
@@ -914,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
@@ -1371,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.
@@ -1383,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]);
@@ -1391,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));
@@ -1401,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) {
@@ -2239,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
@@ -3563,13 +3640,20 @@
// A somewhat arbitrary limit that should be sufficient for 8k video frames
// If you see the message below for a valid input stream: increase the limit
- if (max_size > 64 * 1024 * 1024) {
- ALOGE("bogus max input size: %zu", max_size);
+ const size_t kMaxBufferSize = 64 * 1024 * 1024;
+ if (max_size > kMaxBufferSize) {
+ ALOGE("bogus max input size: %zu > %zu", max_size, kMaxBufferSize);
return ERROR_MALFORMED;
}
- mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(max_size));
+ if (max_size == 0) {
+ ALOGE("zero max input size");
+ return ERROR_MALFORMED;
+ }
+ // Allow up to kMaxBuffers, but not if the total exceeds kMaxBufferSize.
+ const size_t kMaxBuffers = 8;
+ const size_t buffers = min(kMaxBufferSize / max_size, kMaxBuffers);
+ mGroup = new MediaBufferGroup(buffers, max_size);
mSrcBuffer = new (std::nothrow) uint8_t[max_size];
if (mSrcBuffer == NULL) {
// file probably specified a bad max size
@@ -4196,6 +4280,11 @@
CHECK(mStarted);
+ if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+ *out = nullptr;
+ return WOULD_BLOCK;
+ }
+
if (mFirstMoofOffset > 0) {
return fragmentedRead(out, options);
}
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 0180932..66c4ffd 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -17,6 +17,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MPEG4Writer"
+#include <algorithm>
+
#include <arpa/inet.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -28,8 +30,11 @@
#include <utils/Log.h>
+#include <functional>
+
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/ColorUtils.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaBuffer.h>
@@ -43,6 +48,7 @@
#include "include/ESDS.h"
#include "include/HevcUtils.h"
+#include "include/avc_utils.h"
#ifndef __predict_false
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -118,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.
@@ -146,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;
@@ -157,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.
@@ -165,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;
@@ -178,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;
}
@@ -209,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;
}
}
@@ -230,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;
@@ -255,6 +275,7 @@
int32_t mTrackId;
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
+ int64_t mLastDecodingTimeUs;
int64_t mEstimatedTrackSizeBytes;
int64_t mMdatSizeBytes;
@@ -266,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;
@@ -1163,6 +1184,37 @@
}
}
+off64_t MPEG4Writer::addMultipleLengthPrefixedSamples_l(MediaBuffer *buffer) {
+ off64_t old_offset = mOffset;
+
+ const size_t kExtensionNALSearchRange = 64; // bytes to look for non-VCL NALUs
+
+ const uint8_t *dataStart = (const uint8_t *)buffer->data() + buffer->range_offset();
+ const uint8_t *currentNalStart = dataStart;
+ const uint8_t *nextNalStart;
+ const uint8_t *data = dataStart;
+ size_t nextNalSize;
+ size_t searchSize = buffer->range_length() > kExtensionNALSearchRange ?
+ kExtensionNALSearchRange : buffer->range_length();
+
+ while (getNextNALUnit(&data, &searchSize, &nextNalStart,
+ &nextNalSize, true) == OK) {
+ size_t currentNalSize = nextNalStart - currentNalStart - 4 /* strip start-code */;
+ MediaBuffer *nalBuf = new MediaBuffer((void *)currentNalStart, currentNalSize);
+ addLengthPrefixedSample_l(nalBuf);
+ nalBuf->release();
+
+ currentNalStart = nextNalStart;
+ }
+
+ size_t currentNalOffset = currentNalStart - dataStart;
+ buffer->set_range(buffer->range_offset() + currentNalOffset,
+ buffer->range_length() - currentNalOffset);
+ addLengthPrefixedSample_l(buffer);
+
+ return old_offset;
+}
+
off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) {
off64_t old_offset = mOffset;
@@ -1488,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),
@@ -1510,6 +1562,14 @@
mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) ||
!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC);
+ // store temporal layer count
+ if (!mIsAudio) {
+ int32_t count;
+ if (mMeta->findInt32(kKeyTemporalLayerCount, &count) && count > 1) {
+ mOwner->setTemporalLayerCount(count);
+ }
+ }
+
setTimeScale();
}
@@ -1698,7 +1758,7 @@
List<MediaBuffer *>::iterator it = chunk->mSamples.begin();
off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHevc())
- ? addLengthPrefixedSample_l(*it)
+ ? addMultipleLengthPrefixedSamples_l(*it)
: addSample_l(*it);
if (isFirstSample) {
@@ -1888,6 +1948,7 @@
mEstimatedTrackSizeBytes = 0;
mMdatSizeBytes = 0;
mMaxChunkDurationUs = 0;
+ mLastDecodingTimeUs = -1;
pthread_create(&mThread, &attr, ThreadWrapper, this);
pthread_attr_destroy(&attr);
@@ -2472,6 +2533,17 @@
int64_t decodingTimeUs;
CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
decodingTimeUs -= previousPausedDurationUs;
+
+ // ensure non-negative, monotonic decoding time
+ if (mLastDecodingTimeUs < 0) {
+ decodingTimeUs = std::max((int64_t)0, decodingTimeUs);
+ } else {
+ // increase decoding time by at least 1 tick
+ decodingTimeUs = std::max(
+ mLastDecodingTimeUs + divUp(1000000, mTimeScale), decodingTimeUs);
+ }
+
+ mLastDecodingTimeUs = decodingTimeUs;
cttsOffsetTimeUs =
timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
if (WARN_UNLESS(cttsOffsetTimeUs >= 0ll, "for %s track", trackName)) {
@@ -2607,7 +2679,7 @@
trackProgressStatus(timestampUs);
}
if (!hasMultipleTracks) {
- off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addLengthPrefixedSample_l(copy)
+ off64_t offset = (mIsAvc || mIsHevc) ? mOwner->addMultipleLengthPrefixedSamples_l(copy)
: mOwner->addSample_l(copy);
uint32_t count = (mOwner->use32BitFileOffset()
@@ -3326,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/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 1c6c882..92ce88c 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -88,6 +88,7 @@
virtual void close();
virtual uint32_t getFlags();
virtual String8 toString();
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime);
private:
sp<IMemory> mMemory;
@@ -134,6 +135,10 @@
return mName;
}
+sp<DecryptHandle> RemoteDataSource::DrmInitialization(const char *mime) {
+ return mSource->DrmInitialization(mime);
+}
+
// static
sp<IMediaExtractor> MediaExtractor::Create(
const sp<DataSource> &source, const char *mime) {
@@ -146,10 +151,6 @@
ALOGW("creating media extractor in calling process");
return CreateFromService(source, mime);
} else {
- // remote extractor
- ALOGV("get service manager");
- sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
-
// Check if it's WVM, since WVMExtractor needs to be created in the media server process,
// not the extractor process.
String8 mime8;
@@ -160,6 +161,21 @@
return new WVMExtractor(source);
}
+ // Check if it's es-based DRM, since DRMExtractor needs to be created in the media server
+ // process, not the extractor process.
+ if (SniffDRM(source, &mime8, &confidence, &meta)) {
+ const char *drmMime = mime8.string();
+ ALOGV("Detected media content as '%s' with confidence %.2f", drmMime, confidence);
+ if (!strncmp(drmMime, "drm+es_based+", 13)) {
+ // DRMExtractor sets container metadata kKeyIsDRM to 1
+ return new DRMExtractor(source, drmMime + 14);
+ }
+ }
+
+ // remote extractor
+ ALOGV("get service manager");
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
+
if (binder != 0) {
sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
sp<IMediaExtractor> ex = mediaExService->makeExtractor(RemoteDataSource::wrap(source), mime);
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 8935f6a..f0c27ac 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -40,7 +40,7 @@
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
- ".avi", ".mpeg", ".mpg", ".awb", ".mpga"
+ ".avi", ".mpeg", ".mpg", ".awb", ".mpga", ".mov"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index 377f5fd..d2ba02e 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -158,6 +158,12 @@
// TODO: Use Flexible color instead
videoFormat->setInt32("color-format", OMX_COLOR_FormatYUV420Planar);
+ // For the thumbnail extraction case, try to allocate single buffer
+ // in both input and output ports. NOTE: This request may fail if
+ // component requires more than that for decoding.
+ videoFormat->setInt32("android._num-input-buffers", 1);
+ videoFormat->setInt32("android._num-output-buffers", 1);
+
status_t err;
sp<ALooper> looper = new ALooper;
looper->start();
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 4303d09..1c76ad7 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <utility>
+#include <vector>
#include "include/ESDS.h"
#include "include/HevcUtils.h"
@@ -1316,6 +1317,20 @@
}
convertMessageToMetaDataColorAspects(msg, meta);
+
+ AString tsSchema;
+ if (msg->findString("ts-schema", &tsSchema)) {
+ unsigned int numLayers = 0;
+ unsigned int numBLayers = 0;
+ char dummy;
+ int tags = sscanf(tsSchema.c_str(), "android.generic.%u%c%u%c",
+ &numLayers, &dummy, &numBLayers, &dummy);
+ if ((tags == 1 || (tags == 3 && dummy == '+'))
+ && numLayers > 0 && numLayers < UINT32_MAX - numBLayers
+ && numLayers + numBLayers <= INT32_MAX) {
+ meta->setInt32(kKeyTemporalLayerCount, numLayers + numBLayers);
+ }
+ }
} else if (mime.startsWith("audio/")) {
int32_t numChannels;
if (msg->findInt32("channel-count", &numChannels)) {
@@ -1377,24 +1392,24 @@
// reassemble the csd data into its original form
sp<ABuffer> csd0, csd1, csd2;
if (msg->findBuffer("csd-0", &csd0)) {
+ int csd0size = csd0->size();
if (mime == MEDIA_MIMETYPE_VIDEO_AVC) {
sp<ABuffer> csd1;
if (msg->findBuffer("csd-1", &csd1)) {
- char avcc[1024]; // that oughta be enough, right?
- size_t outsize = reassembleAVCC(csd0, csd1, avcc);
- meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
+ std::vector<char> avcc(csd0size + csd1->size() + 1024);
+ size_t outsize = reassembleAVCC(csd0, csd1, avcc.data());
+ meta->setData(kKeyAVCC, kKeyAVCC, avcc.data(), outsize);
}
} else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) {
- int csd0size = csd0->size();
- char esds[csd0size + 31];
+ std::vector<char> esds(csd0size + 31);
// The written ESDS is actually for an audio stream, but it's enough
// for transporting the CSD to muxers.
- reassembleESDS(csd0, esds);
- meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
+ reassembleESDS(csd0, esds.data());
+ meta->setData(kKeyESDS, kKeyESDS, esds.data(), esds.size());
} else if (mime == MEDIA_MIMETYPE_VIDEO_HEVC) {
- uint8_t hvcc[1024]; // that oughta be enough, right?
- size_t outsize = reassembleHVCC(csd0, hvcc, 1024, 4);
- meta->setData(kKeyHVCC, kKeyHVCC, hvcc, outsize);
+ std::vector<uint8_t> hvcc(csd0size + 1024);
+ size_t outsize = reassembleHVCC(csd0, hvcc.data(), hvcc.size(), 4);
+ meta->setData(kKeyHVCC, kKeyHVCC, hvcc.data(), outsize);
} else if (mime == MEDIA_MIMETYPE_VIDEO_VP9) {
meta->setData(kKeyVp9CodecPrivate, 0, csd0->data(), csd0->size());
} else if (mime == MEDIA_MIMETYPE_AUDIO_OPUS) {
diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp
index 38a2a06..1f04fa0 100644
--- a/media/libstagefright/WAVExtractor.cpp
+++ b/media/libstagefright/WAVExtractor.cpp
@@ -70,6 +70,8 @@
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
+ virtual bool supportNonblockingRead() { return true; }
+
protected:
virtual ~WAVSource();
@@ -377,8 +379,8 @@
CHECK(!mStarted);
- mGroup = new MediaBufferGroup;
- mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+ // some WAV files may have large audio buffers that use shared memory transfer.
+ mGroup = new MediaBufferGroup(4 /* buffers */, kMaxFrameSize);
if (mBitsPerSample == 8) {
// As a temporary buffer for 8->16 bit conversion.
@@ -415,6 +417,10 @@
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
+ if (options != nullptr && options->getNonBlocking() && !mGroup->has_buffers()) {
+ return WOULD_BLOCK;
+ }
+
int64_t seekTimeUs;
ReadOptions::SeekMode mode;
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index 61b9bfd..cecc52b 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -122,7 +122,8 @@
mSignalledError(false),
mStride(mWidth){
initPorts(
- kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
+ 1 /* numMinInputBuffers */, kNumBuffers, INPUT_BUF_SIZE,
+ 1 /* numMinOutputBuffers */, kNumBuffers, CODEC_MIME_TYPE);
GETTIME(&mTimeStart, NULL);
@@ -381,6 +382,48 @@
resetPlugin();
}
+bool SoftAVC::getVUIParams() {
+ IV_API_CALL_STATUS_T status;
+ ih264d_ctl_get_vui_params_ip_t s_ctl_get_vui_params_ip;
+ ih264d_ctl_get_vui_params_op_t s_ctl_get_vui_params_op;
+
+ s_ctl_get_vui_params_ip.e_cmd = IVD_CMD_VIDEO_CTL;
+ s_ctl_get_vui_params_ip.e_sub_cmd =
+ (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_GET_VUI_PARAMS;
+
+ s_ctl_get_vui_params_ip.u4_size =
+ sizeof(ih264d_ctl_get_vui_params_ip_t);
+
+ s_ctl_get_vui_params_op.u4_size = sizeof(ih264d_ctl_get_vui_params_op_t);
+
+ status = ivdec_api_function(
+ (iv_obj_t *)mCodecCtx, (void *)&s_ctl_get_vui_params_ip,
+ (void *)&s_ctl_get_vui_params_op);
+
+ if (status != IV_SUCCESS) {
+ ALOGW("Error in getting VUI params: 0x%x",
+ s_ctl_get_vui_params_op.u4_error_code);
+ return false;
+ }
+
+ int32_t primaries = s_ctl_get_vui_params_op.u1_colour_primaries;
+ int32_t transfer = s_ctl_get_vui_params_op.u1_tfr_chars;
+ int32_t coeffs = s_ctl_get_vui_params_op.u1_matrix_coeffs;
+ bool fullRange = s_ctl_get_vui_params_op.u1_video_full_range_flag;
+
+ ColorAspects colorAspects;
+ ColorUtils::convertIsoColorAspectsToCodecAspects(
+ primaries, transfer, coeffs, fullRange, colorAspects);
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspects, mBitstreamColorAspects)) {
+ mBitstreamColorAspects = colorAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return true;
+}
+
bool SoftAVC::setDecodeArgs(
ivd_video_decode_ip_t *ps_dec_ip,
ivd_video_decode_op_t *ps_dec_op,
@@ -606,6 +649,8 @@
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+ getVUIParams();
+
GETTIME(&mTimeEnd, NULL);
/* Compute time taken for decode() */
TIME_DIFF(mTimeStart, mTimeEnd, timeTaken);
@@ -641,16 +686,22 @@
continue;
}
+ // Combine the resolution change and coloraspects change in one PortSettingChange event
+ // if necessary.
if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
uint32_t width = s_dec_op.u4_pic_wd;
uint32_t height = s_dec_op.u4_pic_ht;
bool portWillReset = false;
handlePortSettingsChange(&portWillReset, width, height);
-
if (portWillReset) {
resetDecoder();
return;
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
+ return;
}
if (s_dec_op.u4_output_present) {
@@ -705,6 +756,10 @@
}
}
+int SoftAVC::getColorAspectPreference() {
+ return kPreferBitstream;
+}
+
} // namespace android
android::SoftOMXComponent *createSoftOMXComponent(
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
index c710c76..154ca38 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -59,6 +59,7 @@
virtual void onQueueFilled(OMX_U32 portIndex);
virtual void onPortFlushCompleted(OMX_U32 portIndex);
virtual void onReset();
+ virtual int getColorAspectPreference();
private:
// Number of input and output buffers
enum {
@@ -116,6 +117,8 @@
OMX_BUFFERHEADERTYPE *outHeader,
size_t timeStampIx);
+ bool getVUIParams();
+
DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
};
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index 6ec8c41..9e7a3be 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -1016,10 +1016,10 @@
if ((avcType->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) &&
avcType->nPFrames) {
- mBframes = avcType->nBFrames / avcType->nPFrames;
+ mBframes = avcType->nBFrames;
}
- mIInterval = avcType->nPFrames + avcType->nBFrames;
+ mIInterval = (avcType->nPFrames + 1) * (avcType->nBFrames + 1);
mConstrainedIntraFlag = avcType->bconstIpred;
if (OMX_VIDEO_AVCLoopFilterDisable == avcType->eLoopFilterMode)
@@ -1033,7 +1033,9 @@
|| avcType->bDirect8x8Inference != OMX_FALSE
|| avcType->bDirectSpatialTemporal != OMX_FALSE
|| avcType->nCabacInitIdc != 0) {
- return OMX_ErrorUndefined;
+ // OMX does not allow a way to signal what values are wrong, so it's
+ // best for components to just do best effort in supporting these values
+ ALOGV("ignoring unsupported settings");
}
if (OK != ConvertOmxAvcLevelToAvcSpecLevel(avcType->eLevel, &mAVCEncLevel)) {
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index 7638bb7..8802fad 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -76,7 +76,7 @@
176 /* width */, 144 /* height */,
callbacks, appData, component),
mEncodeMode(COMBINE_MODE_WITH_ERR_RES),
- mIDRFrameRefreshIntervalInSec(1),
+ mKeyFrameInterval(30),
mNumInputFrames(-1),
mStarted(false),
mSawInputEOS(false),
@@ -159,14 +159,7 @@
}
// Set IDR frame refresh interval
- if (mIDRFrameRefreshIntervalInSec < 0) {
- mEncParams->intraPeriod = -1;
- } else if (mIDRFrameRefreshIntervalInSec == 0) {
- mEncParams->intraPeriod = 1; // All I frames
- } else {
- mEncParams->intraPeriod =
- (mIDRFrameRefreshIntervalInSec * mFramerate) >> 16;
- }
+ mEncParams->intraPeriod = mKeyFrameInterval;
mEncParams->numIntraMB = 0;
mEncParams->sceneDetect = PV_ON;
@@ -378,6 +371,8 @@
return OMX_ErrorUndefined;
}
+ mKeyFrameInterval = int32_t(mpeg4type->nPFrames + 1);
+
return OMX_ErrorNone;
}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
index 3389c37..bb6ea92 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.h
@@ -63,7 +63,7 @@
} InputBufferInfo;
MP4EncodingMode mEncodeMode;
- int32_t mIDRFrameRefreshIntervalInSec;
+ int32_t mKeyFrameInterval; // 1: all I-frames, <0: infinite
int64_t mNumInputFrames;
bool mStarted;
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
index 4cde54e..0822c34 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.cpp
@@ -120,6 +120,17 @@
mIsFirst = true;
}
+void *SoftMP3::memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len) {
+ if (len > outHeader->nAllocLen) {
+ ALOGE("memset buffer too small: got %u, expected %zu", outHeader->nAllocLen, len);
+ android_errorWriteLog(0x534e4554, "29422022");
+ notify(OMX_EventError, OMX_ErrorUndefined, OUTPUT_BUFFER_TOO_SMALL, NULL);
+ mSignalledError = true;
+ return NULL;
+ }
+ return memset(outHeader->pBuffer, c, len);
+}
+
OMX_ERRORTYPE SoftMP3::internalGetParameter(
OMX_INDEXTYPE index, OMX_PTR params) {
switch (index) {
@@ -300,7 +311,10 @@
outHeader->nOffset = 0;
outHeader->nFilledLen = kPVMP3DecoderDelay * mNumChannels * sizeof(int16_t);
- memset(outHeader->pBuffer, 0, outHeader->nFilledLen);
+ if (!memsetSafe(outHeader, 0, outHeader->nFilledLen)) {
+ return;
+ }
+
}
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
mSignalledOutputEos = true;
@@ -312,9 +326,9 @@
// if mIsFirst is true as we may not have a valid
// mConfig->samplingRate and mConfig->num_channels?
ALOGV_IF(mIsFirst, "insufficient data for first frame, sending silence");
- memset(outHeader->pBuffer,
- 0,
- mConfig->outputFrameSize * sizeof(int16_t));
+ if (!memsetSafe(outHeader, 0, mConfig->outputFrameSize * sizeof(int16_t))) {
+ return;
+ }
if (inHeader) {
mConfig->inputBufferUsedLength = inHeader->nFilledLen;
diff --git a/media/libstagefright/codecs/mp3dec/SoftMP3.h b/media/libstagefright/codecs/mp3dec/SoftMP3.h
index f9e7b53..3bfa6c7 100644
--- a/media/libstagefright/codecs/mp3dec/SoftMP3.h
+++ b/media/libstagefright/codecs/mp3dec/SoftMP3.h
@@ -72,6 +72,7 @@
void initPorts();
void initDecoder();
+ void *memsetSafe(OMX_BUFFERHEADERTYPE *outHeader, int c, size_t len);
DISALLOW_EVIL_CONSTRUCTORS(SoftMP3);
};
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 8022467..3490008 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -156,7 +156,7 @@
outHeader->nFlags = 0;
outHeader->nFilledLen = (outputBufferWidth() * outputBufferHeight() * 3) / 2;
outHeader->nTimeStamp = *(OMX_TICKS *)mImg->user_priv;
- if (outHeader->nAllocLen >= outHeader->nFilledLen) {
+ if (outputBufferSafe(outHeader)) {
uint8_t *dst = outHeader->pBuffer;
const uint8_t *srcY = (const uint8_t *)mImg->planes[VPX_PLANE_Y];
const uint8_t *srcU = (const uint8_t *)mImg->planes[VPX_PLANE_U];
@@ -166,8 +166,6 @@
size_t srcVStride = mImg->stride[VPX_PLANE_V];
copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
} else {
- ALOGE("b/27597103, buffer too small");
- android_errorWriteLog(0x534e4554, "27597103");
outHeader->nFilledLen = 0;
}
@@ -197,6 +195,25 @@
return true;
}
+bool SoftVPX::outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader) {
+ uint32_t width = outputBufferWidth();
+ uint32_t height = outputBufferHeight();
+ uint64_t nFilledLen = width;
+ nFilledLen *= height;
+ if (nFilledLen > UINT32_MAX / 3) {
+ ALOGE("b/29421675, nFilledLen overflow %llu w %u h %u",
+ (unsigned long long)nFilledLen, width, height);
+ android_errorWriteLog(0x534e4554, "29421675");
+ return false;
+ } else if (outHeader->nAllocLen < outHeader->nFilledLen) {
+ ALOGE("b/27597103, buffer too small");
+ android_errorWriteLog(0x534e4554, "27597103");
+ return false;
+ }
+
+ return true;
+}
+
void SoftVPX::onQueueFilled(OMX_U32 /* portIndex */) {
if (mOutputPortSettingsChange != NONE || mEOSStatus == OUTPUT_FRAMES_FLUSHED) {
return;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.h b/media/libstagefright/codecs/on2/dec/SoftVPX.h
index 8ccbae2..84cf79c 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.h
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.h
@@ -66,6 +66,7 @@
status_t initDecoder();
status_t destroyDecoder();
bool outputBuffers(bool flushDecoder, bool display, bool eos, bool *portWillReset);
+ bool outputBufferSafe(OMX_BUFFERHEADERTYPE *outHeader);
DISALLOW_EVIL_CONSTRUCTORS(SoftVPX);
};
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 37fb33f..a4583d6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -212,6 +212,33 @@
return NULL;
}
+bool AMessage::findAsFloat(const char *name, float *value) const {
+ size_t i = findItemIndex(name, strlen(name));
+ if (i < mNumItems) {
+ const Item *item = &mItems[i];
+ switch (item->mType) {
+ case kTypeFloat:
+ *value = item->u.floatValue;
+ return true;
+ case kTypeDouble:
+ *value = (float)item->u.doubleValue;
+ return true;
+ case kTypeInt64:
+ *value = (float)item->u.int64Value;
+ return true;
+ case kTypeInt32:
+ *value = (float)item->u.int32Value;
+ return true;
+ case kTypeSize:
+ *value = (float)item->u.sizeValue;
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
bool AMessage::contains(const char *name) const {
size_t i = findItemIndex(name, strlen(name));
return i < mNumItems;
diff --git a/media/libstagefright/foundation/MediaBuffer.cpp b/media/libstagefright/foundation/MediaBuffer.cpp
index fa8e241..15d557d 100644
--- a/media/libstagefright/foundation/MediaBuffer.cpp
+++ b/media/libstagefright/foundation/MediaBuffer.cpp
@@ -32,7 +32,6 @@
MediaBuffer::MediaBuffer(void *data, size_t size)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(data),
mSize(size),
@@ -45,7 +44,6 @@
MediaBuffer::MediaBuffer(size_t size)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(size),
@@ -57,8 +55,10 @@
if (size < kSharedMemThreshold) {
mData = malloc(size);
} else {
- sp<MemoryDealer> memoryDealer = new MemoryDealer(size, "MediaBuffer");
- mMemory = memoryDealer->allocate(size);
+ ALOGV("creating memoryDealer");
+ sp<MemoryDealer> memoryDealer =
+ new MemoryDealer(size + sizeof(SharedControl), "MediaBuffer");
+ mMemory = memoryDealer->allocate(size + sizeof(SharedControl));
if (mMemory == NULL) {
ALOGW("Failed to allocate shared memory, trying regular allocation!");
mData = malloc(size);
@@ -66,7 +66,8 @@
ALOGE("Out of memory");
}
} else {
- mData = mMemory->pointer();
+ getSharedControl()->clear();
+ mData = (uint8_t *)mMemory->pointer() + sizeof(SharedControl);
ALOGV("Allocated shared mem buffer of size %zu @ %p", size, mData);
}
}
@@ -74,7 +75,6 @@
MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(NULL),
mSize(1),
@@ -88,7 +88,6 @@
MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
: mObserver(NULL),
- mNextBuffer(NULL),
mRefCount(0),
mData(buffer->data()),
mSize(buffer->size()),
@@ -102,6 +101,14 @@
void MediaBuffer::release() {
if (mObserver == NULL) {
+ if (mMemory.get() != nullptr) {
+ // See if there is a pending release and there are no observers.
+ // Ideally this never happens.
+ while (addPendingRelease(-1) > 0) {
+ __sync_fetch_and_sub(&mRefCount, 1);
+ }
+ addPendingRelease(1);
+ }
CHECK_EQ(mRefCount, 0);
delete this;
return;
@@ -183,6 +190,10 @@
mOriginal->release();
mOriginal = NULL;
}
+
+ if (mMemory.get() != nullptr) {
+ getSharedControl()->setDeadObject();
+ }
}
void MediaBuffer::setObserver(MediaBufferObserver *observer) {
@@ -190,14 +201,6 @@
mObserver = observer;
}
-void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
- mNextBuffer = buffer;
-}
-
-MediaBuffer *MediaBuffer::nextBuffer() {
- return mNextBuffer;
-}
-
int MediaBuffer::refcount() const {
return mRefCount;
}
diff --git a/media/libstagefright/foundation/MediaBufferGroup.cpp b/media/libstagefright/foundation/MediaBufferGroup.cpp
index 9022324..cb78879 100644
--- a/media/libstagefright/foundation/MediaBufferGroup.cpp
+++ b/media/libstagefright/foundation/MediaBufferGroup.cpp
@@ -23,20 +23,65 @@
namespace android {
-MediaBufferGroup::MediaBufferGroup()
- : mFirstBuffer(NULL),
- mLastBuffer(NULL) {
+// std::min is not constexpr in C++11
+template<typename T>
+constexpr T MIN(const T &a, const T &b) { return a <= b ? a : b; }
+
+// MediaBufferGroup may create shared memory buffers at a
+// smaller threshold than an isolated new MediaBuffer.
+static const size_t kSharedMemoryThreshold = MIN(
+ (size_t)MediaBuffer::kSharedMemThreshold, (size_t)(4 * 1024));
+
+MediaBufferGroup::MediaBufferGroup(size_t growthLimit) :
+ mGrowthLimit(growthLimit) {
+}
+
+MediaBufferGroup::MediaBufferGroup(size_t buffers, size_t buffer_size, size_t growthLimit)
+ : mGrowthLimit(growthLimit) {
+
+ if (buffer_size >= kSharedMemoryThreshold) {
+ ALOGD("creating MemoryDealer");
+ // Using a single MemoryDealer is efficient for a group of shared memory objects.
+ // This loop guarantees that we use shared memory (no fallback to malloc).
+
+ size_t alignment = MemoryDealer::getAllocationAlignment();
+ size_t augmented_size = buffer_size + sizeof(MediaBuffer::SharedControl);
+ size_t total = (augmented_size + alignment - 1) / alignment * alignment * buffers;
+ sp<MemoryDealer> memoryDealer = new MemoryDealer(total, "MediaBufferGroup");
+
+ for (size_t i = 0; i < buffers; ++i) {
+ sp<IMemory> mem = memoryDealer->allocate(augmented_size);
+ if (mem.get() == nullptr) {
+ ALOGW("Only allocated %zu shared buffers of size %zu", i, buffer_size);
+ break;
+ }
+ MediaBuffer *buffer = new MediaBuffer(mem);
+ buffer->getSharedControl()->clear();
+ add_buffer(buffer);
+ }
+ return;
+ }
+
+ // Non-shared memory allocation.
+ for (size_t i = 0; i < buffers; ++i) {
+ MediaBuffer *buffer = new MediaBuffer(buffer_size);
+ if (buffer->data() == nullptr) {
+ delete buffer; // don't call release, it's not properly formed
+ ALOGW("Only allocated %zu malloc buffers of size %zu", i, buffer_size);
+ break;
+ }
+ add_buffer(buffer);
+ }
}
MediaBufferGroup::~MediaBufferGroup() {
- MediaBuffer *next;
- for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
- buffer = next) {
- next = buffer->nextBuffer();
-
- CHECK_EQ(buffer->refcount(), 0);
-
- buffer->setObserver(NULL);
+ for (MediaBuffer *buffer : mBuffers) {
+ buffer->resolvePendingRelease();
+ // If we don't release it, perhaps noone will release it.
+ LOG_ALWAYS_FATAL_IF(buffer->refcount() != 0,
+ "buffer refcount %p = %d != 0", buffer, buffer->refcount());
+ // actually delete it.
+ buffer->setObserver(nullptr);
buffer->release();
}
}
@@ -45,87 +90,105 @@
Mutex::Autolock autoLock(mLock);
buffer->setObserver(this);
+ mBuffers.emplace_back(buffer);
+ // optionally: mGrowthLimit = max(mGrowthLimit, mBuffers.size());
+}
- if (mLastBuffer) {
- mLastBuffer->setNextBuffer(buffer);
- } else {
- mFirstBuffer = buffer;
+void MediaBufferGroup::gc(size_t freeBuffers) {
+ Mutex::Autolock autoLock(mLock);
+
+ size_t freeCount = 0;
+ for (auto it = mBuffers.begin(); it != mBuffers.end(); ) {
+ (*it)->resolvePendingRelease();
+ if ((*it)->isDeadObject()) {
+ // The MediaBuffer has been deleted, why is it in the MediaBufferGroup?
+ LOG_ALWAYS_FATAL("buffer(%p) has dead object with refcount %d",
+ (*it), (*it)->refcount());
+ } else if ((*it)->refcount() == 0 && ++freeCount > freeBuffers) {
+ (*it)->setObserver(nullptr);
+ (*it)->release();
+ it = mBuffers.erase(it);
+ } else {
+ ++it;
+ }
}
+}
- mLastBuffer = buffer;
+bool MediaBufferGroup::has_buffers() {
+ if (mBuffers.size() < mGrowthLimit) {
+ return true; // We can add more buffers internally.
+ }
+ for (MediaBuffer *buffer : mBuffers) {
+ buffer->resolvePendingRelease();
+ if (buffer->refcount() == 0) {
+ return true;
+ }
+ }
+ return false;
}
status_t MediaBufferGroup::acquire_buffer(
MediaBuffer **out, bool nonBlocking, size_t requestedSize) {
Mutex::Autolock autoLock(mLock);
-
for (;;) {
- MediaBuffer *freeBuffer = NULL;
- MediaBuffer *freeBufferPrevious = NULL;
- MediaBuffer *buffer = NULL;
- MediaBuffer *bufferPrevious = NULL;
size_t smallest = requestedSize;
- for (buffer = mFirstBuffer;
- buffer != NULL; buffer = buffer->nextBuffer()) {
- if (buffer->refcount() == 0) {
- if (buffer->size() >= requestedSize) {
- break;
- } else if (buffer->size() < smallest) {
- freeBuffer = buffer;
- freeBufferPrevious = bufferPrevious;
- }
+ MediaBuffer *buffer = nullptr;
+ auto free = mBuffers.end();
+ for (auto it = mBuffers.begin(); it != mBuffers.end(); ++it) {
+ (*it)->resolvePendingRelease();
+ if ((*it)->refcount() == 0) {
+ const size_t size = (*it)->size();
+ if (size >= requestedSize) {
+ buffer = *it;
+ break;
+ }
+ if (size < smallest) {
+ smallest = size; // always free the smallest buf
+ free = it;
+ }
}
- bufferPrevious = buffer;
}
-
- if (buffer == NULL && freeBuffer != NULL) {
- ALOGV("allocate new buffer, requested size %zu vs available %zu",
- requestedSize, freeBuffer->size());
- size_t allocateSize = requestedSize;
- if (requestedSize < SIZE_MAX / 3) {
- allocateSize = requestedSize * 3 / 2;
+ if (buffer == nullptr
+ && (free != mBuffers.end() || mBuffers.size() < mGrowthLimit)) {
+ // We alloc before we free so failure leaves group unchanged.
+ const size_t allocateSize = requestedSize < SIZE_MAX / 3 * 2 /* NB: ordering */ ?
+ requestedSize * 3 / 2 : requestedSize;
+ buffer = new MediaBuffer(allocateSize);
+ if (buffer->data() == nullptr) {
+ ALOGE("Allocation failure for size %zu", allocateSize);
+ delete buffer; // Invalid alloc, prefer not to call release.
+ buffer = nullptr;
+ } else {
+ buffer->setObserver(this);
+ if (free != mBuffers.end()) {
+ ALOGV("reallocate buffer, requested size %zu vs available %zu",
+ requestedSize, (*free)->size());
+ (*free)->setObserver(nullptr);
+ (*free)->release();
+ *free = buffer; // in-place replace
+ } else {
+ ALOGV("allocate buffer, requested size %zu", requestedSize);
+ mBuffers.emplace_back(buffer);
+ }
}
- MediaBuffer *newBuffer = new MediaBuffer(allocateSize);
- newBuffer->setObserver(this);
- if (freeBuffer == mFirstBuffer) {
- mFirstBuffer = newBuffer;
- }
- if (freeBuffer == mLastBuffer) {
- mLastBuffer = newBuffer;
- }
- newBuffer->setNextBuffer(freeBuffer->nextBuffer());
- if (freeBufferPrevious != NULL) {
- freeBufferPrevious->setNextBuffer(newBuffer);
- }
- freeBuffer->setObserver(NULL);
- freeBuffer->release();
-
- buffer = newBuffer;
}
-
- if (buffer != NULL) {
+ if (buffer != nullptr) {
buffer->add_ref();
buffer->reset();
-
*out = buffer;
- goto exit;
+ return OK;
}
-
if (nonBlocking) {
- *out = NULL;
+ *out = nullptr;
return WOULD_BLOCK;
}
-
- // All buffers are in use. Block until one of them is returned to us.
+ // All buffers are in use, block until one of them is returned.
mCondition.wait(mLock);
}
-
-exit:
- return OK;
+ // Never gets here.
}
void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
- Mutex::Autolock autoLock(mLock);
mCondition.signal();
}
diff --git a/media/libstagefright/include/CallbackDataSource.h b/media/libstagefright/include/CallbackDataSource.h
index 43e9b8d..d2187d5 100644
--- a/media/libstagefright/include/CallbackDataSource.h
+++ b/media/libstagefright/include/CallbackDataSource.h
@@ -41,6 +41,7 @@
virtual String8 toString() {
return mName;
}
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
private:
sp<IDataSource> mIDataSource;
@@ -68,6 +69,7 @@
virtual String8 toString() {
return mName;
}
+ virtual sp<DecryptHandle> DrmInitialization(const char *mime = NULL);
private:
// 2kb comes from experimenting with the time-to-first-frame from a MediaPlayer
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/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
index 4529007..c9fd745 100644
--- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
+++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
@@ -21,6 +21,7 @@
#include "SimpleSoftOMXComponent.h"
#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/ColorUtils.h>
#include <media/IOMX.h>
#include <utils/RefBase.h>
@@ -43,6 +44,16 @@
OMX_COMPONENTTYPE **component);
protected:
+ enum {
+ kDescribeColorAspectsIndex = kPrepareForAdaptivePlaybackIndex + 1,
+ };
+
+ enum {
+ kNotSupported,
+ kPreferBitstream,
+ kPreferContainer,
+ };
+
virtual void onPortEnableCompleted(OMX_U32 portIndex, bool enabled);
virtual void onReset();
@@ -55,15 +66,37 @@
virtual OMX_ERRORTYPE getConfig(
OMX_INDEXTYPE index, OMX_PTR params);
+ virtual OMX_ERRORTYPE setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params);
+
virtual OMX_ERRORTYPE getExtensionIndex(
const char *name, OMX_INDEXTYPE *index);
+ virtual bool supportsDescribeColorAspects();
+
+ virtual int getColorAspectPreference();
+
+ // This function sets both minimum buffer count and actual buffer count of
+ // input port to be |numInputBuffers|. It will also set both minimum buffer
+ // count and actual buffer count of output port to be |numOutputBuffers|.
void initPorts(OMX_U32 numInputBuffers,
OMX_U32 inputBufferSize,
OMX_U32 numOutputBuffers,
const char *mimeType,
OMX_U32 minCompressionRatio = 1u);
+ // This function sets input port's minimum buffer count to |numMinInputBuffers|,
+ // sets input port's actual buffer count to |numInputBuffers|, sets output port's
+ // minimum buffer count to |numMinOutputBuffers| and sets output port's actual buffer
+ // count to be |numOutputBuffers|.
+ void initPorts(OMX_U32 numMinInputBuffers,
+ OMX_U32 numInputBuffers,
+ OMX_U32 inputBufferSize,
+ OMX_U32 numMinOutputBuffers,
+ OMX_U32 numOutputBuffers,
+ const char *mimeType,
+ OMX_U32 minCompressionRatio = 1u);
+
virtual void updatePortDefinitions(bool updateCrop = true, bool updateInputSize = false);
uint32_t outputBufferWidth();
@@ -74,6 +107,10 @@
kCropSet,
kCropChanged,
};
+
+ // This function will handle several port change events which include
+ // size changed, crop changed, stride changed and coloraspects changed.
+ // It will trigger OMX_EventPortSettingsChanged event if necessary.
void handlePortSettingsChange(
bool *portWillReset, uint32_t width, uint32_t height,
CropSettingsMode cropSettingsMode = kCropUnSet, bool fakeStride = false);
@@ -99,6 +136,29 @@
AWAITING_ENABLED
} mOutputPortSettingsChange;
+ bool mUpdateColorAspects;
+
+ Mutex mColorAspectsLock;
+ // color aspects passed from the framework.
+ ColorAspects mDefaultColorAspects;
+ // color aspects parsed from the bitstream.
+ ColorAspects mBitstreamColorAspects;
+ // final color aspects after combining the above two aspects.
+ ColorAspects mFinalColorAspects;
+
+ bool colorAspectsDiffer(const ColorAspects &a, const ColorAspects &b);
+
+ // This functions takes two color aspects and updates the mFinalColorAspects
+ // based on |preferredAspects|.
+ void updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects);
+
+ // This function will update the mFinalColorAspects based on codec preference.
+ status_t handleColorAspectsChange();
+
+ // Helper function to dump the ColorAspects.
+ void dumpColorAspects(const ColorAspects &colorAspects);
+
private:
uint32_t mMinInputBufferSize;
uint32_t mMinCompressionRatio;
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/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index d3553bd..409cef7 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -68,6 +68,11 @@
mCodingType(codingType),
mProfileLevels(profileLevels),
mNumProfileLevels(numProfileLevels) {
+
+ // init all the color aspects to be Unspecified.
+ memset(&mDefaultColorAspects, 0, sizeof(ColorAspects));
+ memset(&mBitstreamColorAspects, 0, sizeof(ColorAspects));
+ memset(&mFinalColorAspects, 0, sizeof(ColorAspects));
}
void SoftVideoDecoderOMXComponent::initPorts(
@@ -76,6 +81,18 @@
OMX_U32 numOutputBuffers,
const char *mimeType,
OMX_U32 minCompressionRatio) {
+ initPorts(numInputBuffers, numInputBuffers, inputBufferSize,
+ numOutputBuffers, numOutputBuffers, mimeType, minCompressionRatio);
+}
+
+void SoftVideoDecoderOMXComponent::initPorts(
+ OMX_U32 numMinInputBuffers,
+ OMX_U32 numInputBuffers,
+ OMX_U32 inputBufferSize,
+ OMX_U32 numMinOutputBuffers,
+ OMX_U32 numOutputBuffers,
+ const char *mimeType,
+ OMX_U32 minCompressionRatio) {
mMinInputBufferSize = inputBufferSize;
mMinCompressionRatio = minCompressionRatio;
@@ -84,8 +101,8 @@
def.nPortIndex = kInputPortIndex;
def.eDir = OMX_DirInput;
- def.nBufferCountMin = numInputBuffers;
- def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferCountMin = numMinInputBuffers;
+ def.nBufferCountActual = numInputBuffers;
def.nBufferSize = inputBufferSize;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
@@ -107,8 +124,8 @@
def.nPortIndex = kOutputPortIndex;
def.eDir = OMX_DirOutput;
- def.nBufferCountMin = numOutputBuffers;
- def.nBufferCountActual = def.nBufferCountMin;
+ def.nBufferCountMin = numMinOutputBuffers;
+ def.nBufferCountActual = numOutputBuffers;
def.bEnabled = OMX_TRUE;
def.bPopulated = OMX_FALSE;
def.eDomain = OMX_PortDomainVideo;
@@ -224,9 +241,66 @@
notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
OMX_IndexConfigCommonOutputCrop, NULL);
}
+ } else if (mUpdateColorAspects) {
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ kDescribeColorAspectsIndex, NULL);
+ mUpdateColorAspects = false;
}
}
+void SoftVideoDecoderOMXComponent::dumpColorAspects(const ColorAspects &colorAspects) {
+ ALOGD("dumpColorAspects: (R:%d(%s), P:%d(%s), M:%d(%s), T:%d(%s)) ",
+ colorAspects.mRange, asString(colorAspects.mRange),
+ colorAspects.mPrimaries, asString(colorAspects.mPrimaries),
+ colorAspects.mMatrixCoeffs, asString(colorAspects.mMatrixCoeffs),
+ colorAspects.mTransfer, asString(colorAspects.mTransfer));
+}
+
+bool SoftVideoDecoderOMXComponent::colorAspectsDiffer(
+ const ColorAspects &a, const ColorAspects &b) {
+ if (a.mRange != b.mRange
+ || a.mPrimaries != b.mPrimaries
+ || a.mTransfer != b.mTransfer
+ || a.mMatrixCoeffs != b.mMatrixCoeffs) {
+ return true;
+ }
+ return false;
+}
+
+void SoftVideoDecoderOMXComponent::updateFinalColorAspects(
+ const ColorAspects &otherAspects, const ColorAspects &preferredAspects) {
+ Mutex::Autolock autoLock(mColorAspectsLock);
+ ColorAspects newAspects;
+ newAspects.mRange = preferredAspects.mRange != ColorAspects::RangeUnspecified ?
+ preferredAspects.mRange : otherAspects.mRange;
+ newAspects.mPrimaries = preferredAspects.mPrimaries != ColorAspects::PrimariesUnspecified ?
+ preferredAspects.mPrimaries : otherAspects.mPrimaries;
+ newAspects.mTransfer = preferredAspects.mTransfer != ColorAspects::TransferUnspecified ?
+ preferredAspects.mTransfer : otherAspects.mTransfer;
+ newAspects.mMatrixCoeffs = preferredAspects.mMatrixCoeffs != ColorAspects::MatrixUnspecified ?
+ preferredAspects.mMatrixCoeffs : otherAspects.mMatrixCoeffs;
+
+ // Check to see if need update mFinalColorAspects.
+ if (colorAspectsDiffer(mFinalColorAspects, newAspects)) {
+ mFinalColorAspects = newAspects;
+ mUpdateColorAspects = true;
+ }
+}
+
+status_t SoftVideoDecoderOMXComponent::handleColorAspectsChange() {
+ int perference = getColorAspectPreference();
+ ALOGD("Color Aspects preference: %d ", perference);
+
+ if (perference == kPreferBitstream) {
+ updateFinalColorAspects(mDefaultColorAspects, mBitstreamColorAspects);
+ } else if (perference == kPreferContainer) {
+ updateFinalColorAspects(mBitstreamColorAspects, mDefaultColorAspects);
+ } else {
+ return OMX_ErrorUnsupportedSetting;
+ }
+ return OK;
+}
+
void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer(
uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
size_t srcYStride, size_t srcUStride, size_t srcVStride) {
@@ -450,7 +524,7 @@
OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getConfig(
OMX_INDEXTYPE index, OMX_PTR params) {
- switch (index) {
+ switch ((int)index) {
case OMX_IndexConfigCommonOutputCrop:
{
OMX_CONFIG_RECTTYPE *rectParams = (OMX_CONFIG_RECTTYPE *)params;
@@ -470,22 +544,88 @@
return OMX_ErrorNone;
}
+ case kDescribeColorAspectsIndex:
+ {
+ if (!supportsDescribeColorAspects()) {
+ return OMX_ErrorUnsupportedIndex;
+ }
+
+ DescribeColorAspectsParams* colorAspectsParams =
+ (DescribeColorAspectsParams *)params;
+
+ if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadParameter;
+ }
+
+ colorAspectsParams->sAspects = mFinalColorAspects;
+ if (colorAspectsParams->bRequestingDataSpace || colorAspectsParams->bDataSpaceChanged) {
+ return OMX_ErrorUnsupportedSetting;
+ }
+
+ return OMX_ErrorNone;
+ }
default:
return OMX_ErrorUnsupportedIndex;
}
}
+OMX_ERRORTYPE SoftVideoDecoderOMXComponent::setConfig(
+ OMX_INDEXTYPE index, const OMX_PTR params){
+ switch ((int)index) {
+ case kDescribeColorAspectsIndex:
+ {
+ if (!supportsDescribeColorAspects()) {
+ return OMX_ErrorUnsupportedIndex;
+ }
+ const DescribeColorAspectsParams* colorAspectsParams =
+ (const DescribeColorAspectsParams *)params;
+
+ if (!isValidOMXParam(colorAspectsParams)) {
+ return OMX_ErrorBadParameter;
+ }
+
+ if (colorAspectsParams->nPortIndex != kOutputPortIndex) {
+ return OMX_ErrorBadParameter;
+ }
+
+ // Update color aspects if necessary.
+ if (colorAspectsDiffer(colorAspectsParams->sAspects, mDefaultColorAspects)) {
+ mDefaultColorAspects = colorAspectsParams->sAspects;
+ status_t err = handleColorAspectsChange();
+ CHECK(err == OK);
+ }
+ return OMX_ErrorNone;
+ }
+
+ default:
+ return OMX_ErrorUnsupportedIndex;
+ }
+}
+
+
OMX_ERRORTYPE SoftVideoDecoderOMXComponent::getExtensionIndex(
const char *name, OMX_INDEXTYPE *index) {
if (!strcmp(name, "OMX.google.android.index.prepareForAdaptivePlayback")) {
*(int32_t*)index = kPrepareForAdaptivePlaybackIndex;
return OMX_ErrorNone;
+ } else if (!strcmp(name, "OMX.google.android.index.describeColorAspects")
+ && supportsDescribeColorAspects()) {
+ *(int32_t*)index = kDescribeColorAspectsIndex;
+ return OMX_ErrorNone;
}
return SimpleSoftOMXComponent::getExtensionIndex(name, index);
}
+bool SoftVideoDecoderOMXComponent::supportsDescribeColorAspects() {
+ return getColorAspectPreference() != kNotSupported;
+}
+
+int SoftVideoDecoderOMXComponent::getColorAspectPreference() {
+ return kNotSupported;
+}
+
void SoftVideoDecoderOMXComponent::onReset() {
mOutputPortSettingsChange = NONE;
}
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 98498e9..8b0331a 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "ASessionDescription"
#include <utils/Log.h>
+#include <cutils/log.h>
#include "ASessionDescription.h"
@@ -211,13 +212,15 @@
*PT = x;
- char key[20];
- sprintf(key, "a=rtpmap:%lu", x);
-
- CHECK(findAttribute(index, key, desc));
-
- sprintf(key, "a=fmtp:%lu", x);
- if (!findAttribute(index, key, params)) {
+ char key[32];
+ snprintf(key, sizeof(key), "a=rtpmap:%lu", x);
+ 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();
}
}
@@ -228,8 +231,11 @@
*width = 0;
*height = 0;
- char key[20];
- sprintf(key, "a=framesize:%lu", PT);
+ char key[33];
+ snprintf(key, sizeof(key), "a=framesize:%lu", PT);
+ if (PT > 9999999) {
+ android_errorWriteLog(0x534e4554, "25747670");
+ }
AString value;
if (!findAttribute(index, key, &value)) {
return false;
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/media/ndk/Android.mk b/media/ndk/Android.mk
index 7f6b66b..74729e4 100644
--- a/media/ndk/Android.mk
+++ b/media/ndk/Android.mk
@@ -40,7 +40,7 @@
LOCAL_CFLAGS += -fvisibility=hidden -D EXPORT='__attribute__ ((visibility ("default")))'
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall
LOCAL_SHARED_LIBRARIES := \
libbinder \
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 4f826e5..8b831f0 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -14,6 +14,8 @@
liblog \
libbinder
+LOCAL_CFLAGS := -Wall -Werror
+
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
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 72be301..a3dcdcf 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2299,6 +2299,12 @@
mCallbackThread->resetDraining();
}
+void AudioFlinger::PlaybackThread::errorCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setAsyncError();
+}
+
void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
@@ -2333,6 +2339,9 @@
case STREAM_CBK_EVENT_DRAIN_READY:
me->drainCallback();
break;
+ case STREAM_CBK_EVENT_ERROR:
+ me->errorCallback();
+ break;
default:
ALOGW("asyncCallback() unknown event %d", event);
break;
@@ -3904,6 +3913,13 @@
broadcast_l();
}
+void AudioFlinger::PlaybackThread::onAsyncError()
+{
+ for (int i = AUDIO_STREAM_SYSTEM; i < (int)AUDIO_STREAM_CNT; i++) {
+ invalidateTracks((audio_stream_type_t)i);
+ }
+}
+
void AudioFlinger::MixerThread::threadLoop_mix()
{
// mix buffers...
@@ -4862,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;
+ }
}
}
@@ -4890,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();
}
@@ -5220,7 +5241,8 @@
: Thread(false /*canCallJava*/),
mPlaybackThread(playbackThread),
mWriteAckSequence(0),
- mDrainSequence(0)
+ mDrainSequence(0),
+ mAsyncError(false)
{
}
@@ -5238,11 +5260,13 @@
while (!exitPending()) {
uint32_t writeAckSequence;
uint32_t drainSequence;
+ bool asyncError;
{
Mutex::Autolock _l(mLock);
while (!((mWriteAckSequence & 1) ||
(mDrainSequence & 1) ||
+ mAsyncError ||
exitPending())) {
mWaitWorkCV.wait(mLock);
}
@@ -5256,6 +5280,8 @@
mWriteAckSequence &= ~1;
drainSequence = mDrainSequence;
mDrainSequence &= ~1;
+ asyncError = mAsyncError;
+ mAsyncError = false;
}
{
sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
@@ -5266,6 +5292,9 @@
if (drainSequence & 1) {
playbackThread->resetDraining(drainSequence >> 1);
}
+ if (asyncError) {
+ playbackThread->onAsyncError();
+ }
}
}
}
@@ -5314,6 +5343,13 @@
}
}
+void AudioFlinger::AsyncCallbackThread::setAsyncError()
+{
+ Mutex::Autolock _l(mLock);
+ mAsyncError = true;
+ mWaitWorkCV.signal();
+}
+
// ----------------------------------------------------------------------------
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
@@ -5429,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;
}
@@ -5437,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) {
@@ -5983,14 +6023,6 @@
for (;;) {
Vector< sp<EffectChain> > effectChains;
- // sleep with mutex unlocked
- if (sleepUs > 0) {
- ATRACE_BEGIN("sleep");
- usleep(sleepUs);
- ATRACE_END();
- sleepUs = 0;
- }
-
// activeTracks accumulates a copy of a subset of mActiveTracks
Vector< sp<RecordTrack> > activeTracks;
@@ -6011,6 +6043,15 @@
break;
}
+ // sleep with mutex unlocked
+ if (sleepUs > 0) {
+ ATRACE_BEGIN("sleepC");
+ mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)sleepUs));
+ ATRACE_END();
+ sleepUs = 0;
+ continue;
+ }
+
// if no active track(s), then standby and release wakelock
size_t size = mActiveTracks.size();
if (size == 0) {
@@ -6034,6 +6075,7 @@
}
bool doBroadcast = false;
+ bool allStopped = true;
for (size_t i = 0; i < size; ) {
activeTrack = mActiveTracks[i];
@@ -6062,15 +6104,18 @@
case TrackBase::STARTING_1:
sleepUs = 10000;
i++;
+ allStopped = false;
continue;
case TrackBase::STARTING_2:
doBroadcast = true;
mStandby = false;
activeTrack->mState = TrackBase::ACTIVE;
+ allStopped = false;
break;
case TrackBase::ACTIVE:
+ allStopped = false;
break;
case TrackBase::IDLE:
@@ -6090,6 +6135,10 @@
fastTrack = activeTrack;
}
}
+
+ if (allStopped) {
+ standbyIfNotAlreadyInStandby();
+ }
if (doBroadcast) {
mStartStopCond.broadcast();
}
@@ -6197,7 +6246,8 @@
mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] = systemTime();
// Update server timestamp with kernel stats
- if (mInput->stream->get_capture_position != nullptr) {
+ if (mInput->stream->get_capture_position != nullptr
+ && mPipeSource.get() == nullptr /* don't obtain for FastCapture, could block */) {
int64_t position, time;
int ret = mInput->stream->get_capture_position(mInput->stream, &position, &time);
if (ret == NO_ERROR) {
@@ -6672,6 +6722,8 @@
}
// note that threadLoop may still be processing the track at this point [without lock]
recordTrack->mState = TrackBase::PAUSING;
+ // signal thread to stop
+ mWaitWorkCV.broadcast();
// do not wait for mStartStopCond if exiting
if (exitPending()) {
return true;
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 1bfbca9..2fd7eeb 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -543,6 +543,7 @@
void resetWriteBlocked(uint32_t sequence);
void drainCallback();
void resetDraining(uint32_t sequence);
+ void errorCallback();
static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
@@ -550,6 +551,7 @@
virtual bool waitingAsyncCallback_l();
virtual bool shouldStandby_l();
virtual void onAddNewTrack_l();
+ void onAsyncError(); // error reported by AsyncCallbackThread
// ThreadBase virtuals
virtual void preExit();
@@ -1044,6 +1046,7 @@
void resetWriteBlocked();
void setDraining(uint32_t sequence);
void resetDraining();
+ void setAsyncError();
private:
const wp<PlaybackThread> mPlaybackThread;
@@ -1057,6 +1060,7 @@
uint32_t mDrainSequence;
Condition mWaitWorkCV;
Mutex mLock;
+ bool mAsyncError;
};
class DuplicatingThread : public MixerThread {
diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk
index 8b45adc..c8e5148 100644
--- a/services/audiopolicy/Android.mk
+++ b/services/audiopolicy/Android.mk
@@ -50,6 +50,7 @@
LOCAL_MODULE:= libaudiopolicyservice
LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_CFLAGS += -Wall -Werror
include $(BUILD_SHARED_LIBRARY)
@@ -102,6 +103,8 @@
LOCAL_CFLAGS += -DUSE_XML_AUDIO_POLICY_CONF
endif #ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)
+LOCAL_CFLAGS += -Wall -Werror
+
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_MODULE:= libaudiopolicymanagerdefault
@@ -125,6 +128,8 @@
$(TOPDIR)frameworks/av/services/audiopolicy/common/include \
$(TOPDIR)frameworks/av/services/audiopolicy/engine/interface
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
LOCAL_MODULE:= libaudiopolicymanager
diff --git a/services/audiopolicy/common/managerdefinitions/Android.mk b/services/audiopolicy/common/managerdefinitions/Android.mk
index 3b4ae6b..d7da0ad 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.mk
+++ b/services/audiopolicy/common/managerdefinitions/Android.mk
@@ -60,6 +60,8 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MODULE := libaudiopolicycomponents
include $(BUILD_STATIC_LIBRARY)
diff --git a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
index b828f81..1612714 100644
--- a/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
+++ b/services/audiopolicy/common/managerdefinitions/include/TypeConverter.h
@@ -131,6 +131,7 @@
typedef TypeConverter<StreamTraits> StreamTypeConverter;
typedef TypeConverter<DeviceCategoryTraits> DeviceCategoryConverter;
+inline
static SampleRateTraits::Collection samplingRatesFromString(const std::string &samplingRates,
const char *del = "|")
{
@@ -139,6 +140,7 @@
return samplingRateCollection;
}
+inline
static FormatTraits::Collection formatsFromString(const std::string &formats, const char *del = "|")
{
FormatTraits::Collection formatCollection;
@@ -146,6 +148,7 @@
return formatCollection;
}
+inline
static audio_format_t formatFromString(const std::string &literalFormat)
{
audio_format_t format;
@@ -156,6 +159,7 @@
return format;
}
+inline
static audio_channel_mask_t channelMaskFromString(const std::string &literalChannels)
{
audio_channel_mask_t channels;
@@ -166,6 +170,7 @@
return channels;
}
+inline
static ChannelTraits::Collection channelMasksFromString(const std::string &channels,
const char *del = "|")
{
@@ -176,6 +181,7 @@
return channelMaskCollection;
}
+inline
static InputChannelTraits::Collection inputChannelMasksFromString(const std::string &inChannels,
const char *del = "|")
{
@@ -185,6 +191,7 @@
return inputChannelMaskCollection;
}
+inline
static OutputChannelTraits::Collection outputChannelMasksFromString(const std::string &outChannels,
const char *del = "|")
{
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 35f078e..50453ad 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -104,7 +104,6 @@
ssize_t DeviceVector::remove(const sp<DeviceDescriptor>& item)
{
- size_t i;
ssize_t ret = indexOf(item);
if (ret < 0) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
index ab2b51f..14caf7c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/VolumeCurve.cpp
@@ -34,7 +34,8 @@
// Where would this volume index been inserted in the curve point
size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
if (indexInUiPosition >= nbCurvePoints) {
- return 0.0f; // out of bounds
+ //use last point of table
+ return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
}
if (indexInUiPosition == 0) {
if (indexInUiPosition != mCurvePoints[0].mIndex) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 0e64716..ff38df4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -775,7 +775,6 @@
const audio_offload_info_t *offloadInfo)
{
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
- uint32_t latency = 0;
status_t status;
#ifdef AUDIO_POLICY_TEST
@@ -1192,7 +1191,7 @@
}
}
}
- uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
+ (void) /*uint32_t muteWaitMs*/ setOutputDevice(outputDesc, device, force, 0, NULL, address);
// handle special case for sonification while in call
if (isInCall()) {
@@ -1288,7 +1287,6 @@
// force restoring the device selection on other active outputs if it differs from the
// one being selected for this output
for (size_t i = 0; i < mOutputs.size(); i++) {
- audio_io_handle_t curOutput = mOutputs.keyAt(i);
sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
if (desc != outputDesc &&
desc->isActive() &&
@@ -1795,7 +1793,7 @@
ssize_t patch_index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (patch_index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patch_index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(patch_index);
patchRemoved = true;
}
@@ -2708,7 +2706,6 @@
true,
NULL);
} else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
- audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
status, patchDesc->mAfPatchHandle);
@@ -3187,6 +3184,7 @@
}
mEngine->setObserver(this);
status_t status = mEngine->initCheck();
+ (void) status;
ALOG_ASSERT(status == NO_ERROR, "Policy engine not initialized(err=%d)", status);
// mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices
@@ -4066,7 +4064,7 @@
ssize_t index = mAudioPatches.indexOfKey(outputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -4095,7 +4093,7 @@
ssize_t index = mAudioPatches.indexOfKey(inputDesc->getPatchHandle());
if (index >= 0) {
sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
- status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ (void) /*status_t status*/ mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
mAudioPatches.removeItemsAt(index);
mpClientInterface->onAudioPatchListUpdate();
}
@@ -5400,7 +5398,6 @@
AudioProfileVector &profiles)
{
String8 reply;
- char *value;
// Format MUST be checked first to update the list of AudioProfile
if (profiles.hasDynamicFormat()) {
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index a6cd50e..f6e24e4 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -52,9 +52,11 @@
static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds
+#ifdef USE_LEGACY_AUDIO_POLICY
namespace {
extern struct audio_policy_service_ops aps_ops;
};
+#endif
// ----------------------------------------------------------------------------
@@ -66,11 +68,6 @@
void AudioPolicyService::onFirstRef()
{
- char value[PROPERTY_VALUE_MAX];
- const struct hw_module_t *module;
- int forced_val;
- int rc;
-
{
Mutex::Autolock _l(mLock);
@@ -85,7 +82,8 @@
ALOGI("AudioPolicyService CSTOR in legacy mode");
/* instantiate the audio policy manager */
- rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
+ const struct hw_module_t *module;
+ int rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc) {
return;
}
@@ -1198,6 +1196,7 @@
int aps_set_voice_volume(void *service, float volume, int delay_ms);
};
+#ifdef USE_LEGACY_AUDIO_POLICY
namespace {
struct audio_policy_service_ops aps_ops = {
.open_output = aps_open_output,
@@ -1220,5 +1219,6 @@
.open_input_on_module = aps_open_input_on_module,
};
}; // namespace <unnamed>
+#endif
}; // namespace android
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
diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk
index e8e18b8..c55ac7f 100644
--- a/services/soundtrigger/Android.mk
+++ b/services/soundtrigger/Android.mk
@@ -40,6 +40,8 @@
LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+LOCAL_CFLAGS := -Wall -Werror
+
LOCAL_MODULE:= libsoundtriggerservice
include $(BUILD_SHARED_LIBRARY)