#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
namespace android {
}
}
+status_t getNextNALUnit(
+ const uint8_t **_data, size_t *_size,
+ const uint8_t **nalStart, size_t *nalSize,
+ bool startCodeFollows) {
+ const uint8_t *data = *_data;
+ size_t size = *_size;
+
+ *nalStart = NULL;
+ *nalSize = 0;
+
+ if (size == 0) {
+ return -EAGAIN;
+ }
+
+ // Skip any number of leading 0x00.
+
+ size_t offset = 0;
+ while (offset < size && data[offset] == 0x00) {
+ ++offset;
+ }
+
+ if (offset == size) {
+ return -EAGAIN;
+ }
+
+ // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
+
+ if (offset < 2 || data[offset] != 0x01) {
+ return ERROR_MALFORMED;
+ }
+
+ ++offset;
+
+ size_t startOffset = offset;
+
+ for (;;) {
+ while (offset < size && data[offset] != 0x01) {
+ ++offset;
+ }
+
+ if (offset == size) {
+ if (startCodeFollows) {
+ offset = size + 2;
+ break;
+ }
+
+ return -EAGAIN;
+ }
+
+ if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
+ break;
+ }
+
+ ++offset;
+ }
+
+ size_t endOffset = offset - 2;
+ while (data[endOffset - 1] == 0x00) {
+ --endOffset;
+ }
+
+ *nalStart = &data[startOffset];
+ *nalSize = endOffset - startOffset;
+
+ if (offset + 2 < size) {
+ *_data = &data[offset - 2];
+ *_size = size - offset + 2;
+ } else {
+ *_data = NULL;
+ *_size = 0;
+ }
+
+ return OK;
+}
+
+static sp<ABuffer> FindNAL(
+ const uint8_t *data, size_t size, unsigned nalType,
+ size_t *stopOffset) {
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ if ((nalStart[0] & 0x1f) == nalType) {
+ sp<ABuffer> buffer = new ABuffer(nalSize);
+ memcpy(buffer->data(), nalStart, nalSize);
+ return buffer;
+ }
+ }
+
+ return NULL;
+}
+
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+ const uint8_t *data = accessUnit->data();
+ size_t size = accessUnit->size();
+
+ sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+ if (seqParamSet == NULL) {
+ return NULL;
+ }
+
+ int32_t width, height;
+ FindAVCDimensions(seqParamSet, &width, &height);
+
+ size_t stopOffset;
+ sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+ CHECK(picParamSet != NULL);
+
+ size_t csdSize =
+ 1 + 3 + 1 + 1
+ + 2 * 1 + seqParamSet->size()
+ + 1 + 2 * 1 + picParamSet->size();
+
+ sp<ABuffer> csd = new ABuffer(csdSize);
+ uint8_t *out = csd->data();
+
+ *out++ = 0x01; // configurationVersion
+ memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
+ out += 3;
+ *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
+ *out++ = 0xe0 | 1;
+
+ *out++ = seqParamSet->size() >> 8;
+ *out++ = seqParamSet->size() & 0xff;
+ memcpy(out, seqParamSet->data(), seqParamSet->size());
+ out += seqParamSet->size();
+
+ *out++ = 1;
+
+ *out++ = picParamSet->size() >> 8;
+ *out++ = picParamSet->size() & 0xff;
+ memcpy(out, picParamSet->data(), picParamSet->size());
+
+#if 0
+ LOGI("AVC seq param set");
+ hexdump(seqParamSet->data(), seqParamSet->size());
+#endif
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+ meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+
+ LOGI("found AVC codec config (%d x %d)", width, height);
+
+ return meta;
+}
+
+bool IsIDR(const sp<ABuffer> &buffer) {
+ const uint8_t *data = buffer->data();
+ size_t size = buffer->size();
+
+ bool foundIDR = false;
+
+ const uint8_t *nalStart;
+ size_t nalSize;
+ while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+ CHECK_GT(nalSize, 0u);
+
+ unsigned nalType = nalStart[0] & 0x1f;
+
+ if (nalType == 5) {
+ foundIDR = true;
+ break;
+ }
+ }
+
+ return foundIDR;
+}
+
} // namespace android
unsigned parseUE(ABitReader *br);
+status_t getNextNALUnit(
+ const uint8_t **_data, size_t *_size,
+ const uint8_t **nalStart, size_t *nalSize,
+ bool startCodeFollows = false);
+
+struct MetaData;
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+
+bool IsIDR(const sp<ABuffer> &accessUnit);
+
} // namespace android
#endif // AVC_UTILS_H_
return mFormat;
}
-static status_t getNextNALUnit(
- const uint8_t **_data, size_t *_size,
- const uint8_t **nalStart, size_t *nalSize,
- bool startCodeFollows = false) {
- const uint8_t *data = *_data;
- size_t size = *_size;
-
- *nalStart = NULL;
- *nalSize = 0;
-
- if (size == 0) {
- return -EAGAIN;
- }
-
- // Skip any number of leading 0x00.
-
- size_t offset = 0;
- while (offset < size && data[offset] == 0x00) {
- ++offset;
- }
-
- if (offset == size) {
- return -EAGAIN;
- }
-
- // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
-
- if (offset < 2 || data[offset] != 0x01) {
- return ERROR_MALFORMED;
- }
-
- ++offset;
-
- size_t startOffset = offset;
-
- for (;;) {
- while (offset < size && data[offset] != 0x01) {
- ++offset;
- }
-
- if (offset == size) {
- if (startCodeFollows) {
- offset = size + 2;
- break;
- }
-
- return -EAGAIN;
- }
-
- if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
- break;
- }
-
- ++offset;
- }
-
- size_t endOffset = offset - 2;
- while (data[endOffset - 1] == 0x00) {
- --endOffset;
- }
-
- *nalStart = &data[startOffset];
- *nalSize = endOffset - startOffset;
-
- if (offset + 2 < size) {
- *_data = &data[offset - 2];
- *_size = size - offset + 2;
- } else {
- *_data = NULL;
- *_size = 0;
- }
-
- return OK;
-}
-
void ElementaryStreamQueue::clear() {
mBuffer->setRange(0, 0);
mFormat.clear();
return NULL;
}
-static sp<ABuffer> FindNAL(
- const uint8_t *data, size_t size, unsigned nalType,
- size_t *stopOffset) {
- const uint8_t *nalStart;
- size_t nalSize;
- while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
- if ((nalStart[0] & 0x1f) == nalType) {
- sp<ABuffer> buffer = new ABuffer(nalSize);
- memcpy(buffer->data(), nalStart, nalSize);
- return buffer;
- }
- }
-
- return NULL;
-}
-
-sp<MetaData> ElementaryStreamQueue::MakeAVCCodecSpecificData(
- const sp<ABuffer> &accessUnit) {
- const uint8_t *data = accessUnit->data();
- size_t size = accessUnit->size();
-
- sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
- if (seqParamSet == NULL) {
- return NULL;
- }
-
- int32_t width, height;
- FindAVCDimensions(seqParamSet, &width, &height);
-
- size_t stopOffset;
- sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
- CHECK(picParamSet != NULL);
-
- size_t csdSize =
- 1 + 3 + 1 + 1
- + 2 * 1 + seqParamSet->size()
- + 1 + 2 * 1 + picParamSet->size();
-
- sp<ABuffer> csd = new ABuffer(csdSize);
- uint8_t *out = csd->data();
-
- *out++ = 0x01; // configurationVersion
- memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
- out += 3;
- *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
- *out++ = 0xe0 | 1;
-
- *out++ = seqParamSet->size() >> 8;
- *out++ = seqParamSet->size() & 0xff;
- memcpy(out, seqParamSet->data(), seqParamSet->size());
- out += seqParamSet->size();
-
- *out++ = 1;
-
- *out++ = picParamSet->size() >> 8;
- *out++ = picParamSet->size() & 0xff;
- memcpy(out, picParamSet->data(), picParamSet->size());
-
-#if 0
- LOGI("AVC seq param set");
- hexdump(seqParamSet->data(), seqParamSet->size());
-#endif
-
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
-
- meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
-
- LOGI("found AVC codec config (%d x %d)", width, height);
-
- return meta;
-}
-
} // namespace android
unsigned profile, unsigned sampling_freq_index,
unsigned channel_configuration);
- static sp<MetaData> MakeAVCCodecSpecificData(
- const sp<ABuffer> &accessUnit);
-
DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
};
: mInitCheck(NO_INIT),
mFormat(new MetaData),
mEOSResult(OK),
+ mIsAVC(false),
+ mScanForIDR(true),
mRTPTimeBase(0),
mNormalPlayTimeBaseUs(0),
mLastNormalPlayTimeUs(0) {
mInitCheck = OK;
if (!strncmp(desc.c_str(), "H264/", 5)) {
+ mIsAVC = true;
+
mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t width, height;
return;
}
+ if (mScanForIDR && mIsAVC) {
+ // This pretty piece of code ensures that the first access unit
+ // fed to the decoder after stream-start or seek is guaranteed to
+ // be an IDR frame. This is to workaround limitations of a certain
+ // hardware h.264 decoder that requires this to be the case.
+
+ if (!IsIDR(buffer)) {
+ LOGV("skipping AU while scanning for next IDR frame.");
+ return;
+ }
+
+ mScanForIDR = false;
+ }
+
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
void APacketSource::flushQueue() {
Mutex::Autolock autoLock(mLock);
mBuffers.clear();
+
+ mScanForIDR = true;
}
int64_t APacketSource::getNormalPlayTimeUs() {
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
+ bool mIsAVC;
+ bool mScanForIDR;
+
uint32_t mClockRate;
uint32_t mRTPTimeBase;