From ab88ea9503e0d84d739e6f232ebe740a5b8959a8 Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Mon, 5 Apr 2010 13:25:12 -0700
Subject: [PATCH 01/10] Remove legacy NO_OPENCORE code bypass from
MediaPlayerService.
Change-Id: I72db73c91673e0d7f1090e3b033ae915337dc16e
---
media/libmediaplayerservice/MediaPlayerService.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 594e01049157e..fed4f95336649 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -228,14 +228,10 @@ MediaPlayerService::~MediaPlayerService()
sp MediaPlayerService::createMediaRecorder(pid_t pid)
{
-#ifndef NO_OPENCORE
sp recorder = new MediaRecorderClient(this, pid);
wp w = recorder;
Mutex::Autolock lock(mLock);
mMediaRecorderClients.add(w);
-#else
- sp recorder = NULL;
-#endif
LOGV("Create new media recorder client from pid %d", pid);
return recorder;
}
From 71c27d991ad9f07cc7e28545bf6cd2b133668cd5 Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Fri, 19 Mar 2010 11:43:15 -0700
Subject: [PATCH 02/10] Various fixes to enable recording on passion and
nexus1.
Change-Id: I75a461c9882e2449082ad754ee7b231c1ceec039
---
cmds/stagefright/record.cpp | 11 +-
media/libstagefright/CameraSource.cpp | 7 -
media/libstagefright/MPEG4Writer.cpp | 225 +++++++++++++++----
media/libstagefright/OMXCodec.cpp | 14 +-
media/libstagefright/omx/OMXNodeInstance.cpp | 6 +
5 files changed, 211 insertions(+), 52 deletions(-)
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 845c85471f920..5a87f4ca2a6e3 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -147,7 +147,7 @@ int main(int argc, char **argv) {
OMXClient client;
CHECK_EQ(client.connect(), OK);
-#if 1
+#if 0
sp source = createSource(argv[1]);
if (source == NULL) {
@@ -165,14 +165,15 @@ int main(int argc, char **argv) {
success = success && meta->findInt32(kKeyHeight, &height);
CHECK(success);
#else
- int width = 800;
+ int width = 720;
int height = 480;
sp decoder = new DummySource(width, height);
#endif
sp enc_meta = new MetaData;
// enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
- enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ // enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
enc_meta->setInt32(kKeyWidth, width);
enc_meta->setInt32(kKeyHeight, height);
@@ -213,6 +214,8 @@ int main(int argc, char **argv) {
#if 0
CameraSource *source = CameraSource::Create();
+ source->start();
+
printf("source = %p\n", source);
for (int i = 0; i < 100; ++i) {
@@ -227,6 +230,8 @@ int main(int argc, char **argv) {
buffer = NULL;
}
+ source->stop();
+
delete source;
source = NULL;
#endif
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 075b1e344480c..f57ddc1f48a93 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -142,13 +142,6 @@ CameraSource::CameraSource(const sp &camera)
mFirstFrameTimeUs(0),
mNumFrames(0),
mStarted(false) {
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.hardware", value, NULL) && !strcmp(value, "sholes")) {
- // The hardware encoder(s) do not support yuv420, but only YCbYCr,
- // fortunately the camera also supports this, so we needn't transcode.
- mCamera->setParameters(String8("preview-format=yuv422i-yuyv"));
- }
-
String8 s = mCamera->getParameters();
printf("params: \"%s\"\n", s.string());
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 2cf0ddf7b69fd..e0e2b939d90fc 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -65,10 +66,15 @@ private:
static void *ThreadWrapper(void *me);
void threadEntry();
+ status_t makeAVCCodecSpecificData(
+ const uint8_t *data, size_t size);
+
Track(const Track &);
Track &operator=(const Track &);
};
+#define USE_NALLEN_FOUR 1
+
MPEG4Writer::MPEG4Writer(const char *filename)
: mFile(fopen(filename, "wb")),
mOffset(0),
@@ -213,23 +219,55 @@ off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
return old_offset;
}
+static void StripStartcode(MediaBuffer *buffer) {
+ if (buffer->range_length() < 4) {
+ return;
+ }
+
+ const uint8_t *ptr =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+ buffer->set_range(
+ buffer->range_offset() + 4, buffer->range_length() - 4);
+ }
+}
+
off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) {
Mutex::Autolock autoLock(mLock);
+ StripStartcode(buffer);
+
off_t old_offset = mOffset;
size_t length = buffer->range_length();
+
+#if USE_NALLEN_FOUR
+ uint8_t x = length >> 24;
+ fwrite(&x, 1, 1, mFile);
+ x = (length >> 16) & 0xff;
+ fwrite(&x, 1, 1, mFile);
+ x = (length >> 8) & 0xff;
+ fwrite(&x, 1, 1, mFile);
+ x = length & 0xff;
+ fwrite(&x, 1, 1, mFile);
+#else
CHECK(length < 65536);
uint8_t x = length >> 8;
fwrite(&x, 1, 1, mFile);
x = length & 0xff;
fwrite(&x, 1, 1, mFile);
+#endif
fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
1, length, mFile);
+#if USE_NALLEN_FOUR
+ mOffset += length + 4;
+#else
mOffset += length + 2;
+#endif
return old_offset;
}
@@ -380,6 +418,60 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) {
return NULL;
}
+status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
+ const uint8_t *data, size_t size) {
+ if (mCodecSpecificData != NULL) {
+ return ERROR_MALFORMED;
+ }
+
+ if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
+ // Must start with a start-code.
+ return ERROR_MALFORMED;
+ }
+
+ size_t picParamOffset = 4;
+ while (picParamOffset + 3 < size
+ && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) {
+ ++picParamOffset;
+ }
+
+ if (picParamOffset + 3 >= size) {
+ // Could not find start-code for pictureParameterSet.
+ return ERROR_MALFORMED;
+ }
+
+ size_t seqParamSetLength = picParamOffset - 4;
+ size_t picParamSetLength = size - picParamOffset - 4;
+
+ mCodecSpecificDataSize =
+ 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2;
+
+ mCodecSpecificData = malloc(mCodecSpecificDataSize);
+ uint8_t *header = (uint8_t *)mCodecSpecificData;
+ header[0] = 1;
+ header[1] = 0x42; // profile
+ header[2] = 0x80;
+ header[3] = 0x1e; // level
+
+#if USE_NALLEN_FOUR
+ header[4] = 0xfc | 3; // length size == 4 bytes
+#else
+ header[4] = 0xfc | 1; // length size == 2 bytes
+#endif
+
+ header[5] = 0xe0 | 1;
+ header[6] = seqParamSetLength >> 8;
+ header[7] = seqParamSetLength & 0xff;
+ memcpy(&header[8], &data[4], seqParamSetLength);
+ header += 8 + seqParamSetLength;
+ header[0] = 1;
+ header[1] = picParamSetLength >> 8;
+ header[2] = picParamSetLength & 0xff;
+ memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength);
+
+ return OK;
+}
+
void MPEG4Writer::Track::threadEntry() {
sp meta = mSource->getFormat();
const char *mime;
@@ -399,54 +491,40 @@ void MPEG4Writer::Track::threadEntry() {
++count;
- if (is_avc && count < 3) {
- size_t size = buffer->range_length();
+ int32_t isCodecConfig;
+ if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
+ && isCodecConfig) {
+ if (is_avc) {
+ status_t err = makeAVCCodecSpecificData(
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
- switch (count) {
- case 1:
- {
- CHECK_EQ(mCodecSpecificData, NULL);
- mCodecSpecificData = malloc(size + 8);
- uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[0] = 1;
- header[1] = 0x42; // profile
- header[2] = 0x80;
- header[3] = 0x1e; // level
- header[4] = 0xfc | 3;
- header[5] = 0xe0 | 1;
- header[6] = size >> 8;
- header[7] = size & 0xff;
- memcpy(&header[8],
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- size);
-
- mCodecSpecificDataSize = size + 8;
+ if (err != OK) {
+ LOGE("failed to parse avc codec specific data.");
+ break;
+ }
+ } else if (is_mpeg4) {
+ if (mCodecSpecificData != NULL) {
break;
}
- case 2:
- {
- size_t offset = mCodecSpecificDataSize;
- mCodecSpecificDataSize += size + 3;
- mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize);
- uint8_t *header = (uint8_t *)mCodecSpecificData;
- header[offset] = 1;
- header[offset + 1] = size >> 8;
- header[offset + 2] = size & 0xff;
- memcpy(&header[offset + 3],
- (const uint8_t *)buffer->data() + buffer->range_offset(),
- size);
- break;
- }
+ mCodecSpecificDataSize = buffer->range_length();
+ mCodecSpecificData = malloc(mCodecSpecificDataSize);
+ memcpy(mCodecSpecificData,
+ (const uint8_t *)buffer->data()
+ + buffer->range_offset(),
+ buffer->range_length());
}
buffer->release();
buffer = NULL;
continue;
- }
+ } else if (count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
+ // The TI mpeg4 encoder does not properly set the
+ // codec-specific-data flag.
- if (mCodecSpecificData == NULL && is_mpeg4) {
const uint8_t *data =
(const uint8_t *)buffer->data() + buffer->range_offset();
@@ -474,13 +552,70 @@ void MPEG4Writer::Track::threadEntry() {
memcpy(mCodecSpecificData, data, offset);
buffer->set_range(buffer->range_offset() + offset, size - offset);
+
+ if (size == offset) {
+ buffer->release();
+ buffer = NULL;
+
+ continue;
+ }
+ } else if (is_avc && count < 3) {
+ // The TI video encoder does not flag codec specific data
+ // as such and also splits up SPS and PPS across two buffers.
+
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ size_t size = buffer->range_length();
+
+ CHECK(count == 2 || mCodecSpecificData == NULL);
+
+ size_t offset = mCodecSpecificDataSize;
+ mCodecSpecificDataSize += size + 4;
+ mCodecSpecificData =
+ realloc(mCodecSpecificData, mCodecSpecificDataSize);
+
+ memcpy((uint8_t *)mCodecSpecificData + offset,
+ "\x00\x00\x00\x01", 4);
+
+ memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size);
+
+ buffer->release();
+ buffer = NULL;
+
+ if (count == 2) {
+ void *tmp = mCodecSpecificData;
+ size = mCodecSpecificDataSize;
+ mCodecSpecificData = NULL;
+ mCodecSpecificDataSize = 0;
+
+ status_t err = makeAVCCodecSpecificData(
+ (const uint8_t *)tmp, size);
+
+ free(tmp);
+ tmp = NULL;
+
+ if (err != OK) {
+ LOGE("failed to parse avc codec specific data.");
+ break;
+ }
+ }
+
+ continue;
}
off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer)
: mOwner->addSample(buffer);
SampleInfo info;
- info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length();
+ info.size = is_avc
+#if USE_NALLEN_FOUR
+ ? buffer->range_length() + 4
+#else
+ ? buffer->range_length() + 2
+#endif
+ : buffer->range_length();
+
info.offset = offset;
int64_t timestampUs;
@@ -733,19 +868,29 @@ void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
mOwner->beginBox("stts");
mOwner->writeInt32(0); // version=0, flags=0
- mOwner->writeInt32(mSampleInfos.size() - 1);
+ mOwner->writeInt32(mSampleInfos.size());
List::iterator it = mSampleInfos.begin();
int64_t last = (*it).timestamp;
+ int64_t lastDuration = 1;
+
++it;
while (it != mSampleInfos.end()) {
mOwner->writeInt32(1);
- mOwner->writeInt32((*it).timestamp - last);
+ lastDuration = (*it).timestamp - last;
+ mOwner->writeInt32(lastDuration);
last = (*it).timestamp;
++it;
}
+
+ // We don't really know how long the last frame lasts, since
+ // there is no frame time after it, just repeat the previous
+ // frame's duration.
+ mOwner->writeInt32(1);
+ mOwner->writeInt32(lastDuration);
+
mOwner->endBox(); // stts
mOwner->beginBox("stsz");
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 603708806557d..41ce239797d87 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -137,6 +137,7 @@ static const CodecInfo kEncoderInfo[] = {
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
};
@@ -679,6 +680,7 @@ static size_t getFrameSize(
case OMX_COLOR_FormatCbYCrY:
return width * height * 2;
+ case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
return (width * height * 3) / 2;
@@ -706,7 +708,7 @@ void OMXCodec::setVideoInputFormat(
OMX_COLOR_FORMATTYPE colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) {
- colorFormat = OMX_COLOR_FormatYCbYCr;
+ colorFormat = OMX_COLOR_FormatYUV420Planar;
}
CHECK_EQ(setVideoPortFormatType(
@@ -764,6 +766,14 @@ void OMXCodec::setVideoInputFormat(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, OK);
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
switch (compressionFormat) {
case OMX_VIDEO_CodingMPEG4:
{
@@ -911,7 +921,7 @@ status_t OMXCodec::setupAVCEncoderParameters() {
CHECK_EQ(err, OK);
bitrateType.eControlRate = OMX_Video_ControlRateVariable;
- bitrateType.nTargetBitrate = 1000000;
+ bitrateType.nTargetBitrate = 3000000;
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoBitrate,
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index c1a010c2cbbf2..5db516e9c7270 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -264,6 +264,8 @@ status_t OMXNodeInstance::useBuffer(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);
@@ -294,6 +296,8 @@ status_t OMXNodeInstance::allocateBuffer(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
*buffer_data = header->pBuffer;
@@ -325,6 +329,8 @@ status_t OMXNodeInstance::allocateBufferWithBackup(
return UNKNOWN_ERROR;
}
+ CHECK_EQ(header->pAppPrivate, buffer_meta);
+
*buffer = header;
addActiveBuffer(portIndex, *buffer);
From e2018ca9ff9234876bb5ba63d2f51b72396c5fca Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Tue, 23 Mar 2010 14:33:02 -0700
Subject: [PATCH 03/10] Remove unnecessary lock from AMRWriter.
Change-Id: Ia02966d936dd8cbb31e92051578a3fa816885710
---
include/media/stagefright/AMRWriter.h | 4 +---
media/libstagefright/AMRWriter.cpp | 26 +++++---------------------
2 files changed, 6 insertions(+), 24 deletions(-)
diff --git a/include/media/stagefright/AMRWriter.h b/include/media/stagefright/AMRWriter.h
index 372909a0bfbbd..34f3c4a9777b2 100644
--- a/include/media/stagefright/AMRWriter.h
+++ b/include/media/stagefright/AMRWriter.h
@@ -42,14 +42,12 @@ protected:
virtual ~AMRWriter();
private:
- Mutex mLock;
-
FILE *mFile;
status_t mInitCheck;
sp mSource;
bool mStarted;
volatile bool mDone;
- bool mReachedEOS;
+ volatile bool mReachedEOS;
pthread_t mThread;
static void *ThreadWrapper(void *);
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index bf4424b23e217..73ea56d2b612f 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -53,8 +53,6 @@ status_t AMRWriter::initCheck() const {
}
status_t AMRWriter::addSource(const sp &source) {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -95,8 +93,6 @@ status_t AMRWriter::addSource(const sp &source) {
}
status_t AMRWriter::start() {
- Mutex::Autolock autoLock(mLock);
-
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -127,16 +123,12 @@ status_t AMRWriter::start() {
}
void AMRWriter::stop() {
- {
- Mutex::Autolock autoLock(mLock);
-
- if (!mStarted) {
- return;
- }
-
- mDone = true;
+ if (!mStarted) {
+ return;
}
+ mDone = true;
+
void *dummy;
pthread_join(mThread, &dummy);
@@ -153,13 +145,7 @@ void *AMRWriter::ThreadWrapper(void *me) {
}
void AMRWriter::threadFunc() {
- for (;;) {
- Mutex::Autolock autoLock(mLock);
-
- if (mDone) {
- break;
- }
-
+ while (!mDone) {
MediaBuffer *buffer;
status_t err = mSource->read(&buffer);
@@ -184,12 +170,10 @@ void AMRWriter::threadFunc() {
buffer = NULL;
}
- Mutex::Autolock autoLock(mLock);
mReachedEOS = true;
}
bool AMRWriter::reachedEOS() {
- Mutex::Autolock autoLock(mLock);
return mReachedEOS;
}
From f4e5baa1e9ba464de8c1cd8cfeda29c95f3fe81f Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Fri, 9 Apr 2010 14:25:46 -0700
Subject: [PATCH 04/10] I accidentally broken passion encoding while working
around problems with the sholes encoder.
Change-Id: Id91b837ed17083cb21efb08e1c1ab9cc3ff3fa8f
---
media/libstagefright/MPEG4Writer.cpp | 66 ++++++++++++++++++++++++----
1 file changed, 58 insertions(+), 8 deletions(-)
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index e0e2b939d90fc..5ff2abe54f30f 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -60,6 +60,7 @@ private:
void *mCodecSpecificData;
size_t mCodecSpecificDataSize;
+ bool mGotAllCodecSpecificData;
bool mReachedEOS;
@@ -358,6 +359,7 @@ MPEG4Writer::Track::Track(
mMaxTimeStampUs(0),
mCodecSpecificData(NULL),
mCodecSpecificDataSize(0),
+ mGotAllCodecSpecificData(false),
mReachedEOS(false) {
}
@@ -418,14 +420,58 @@ void *MPEG4Writer::Track::ThreadWrapper(void *me) {
return NULL;
}
+#include
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+
status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
const uint8_t *data, size_t size) {
+ // hexdump(data, size);
+
if (mCodecSpecificData != NULL) {
+ LOGE("Already have codec specific data");
return ERROR_MALFORMED;
}
if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) {
- // Must start with a start-code.
+ LOGE("Must start with a start code");
return ERROR_MALFORMED;
}
@@ -436,7 +482,7 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData(
}
if (picParamOffset + 3 >= size) {
- // Could not find start-code for pictureParameterSet.
+ LOGE("Could not find start-code for pictureParameterSet");
return ERROR_MALFORMED;
}
@@ -494,6 +540,8 @@ void MPEG4Writer::Track::threadEntry() {
int32_t isCodecConfig;
if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig)
&& isCodecConfig) {
+ CHECK(!mGotAllCodecSpecificData);
+
if (is_avc) {
status_t err = makeAVCCodecSpecificData(
(const uint8_t *)buffer->data()
@@ -505,10 +553,6 @@ void MPEG4Writer::Track::threadEntry() {
break;
}
} else if (is_mpeg4) {
- if (mCodecSpecificData != NULL) {
- break;
- }
-
mCodecSpecificDataSize = buffer->range_length();
mCodecSpecificData = malloc(mCodecSpecificDataSize);
memcpy(mCodecSpecificData,
@@ -520,8 +564,10 @@ void MPEG4Writer::Track::threadEntry() {
buffer->release();
buffer = NULL;
+ mGotAllCodecSpecificData = true;
continue;
- } else if (count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
+ } else if (!mGotAllCodecSpecificData &&
+ count == 1 && is_mpeg4 && mCodecSpecificData == NULL) {
// The TI mpeg4 encoder does not properly set the
// codec-specific-data flag.
@@ -559,7 +605,9 @@ void MPEG4Writer::Track::threadEntry() {
continue;
}
- } else if (is_avc && count < 3) {
+
+ mGotAllCodecSpecificData = true;
+ } else if (!mGotAllCodecSpecificData && is_avc && count < 3) {
// The TI video encoder does not flag codec specific data
// as such and also splits up SPS and PPS across two buffers.
@@ -599,6 +647,8 @@ void MPEG4Writer::Track::threadEntry() {
LOGE("failed to parse avc codec specific data.");
break;
}
+
+ mGotAllCodecSpecificData = true;
}
continue;
From 2930bb2d47be279dd228ba8c749c1e39e5da8be1 Mon Sep 17 00:00:00 2001
From: Jean-Michel Trivi
Date: Fri, 9 Apr 2010 19:27:58 -0700
Subject: [PATCH 05/10] Fix monkey bug 2586534
java.util.ConcurrentModificationException Unlike the other audio focus and
media button stack handling methods, abandonAudioFocus() and
unregisterAudioFocusClient() were not synchronized around their focus stack.
This CL corrects this.
Change-Id: I5ada574e4e163fa95da9dad2fefe610b48303320
---
media/java/android/media/AudioService.java | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2b7683ad3a1c1..45497e97e150d 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2090,14 +2090,18 @@ public class AudioService extends IAudioService.Stub {
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
// this will take care of notifying the new focus owner if needed
- removeFocusStackEntry(clientId, true);
+ synchronized(mFocusStack) {
+ removeFocusStackEntry(clientId, true);
+ }
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
public void unregisterAudioFocusClient(String clientId) {
- removeFocusStackEntry(clientId, false);
+ synchronized(mFocusStack) {
+ removeFocusStackEntry(clientId, false);
+ }
}
From f538b0ba7ef4499765cfb5e365c8ca37ea6aa1f0 Mon Sep 17 00:00:00 2001
From: Kenny Root
Date: Fri, 9 Apr 2010 22:45:06 -0700
Subject: [PATCH 06/10] Fix typos in name of density-independent pixels
Some places referred to "dip" as "device-independent pixels" but it
should be "density-independent pixels." Some publications are starting
to refer to this incorrectly.
Bug: 2586742
Change-Id: I030ef45a5ff61622c95133dcb7f2c82c7df652f0
---
docs/html/guide/practices/screens_support.jd | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/html/guide/practices/screens_support.jd b/docs/html/guide/practices/screens_support.jd
index 0fad4c6788e61..5e61e6cc65dc7 100644
--- a/docs/html/guide/practices/screens_support.jd
+++ b/docs/html/guide/practices/screens_support.jd
@@ -116,7 +116,7 @@ screen.
generalized densities: high, medium, and low. Applications can provide custom
resources for each of these three densities — the platform handles the
scaling of the resources up or down to meet the actual screen density.
-Density independent pixel (dip)
+Density-independent pixel (dip)
A virtual pixel unit that applications can use in defining their UI, to
express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160
@@ -432,7 +432,7 @@ does this in three ways:
- Through pre-scaling of drawable resources (scaled at resource loading
time)
-- Through auto-scaling of device-independent pixel (dip) values used in
+
- Through auto-scaling of density-independent pixel (dip) values used in
layouts
- Through auto-scaling of absolute pixel values used in the application (only
needed if the application has set
android:anyDensity="false" in its
@@ -573,7 +573,7 @@ installing the application on small-screen devices.
are signaling to the platform that your application wants to manage its UI by
itself, for all screen densities, using the actual screen dimensions and pixels.
In this case, the application must ensure that it declares its UI dimensions
-using device-independent pixels and scales any actual pixel values or math by
+using density-independent pixels and scales any actual pixel values or math by
the scaling factor available from
{@link android.util.DisplayMetrics#density android.util.DisplayMetrics.density}.
From 8f14c55248f88ebb85fb64bbb8e4f07ddb481952 Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Mon, 12 Apr 2010 10:20:12 -0700
Subject: [PATCH 07/10] Fix a case where the aac hardware decoder is in a
transition state and refused a fillbuffer request.
Change-Id: I4cabd18709c29db0a2763a01cc86525ba0b0aeb7
related-to-bug: 2575976
---
media/libstagefright/OMXCodec.cpp | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 603708806557d..b9d6fbc292152 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -2068,7 +2068,13 @@ void OMXCodec::fillOutputBuffer(BufferInfo *info) {
CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
status_t err = mOMX->fillBuffer(mNode, info->mBuffer);
- CHECK_EQ(err, OK);
+
+ if (err != OK) {
+ CODEC_LOGE("fillBuffer failed w/ error 0x%08x", err);
+
+ setState(ERROR);
+ return;
+ }
info->mOwnedByComponent = true;
}
From 36acfbc2b8df6e62f2d583933eca3a49265279a6 Mon Sep 17 00:00:00 2001
From: Patrick Scott
Date: Fri, 9 Apr 2010 12:42:03 -0400
Subject: [PATCH 08/10] Add a bulk request method for bookmark icons.
Rather than dispatch a message for every url in the db, send a message to the
WebCoreThread to handle the query and iteration. Update the documentation for
requestAllIcons.
Bug: 2581894
Change-Id: I8af4f87570465dff3839db4ac492883e8805b007
---
core/java/android/provider/Browser.java | 25 ++----
core/java/android/webkit/WebIconDatabase.java | 81 +++++++++++++++++--
2 files changed, 79 insertions(+), 27 deletions(-)
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index b466b4041d67a..2fba1d7917cd8 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -574,7 +574,9 @@ public class Browser {
}
/**
- * Request all icons from the database.
+ * Request all icons from the database. This call must either be called
+ * in the main thread or have had Looper.prepare() invoked in the calling
+ * thread.
* Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
* @param cr The ContentResolver used to access the database.
* @param where Clause to be used to limit the query from the database.
@@ -584,25 +586,8 @@ public class Browser {
*/
public static final void requestAllIcons(ContentResolver cr, String where,
WebIconDatabase.IconListener listener) {
- Cursor c = null;
- try {
- c = cr.query(
- BOOKMARKS_URI,
- new String[] { BookmarkColumns.URL },
- where, null, null);
- if (c.moveToFirst()) {
- final WebIconDatabase db = WebIconDatabase.getInstance();
- do {
- db.requestIconForPageUrl(c.getString(0), listener);
- } while (c.moveToNext());
- }
- } catch (IllegalStateException e) {
- Log.e(LOGTAG, "requestAllIcons", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
+ WebIconDatabase.getInstance()
+ .bulkRequestIconForPageUrl(cr, where, listener);
}
public static class BookmarkColumns implements BaseColumns {
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index 6cc6bb4c5575e..bb9ec48882991 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -16,10 +16,15 @@
package android.webkit;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
-import android.graphics.Bitmap;
+import android.provider.Browser;
+import android.util.Log;
+import java.util.HashMap;
import java.util.Vector;
/**
@@ -30,6 +35,7 @@ import java.util.Vector;
* single object.
*/
public final class WebIconDatabase {
+ private static final String LOGTAG = "WebIconDatabase";
// Global instance of a WebIconDatabase
private static WebIconDatabase sIconDatabase;
// EventHandler for handling messages before and after the WebCore thread is
@@ -45,6 +51,7 @@ public final class WebIconDatabase {
static final int REQUEST_ICON = 3;
static final int RETAIN_ICON = 4;
static final int RELEASE_ICON = 5;
+ static final int BULK_REQUEST_ICON = 6;
// Message for dispatching icon request results
private static final int ICON_RESULT = 10;
// Actual handler that runs in WebCore thread
@@ -100,12 +107,11 @@ public final class WebIconDatabase {
case REQUEST_ICON:
IconListener l = (IconListener) msg.obj;
String url = msg.getData().getString("url");
- Bitmap icon = nativeIconForPageUrl(url);
- if (icon != null) {
- EventHandler.this.sendMessage(
- Message.obtain(null, ICON_RESULT,
- new IconResult(url, icon, l)));
- }
+ requestIconAndSendResult(url, l);
+ break;
+
+ case BULK_REQUEST_ICON:
+ bulkRequestIcons(msg);
break;
case RETAIN_ICON:
@@ -126,6 +132,10 @@ public final class WebIconDatabase {
}
}
+ private synchronized boolean hasHandler() {
+ return mHandler != null;
+ }
+
private synchronized void postMessage(Message msg) {
if (mMessages != null) {
mMessages.add(msg);
@@ -133,6 +143,39 @@ public final class WebIconDatabase {
mHandler.sendMessage(msg);
}
}
+
+ private void bulkRequestIcons(Message msg) {
+ HashMap map = (HashMap) msg.obj;
+ IconListener listener = (IconListener) map.get("listener");
+ ContentResolver cr = (ContentResolver) map.get("contentResolver");
+ String where = (String) map.get("where");
+
+ Cursor c = null;
+ try {
+ c = cr.query(
+ Browser.BOOKMARKS_URI,
+ new String[] { Browser.BookmarkColumns.URL },
+ where, null, null);
+ if (c.moveToFirst()) {
+ do {
+ String url = c.getString(0);
+ requestIconAndSendResult(url, listener);
+ } while (c.moveToNext());
+ }
+ } catch (IllegalStateException e) {
+ Log.e(LOGTAG, "BulkRequestIcons", e);
+ } finally {
+ if (c != null) c.close();
+ }
+ }
+
+ private void requestIconAndSendResult(String url, IconListener listener) {
+ Bitmap icon = nativeIconForPageUrl(url);
+ if (icon != null) {
+ sendMessage(obtainMessage(ICON_RESULT,
+ new IconResult(url, icon, listener)));
+ }
+ }
}
/**
@@ -192,6 +235,30 @@ public final class WebIconDatabase {
mEventHandler.postMessage(msg);
}
+ /** {@hide}
+ */
+ public void bulkRequestIconForPageUrl(ContentResolver cr, String where,
+ IconListener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ // Special case situation: we don't want to add this message to the
+ // queue if there is no handler because we may never have a real
+ // handler to service the messages and the cursor will never get
+ // closed.
+ if (mEventHandler.hasHandler()) {
+ // Don't use Bundle as it is parcelable.
+ HashMap map = new HashMap();
+ map.put("contentResolver", cr);
+ map.put("where", where);
+ map.put("listener", listener);
+ Message msg =
+ Message.obtain(null, EventHandler.BULK_REQUEST_ICON, map);
+ mEventHandler.postMessage(msg);
+ }
+ }
+
/**
* Retain the icon for the given page url.
* @param url The page's url.
From ea1d6712fe11f59ee9a89cc1cfec3aaa114a5233 Mon Sep 17 00:00:00 2001
From: Andreas Huber
Date: Mon, 12 Apr 2010 13:10:20 -0700
Subject: [PATCH 09/10] Fix a race condition in TimedEventQueue, an event may
be cancelled while we're waiting for its scheduled time to come in which case
we'd be removing it from the queue twice.
Change-Id: I4e42e318fd5373d1f352f54027d4bf823126266d
related-to-bug: 2585276
---
media/libstagefright/TimedEventQueue.cpp | 47 ++++++++++++++-----
.../libstagefright/include/TimedEventQueue.h | 2 +
2 files changed, 38 insertions(+), 11 deletions(-)
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index e62d501a3e784..3de8c1d85971b 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -19,6 +19,7 @@
#define __STDC_LIMIT_MACROS
#include
+//#define LOG_NDEBUG 0
#define LOG_TAG "TimedEventQueue"
#include
@@ -169,6 +170,8 @@ void TimedEventQueue::cancelEvents(
mQueueHeadChangedCondition.signal();
}
+ LOGV("cancelling event %d", (*it).event->eventID());
+
(*it).event->setEventID(0);
it = mQueue.erase(it);
@@ -229,14 +232,16 @@ void TimedEventQueue::threadEntry() {
mQueueNotEmptyCondition.wait(mLock);
}
- List::iterator it;
+ event_id eventID = 0;
for (;;) {
if (mQueue.empty()) {
// The only event in the queue could have been cancelled
// while we were waiting for its scheduled time.
break;
}
- it = mQueue.begin();
+
+ List::iterator it = mQueue.begin();
+ eventID = (*it).event->eventID();
now_us = getRealTimeUs();
int64_t when_us = (*it).realtime_us;
@@ -276,19 +281,39 @@ void TimedEventQueue::threadEntry() {
}
}
- if (mQueue.empty()) {
- continue;
- }
-
- event = (*it).event;
- event->setEventID(0);
- mQueue.erase(it);
+ // The event w/ this id may have been cancelled while we're
+ // waiting for its trigger-time, in that case
+ // removeEventFromQueue_l will return NULL.
+ // Otherwise, the QueueItem will be removed
+ // from the queue and the referenced event returned.
+ event = removeEventFromQueue_l(eventID);
}
- // Fire event with the lock NOT held.
- event->fire(this, now_us);
+ if (event != NULL) {
+ // Fire event with the lock NOT held.
+ event->fire(this, now_us);
+ }
}
}
+sp TimedEventQueue::removeEventFromQueue_l(
+ event_id id) {
+ for (List::iterator it = mQueue.begin();
+ it != mQueue.end(); ++it) {
+ if ((*it).event->eventID() == id) {
+ sp event = (*it).event;
+ event->setEventID(0);
+
+ mQueue.erase(it);
+
+ return event;
+ }
+ }
+
+ LOGW("Event %d was not found in the queue, already cancelled?", id);
+
+ return NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/include/TimedEventQueue.h b/media/libstagefright/include/TimedEventQueue.h
index 21eade3708158..11f844c6d3f29 100644
--- a/media/libstagefright/include/TimedEventQueue.h
+++ b/media/libstagefright/include/TimedEventQueue.h
@@ -121,6 +121,8 @@ private:
static void *ThreadWrapper(void *me);
void threadEntry();
+ sp removeEventFromQueue_l(event_id id);
+
TimedEventQueue(const TimedEventQueue &);
TimedEventQueue &operator=(const TimedEventQueue &);
};
From fc922f115325371aaadd4e423472476303039a72 Mon Sep 17 00:00:00 2001
From: Christopher Tate
Date: Fri, 9 Apr 2010 13:05:16 -0700
Subject: [PATCH 10/10] API CHANGE: remove obsolete constants and hide some
methods
This change removes some unused constants from BackupDataOutput
and hides a few methods that do not actually need to be exposed.
Change-Id: I47a9a107a5b58f4d53b5a2fcf9b73a765b1c5dd8
---
api/current.xml | 35 -------------------
.../android/app/backup/BackupDataOutput.java | 4 +--
2 files changed, 1 insertion(+), 38 deletions(-)
diff --git a/api/current.xml b/api/current.xml
index a2063b592e533..8cffff1b2fe9b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27062,19 +27062,6 @@
deprecated="not deprecated"
visibility="public"
>
-
-
-
-
-
-
-
-