summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/foundation
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/foundation')
-rw-r--r--media/libstagefright/foundation/ABitReader.cpp68
-rw-r--r--media/libstagefright/foundation/ABuffer.cpp31
-rw-r--r--media/libstagefright/foundation/AHierarchicalStateMachine.cpp2
-rw-r--r--media/libstagefright/foundation/ALooper.cpp8
-rw-r--r--media/libstagefright/foundation/ALooperRoster.cpp67
-rw-r--r--media/libstagefright/foundation/AMessage.cpp122
-rw-r--r--media/libstagefright/foundation/ANetworkSession.cpp6
-rw-r--r--media/libstagefright/foundation/AString.cpp88
-rw-r--r--media/libstagefright/foundation/Android.mk2
-rw-r--r--media/libstagefright/foundation/base64.cpp6
10 files changed, 311 insertions, 89 deletions
diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
index 5499c32..beb5cc0 100644
--- a/media/libstagefright/foundation/ABitReader.cpp
+++ b/media/libstagefright/foundation/ABitReader.cpp
@@ -27,6 +27,9 @@ ABitReader::ABitReader(const uint8_t *data, size_t size)
mNumBitsLeft(0) {
}
+ABitReader::~ABitReader() {
+}
+
void ABitReader::fillReservoir() {
CHECK_GT(mSize, 0u);
@@ -99,4 +102,69 @@ const uint8_t *ABitReader::data() const {
return mData - (mNumBitsLeft + 7) / 8;
}
+NALBitReader::NALBitReader(const uint8_t *data, size_t size)
+ : ABitReader(data, size),
+ mNumZeros(0) {
+}
+
+bool NALBitReader::atLeastNumBitsLeft(size_t n) const {
+ // check against raw size and reservoir bits first
+ size_t numBits = numBitsLeft();
+ if (n > numBits) {
+ return false;
+ }
+
+ ssize_t numBitsRemaining = n - mNumBitsLeft;
+
+ size_t size = mSize;
+ const uint8_t *data = mData;
+ int32_t numZeros = mNumZeros;
+ while (size > 0 && numBitsRemaining > 0) {
+ bool isEmulationPreventionByte = (numZeros >= 2 && *data == 3);
+
+ if (*data == 0) {
+ ++numZeros;
+ } else {
+ numZeros = 0;
+ }
+
+ if (!isEmulationPreventionByte) {
+ numBitsRemaining -= 8;
+ }
+
+ ++data;
+ --size;
+ }
+
+ return (numBitsRemaining <= 0);
+}
+
+void NALBitReader::fillReservoir() {
+ CHECK_GT(mSize, 0u);
+
+ mReservoir = 0;
+ size_t i = 0;
+ while (mSize > 0 && i < 4) {
+ bool isEmulationPreventionByte = (mNumZeros >= 2 && *mData == 3);
+
+ if (*mData == 0) {
+ ++mNumZeros;
+ } else {
+ mNumZeros = 0;
+ }
+
+ // skip emulation_prevention_three_byte
+ if (!isEmulationPreventionByte) {
+ mReservoir = (mReservoir << 8) | *mData;
+ ++i;
+ }
+
+ ++mData;
+ --mSize;
+ }
+
+ mNumBitsLeft = 8 * i;
+ mReservoir <<= 32 - mNumBitsLeft;
+}
+
} // namespace android
diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
index 5f5c977..4913fd4 100644
--- a/media/libstagefright/foundation/ABuffer.cpp
+++ b/media/libstagefright/foundation/ABuffer.cpp
@@ -19,11 +19,13 @@
#include "ADebug.h"
#include "ALooper.h"
#include "AMessage.h"
+#include "MediaBufferBase.h"
namespace android {
ABuffer::ABuffer(size_t capacity)
- : mRangeOffset(0),
+ : mMediaBufferBase(NULL),
+ mRangeOffset(0),
mInt32Data(0),
mOwnsData(true) {
mData = malloc(capacity);
@@ -37,7 +39,8 @@ ABuffer::ABuffer(size_t capacity)
}
ABuffer::ABuffer(void *data, size_t capacity)
- : mData(data),
+ : mMediaBufferBase(NULL),
+ mData(data),
mCapacity(capacity),
mRangeOffset(0),
mRangeLength(capacity),
@@ -45,6 +48,14 @@ ABuffer::ABuffer(void *data, size_t capacity)
mOwnsData(false) {
}
+// static
+sp<ABuffer> ABuffer::CreateAsCopy(const void *data, size_t capacity)
+{
+ sp<ABuffer> res = new ABuffer(capacity);
+ memcpy(res->data(), data, capacity);
+ return res;
+}
+
ABuffer::~ABuffer() {
if (mOwnsData) {
if (mData != NULL) {
@@ -56,6 +67,8 @@ ABuffer::~ABuffer() {
if (mFarewell != NULL) {
mFarewell->post();
}
+
+ setMediaBufferBase(NULL);
}
void ABuffer::setRange(size_t offset, size_t size) {
@@ -77,5 +90,19 @@ sp<AMessage> ABuffer::meta() {
return mMeta;
}
+MediaBufferBase *ABuffer::getMediaBufferBase() {
+ if (mMediaBufferBase != NULL) {
+ mMediaBufferBase->add_ref();
+ }
+ return mMediaBufferBase;
+}
+
+void ABuffer::setMediaBufferBase(MediaBufferBase *mediaBuffer) {
+ if (mMediaBufferBase != NULL) {
+ mMediaBufferBase->release();
+ }
+ mMediaBufferBase = mediaBuffer;
+}
+
} // namespace android
diff --git a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
index f7a00d8..5f7c70d 100644
--- a/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
+++ b/media/libstagefright/foundation/AHierarchicalStateMachine.cpp
@@ -51,7 +51,7 @@ AHierarchicalStateMachine::AHierarchicalStateMachine() {
AHierarchicalStateMachine::~AHierarchicalStateMachine() {
}
-void AHierarchicalStateMachine::onMessageReceived(const sp<AMessage> &msg) {
+void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {
sp<AState> save = mState;
sp<AState> cur = mState;
diff --git a/media/libstagefright/foundation/ALooper.cpp b/media/libstagefright/foundation/ALooper.cpp
index ebf9d8d..88b1c92 100644
--- a/media/libstagefright/foundation/ALooper.cpp
+++ b/media/libstagefright/foundation/ALooper.cpp
@@ -68,14 +68,14 @@ int64_t ALooper::GetNowUs() {
ALooper::ALooper()
: mRunningLocally(false) {
+ // clean up stale AHandlers. Doing it here instead of in the destructor avoids
+ // the side effect of objects being deleted from the unregister function recursively.
+ gLooperRoster.unregisterStaleHandlers();
}
ALooper::~ALooper() {
stop();
-
- // Since this looper is "dead" (or as good as dead by now),
- // have ALooperRoster unregister any handlers still registered for it.
- gLooperRoster.unregisterStaleHandlers();
+ // stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
}
void ALooper::setName(const char *name) {
diff --git a/media/libstagefright/foundation/ALooperRoster.cpp b/media/libstagefright/foundation/ALooperRoster.cpp
index 0c181ff..e0dc768 100644
--- a/media/libstagefright/foundation/ALooperRoster.cpp
+++ b/media/libstagefright/foundation/ALooperRoster.cpp
@@ -72,50 +72,40 @@ void ALooperRoster::unregisterHandler(ALooper::handler_id handlerID) {
}
void ALooperRoster::unregisterStaleHandlers() {
- Mutex::Autolock autoLock(mLock);
- for (size_t i = mHandlers.size(); i-- > 0;) {
- const HandlerInfo &info = mHandlers.valueAt(i);
+ Vector<sp<ALooper> > activeLoopers;
+ {
+ Mutex::Autolock autoLock(mLock);
- sp<ALooper> looper = info.mLooper.promote();
- if (looper == NULL) {
- ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i));
- mHandlers.removeItemsAt(i);
+ for (size_t i = mHandlers.size(); i-- > 0;) {
+ const HandlerInfo &info = mHandlers.valueAt(i);
+
+ sp<ALooper> looper = info.mLooper.promote();
+ if (looper == NULL) {
+ ALOGV("Unregistering stale handler %d", mHandlers.keyAt(i));
+ mHandlers.removeItemsAt(i);
+ } else {
+ // At this point 'looper' might be the only sp<> keeping
+ // the object alive. To prevent it from going out of scope
+ // and having ~ALooper call this method again recursively
+ // and then deadlocking because of the Autolock above, add
+ // it to a Vector which will go out of scope after the lock
+ // has been released.
+ activeLoopers.add(looper);
+ }
}
}
}
status_t ALooperRoster::postMessage(
const sp<AMessage> &msg, int64_t delayUs) {
- Mutex::Autolock autoLock(mLock);
- return postMessage_l(msg, delayUs);
-}
-
-status_t ALooperRoster::postMessage_l(
- const sp<AMessage> &msg, int64_t delayUs) {
- ssize_t index = mHandlers.indexOfKey(msg->target());
- if (index < 0) {
- ALOGW("failed to post message '%s'. Target handler not registered.",
- msg->debugString().c_str());
- return -ENOENT;
- }
-
- const HandlerInfo &info = mHandlers.valueAt(index);
-
- sp<ALooper> looper = info.mLooper.promote();
+ sp<ALooper> looper = findLooper(msg->target());
if (looper == NULL) {
- ALOGW("failed to post message. "
- "Target handler %d still registered, but object gone.",
- msg->target());
-
- mHandlers.removeItemsAt(index);
return -ENOENT;
}
-
looper->post(msg, delayUs);
-
return OK;
}
@@ -169,18 +159,23 @@ sp<ALooper> ALooperRoster::findLooper(ALooper::handler_id handlerID) {
status_t ALooperRoster::postAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response) {
+ sp<ALooper> looper = findLooper(msg->target());
+
+ if (looper == NULL) {
+ ALOGW("failed to post message. "
+ "Target handler %d still registered, but object gone.",
+ msg->target());
+ response->clear();
+ return -ENOENT;
+ }
+
Mutex::Autolock autoLock(mLock);
uint32_t replyID = mNextReplyID++;
msg->setInt32("replyID", replyID);
- status_t err = postMessage_l(msg, 0 /* delayUs */);
-
- if (err != OK) {
- response->clear();
- return err;
- }
+ looper->post(msg, 0 /* delayUs */);
ssize_t index;
while ((index = mReplies.indexOfKey(replyID)) < 0) {
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index dc42f91..795e8a6 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define LOG_TAG "AMessage"
+//#define LOG_NDEBUG 0
+//#define DUMP_STATS
+#include <cutils/log.h>
+
#include "AMessage.h"
#include <ctype.h>
@@ -60,12 +65,14 @@ ALooper::handler_id AMessage::target() const {
void AMessage::clear() {
for (size_t i = 0; i < mNumItems; ++i) {
Item *item = &mItems[i];
- freeItem(item);
+ delete[] item->mName;
+ item->mName = NULL;
+ freeItemValue(item);
}
mNumItems = 0;
}
-void AMessage::freeItem(Item *item) {
+void AMessage::freeItemValue(Item *item) {
switch (item->mType) {
case kTypeString:
{
@@ -88,25 +95,85 @@ void AMessage::freeItem(Item *item) {
}
}
-AMessage::Item *AMessage::allocateItem(const char *name) {
- name = AAtomizer::Atomize(name);
+#ifdef DUMP_STATS
+#include <utils/Mutex.h>
+
+Mutex gLock;
+static int32_t gFindItemCalls = 1;
+static int32_t gDupCalls = 1;
+static int32_t gAverageNumItems = 0;
+static int32_t gAverageNumChecks = 0;
+static int32_t gAverageNumMemChecks = 0;
+static int32_t gAverageDupItems = 0;
+static int32_t gLastChecked = -1;
+
+static void reportStats() {
+ int32_t time = (ALooper::GetNowUs() / 1000);
+ if (time / 1000 != gLastChecked / 1000) {
+ gLastChecked = time;
+ ALOGI("called findItemIx %zu times (for len=%.1f i=%.1f/%.1f mem) dup %zu times (for len=%.1f)",
+ gFindItemCalls,
+ gAverageNumItems / (float)gFindItemCalls,
+ gAverageNumChecks / (float)gFindItemCalls,
+ gAverageNumMemChecks / (float)gFindItemCalls,
+ gDupCalls,
+ gAverageDupItems / (float)gDupCalls);
+ gFindItemCalls = gDupCalls = 1;
+ gAverageNumItems = gAverageNumChecks = gAverageNumMemChecks = gAverageDupItems = 0;
+ gLastChecked = time;
+ }
+}
+#endif
+inline size_t AMessage::findItemIndex(const char *name, size_t len) const {
+#ifdef DUMP_STATS
+ size_t memchecks = 0;
+#endif
size_t i = 0;
- while (i < mNumItems && mItems[i].mName != name) {
- ++i;
+ for (; i < mNumItems; i++) {
+ if (len != mItems[i].mNameLength) {
+ continue;
+ }
+#ifdef DUMP_STATS
+ ++memchecks;
+#endif
+ if (!memcmp(mItems[i].mName, name, len)) {
+ break;
+ }
}
+#ifdef DUMP_STATS
+ {
+ Mutex::Autolock _l(gLock);
+ ++gFindItemCalls;
+ gAverageNumItems += mNumItems;
+ gAverageNumMemChecks += memchecks;
+ gAverageNumChecks += i;
+ reportStats();
+ }
+#endif
+ return i;
+}
+
+// assumes item's name was uninitialized or NULL
+void AMessage::Item::setName(const char *name, size_t len) {
+ mNameLength = len;
+ mName = new char[len + 1];
+ memcpy((void*)mName, name, len + 1);
+}
+AMessage::Item *AMessage::allocateItem(const char *name) {
+ size_t len = strlen(name);
+ size_t i = findItemIndex(name, len);
Item *item;
if (i < mNumItems) {
item = &mItems[i];
- freeItem(item);
+ freeItemValue(item);
} else {
CHECK(mNumItems < kMaxNumItems);
i = mNumItems++;
item = &mItems[i];
-
- item->mName = name;
+ item->setName(name, len);
}
return item;
@@ -114,19 +181,20 @@ AMessage::Item *AMessage::allocateItem(const char *name) {
const AMessage::Item *AMessage::findItem(
const char *name, Type type) const {
- name = AAtomizer::Atomize(name);
-
- for (size_t i = 0; i < mNumItems; ++i) {
+ size_t i = findItemIndex(name, strlen(name));
+ if (i < mNumItems) {
const Item *item = &mItems[i];
+ return item->mType == type ? item : NULL;
- if (item->mName == name) {
- return item->mType == type ? item : NULL;
- }
}
-
return NULL;
}
+bool AMessage::contains(const char *name) const {
+ size_t i = findItemIndex(name, strlen(name));
+ return i < mNumItems;
+}
+
#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \
void AMessage::set##NAME(const char *name, TYPENAME value) { \
Item *item = allocateItem(name); \
@@ -160,6 +228,11 @@ void AMessage::setString(
item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}
+void AMessage::setString(
+ const char *name, const AString &s) {
+ setString(name, s.c_str(), s.size());
+}
+
void AMessage::setObjectInternal(
const char *name, const sp<RefBase> &obj, Type type) {
Item *item = allocateItem(name);
@@ -278,11 +351,20 @@ sp<AMessage> AMessage::dup() const {
sp<AMessage> msg = new AMessage(mWhat, mTarget);
msg->mNumItems = mNumItems;
+#ifdef DUMP_STATS
+ {
+ Mutex::Autolock _l(gLock);
+ ++gDupCalls;
+ gAverageDupItems += mNumItems;
+ reportStats();
+ }
+#endif
+
for (size_t i = 0; i < mNumItems; ++i) {
const Item *from = &mItems[i];
Item *to = &msg->mItems[i];
- to->mName = from->mName;
+ to->setName(from->mName, from->mNameLength);
to->mType = from->mType;
switch (from->mType) {
@@ -403,7 +485,7 @@ AString AMessage::debugString(int32_t indent) const {
{
sp<ABuffer> buffer = static_cast<ABuffer *>(item.u.refValue);
- if (buffer != NULL && buffer->size() <= 64) {
+ if (buffer != NULL && buffer->data() != NULL && buffer->size() <= 64) {
tmp = StringPrintf("Buffer %s = {\n", item.mName);
hexdump(buffer->data(), buffer->size(), indent + 4, &tmp);
appendIndent(&tmp, indent + 2);
@@ -453,11 +535,11 @@ sp<AMessage> AMessage::FromParcel(const Parcel &parcel) {
sp<AMessage> msg = new AMessage(what);
msg->mNumItems = static_cast<size_t>(parcel.readInt32());
-
for (size_t i = 0; i < msg->mNumItems; ++i) {
Item *item = &msg->mItems[i];
- item->mName = AAtomizer::Atomize(parcel.readCString());
+ const char *name = parcel.readCString();
+ item->setName(name, strlen(name));
item->mType = static_cast<Type>(parcel.readInt32());
switch (item->mType) {
diff --git a/media/libstagefright/foundation/ANetworkSession.cpp b/media/libstagefright/foundation/ANetworkSession.cpp
index e629588..4504c2b 100644
--- a/media/libstagefright/foundation/ANetworkSession.cpp
+++ b/media/libstagefright/foundation/ANetworkSession.cpp
@@ -521,7 +521,7 @@ status_t ANetworkSession::Session::readMore() {
return err;
}
-void ANetworkSession::Session::dumpFragmentStats(const Fragment &frag) {
+void ANetworkSession::Session::dumpFragmentStats(const Fragment & /* frag */) {
#if 0
int64_t nowUs = ALooper::GetNowUs();
int64_t delayMs = (nowUs - frag.mTimeUs) / 1000ll;
@@ -579,7 +579,7 @@ status_t ANetworkSession::Session::writeMore() {
if (err == -EAGAIN) {
if (!mOutFragments.empty()) {
- ALOGI("%d datagrams remain queued.", mOutFragments.size());
+ ALOGI("%zu datagrams remain queued.", mOutFragments.size());
}
err = OK;
}
@@ -623,7 +623,7 @@ status_t ANetworkSession::Session::writeMore() {
CHECK_EQ(mState, CONNECTED);
CHECK(!mOutFragments.empty());
- ssize_t n;
+ ssize_t n = -1;
while (!mOutFragments.empty()) {
const Fragment &frag = *mOutFragments.begin();
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index dee786d..9835ca3 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -20,6 +20,8 @@
#include <stdlib.h>
#include <string.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
#include "ADebug.h"
#include "AString.h"
@@ -48,6 +50,13 @@ AString::AString(const char *s, size_t size)
setTo(s, size);
}
+AString::AString(const String8 &from)
+ : mData(NULL),
+ mSize(0),
+ mAllocSize(1) {
+ setTo(from.string(), from.length());
+}
+
AString::AString(const AString &from)
: mData(NULL),
mSize(0),
@@ -189,64 +198,64 @@ void AString::append(const AString &from, size_t offset, size_t n) {
void AString::append(int x) {
char s[16];
- sprintf(s, "%d", x);
-
+ int result = snprintf(s, sizeof(s), "%d", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(unsigned x) {
char s[16];
- sprintf(s, "%u", x);
-
+ int result = snprintf(s, sizeof(s), "%u", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(long x) {
- char s[16];
- sprintf(s, "%ld", x);
-
+ char s[32];
+ int result = snprintf(s, sizeof(s), "%ld", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(unsigned long x) {
- char s[16];
- sprintf(s, "%lu", x);
-
+ char s[32];
+ int result = snprintf(s, sizeof(s), "%lu", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(long long x) {
char s[32];
- sprintf(s, "%lld", x);
-
+ int result = snprintf(s, sizeof(s), "%lld", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(unsigned long long x) {
char s[32];
- sprintf(s, "%llu", x);
-
+ int result = snprintf(s, sizeof(s), "%llu", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(float x) {
char s[16];
- sprintf(s, "%f", x);
-
+ int result = snprintf(s, sizeof(s), "%f", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(double x) {
char s[16];
- sprintf(s, "%f", x);
-
+ int result = snprintf(s, sizeof(s), "%f", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
void AString::append(void *x) {
- char s[16];
- sprintf(s, "%p", x);
-
+ char s[32];
+ int result = snprintf(s, sizeof(s), "%p", x);
+ CHECK((result > 0) && ((size_t) result) < sizeof(s));
append(s);
}
@@ -298,6 +307,14 @@ int AString::compare(const AString &other) const {
return strcmp(mData, other.mData);
}
+int AString::compareIgnoreCase(const AString &other) const {
+ return strcasecmp(mData, other.mData);
+}
+
+bool AString::equalsIgnoreCase(const AString &other) const {
+ return compareIgnoreCase(other) == 0;
+}
+
void AString::tolower() {
makeMutable();
@@ -320,6 +337,35 @@ bool AString::endsWith(const char *suffix) const {
return !strcmp(mData + mSize - suffixLen, suffix);
}
+bool AString::startsWithIgnoreCase(const char *prefix) const {
+ return !strncasecmp(mData, prefix, strlen(prefix));
+}
+
+bool AString::endsWithIgnoreCase(const char *suffix) const {
+ size_t suffixLen = strlen(suffix);
+
+ if (mSize < suffixLen) {
+ return false;
+ }
+
+ return !strcasecmp(mData + mSize - suffixLen, suffix);
+}
+
+// static
+AString AString::FromParcel(const Parcel &parcel) {
+ size_t size = static_cast<size_t>(parcel.readInt32());
+ return AString(static_cast<const char *>(parcel.readInplace(size)), size);
+}
+
+status_t AString::writeToParcel(Parcel *parcel) const {
+ CHECK_LE(mSize, static_cast<size_t>(INT32_MAX));
+ status_t err = parcel->writeInt32(mSize);
+ if (err == OK) {
+ err = parcel->write(mData, mSize);
+ }
+ return err;
+}
+
AString StringPrintf(const char *format, ...) {
va_list ap;
va_start(ap, format);
diff --git a/media/libstagefright/foundation/Android.mk b/media/libstagefright/foundation/Android.mk
index ad2dab5..90a6a23 100644
--- a/media/libstagefright/foundation/Android.mk
+++ b/media/libstagefright/foundation/Android.mk
@@ -24,7 +24,7 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
liblog
-LOCAL_CFLAGS += -Wno-multichar
+LOCAL_CFLAGS += -Wno-multichar -Werror
LOCAL_MODULE:= libstagefright_foundation
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index d5fb4e0..dcf5bef 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -33,6 +33,10 @@ sp<ABuffer> decodeBase64(const AString &s) {
if (n >= 2 && s.c_str()[n - 2] == '=') {
padding = 2;
+
+ if (n >= 3 && s.c_str()[n - 3] == '=') {
+ padding = 3;
+ }
}
}
@@ -71,7 +75,7 @@ sp<ABuffer> decodeBase64(const AString &s) {
if (((i + 1) % 4) == 0) {
out[j++] = (accum >> 16);
- if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
+ if (j < outLen) { out[j++] = (accum >> 8) & 0xff; }
if (j < outLen) { out[j++] = accum & 0xff; }
accum = 0;