OSDN Git Service

Refactor some more h.264 utility code out into avc_utils. Work around a hardware...
authorAndreas Huber <andih@google.com>
Fri, 8 Oct 2010 19:06:27 +0000 (12:06 -0700)
committerAndreas Huber <andih@google.com>
Fri, 8 Oct 2010 19:06:27 +0000 (12:06 -0700)
Change-Id: I61936601e55df7e4c23a8c13087579a4f85bd6e6

media/libstagefright/avc_utils.cpp
media/libstagefright/include/avc_utils.h
media/libstagefright/mpeg2ts/ESQueue.cpp
media/libstagefright/mpeg2ts/ESQueue.h
media/libstagefright/rtsp/APacketSource.cpp
media/libstagefright/rtsp/APacketSource.h

index 8762d29..478e40c 100644 (file)
@@ -18,6 +18,9 @@
 
 #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 {
 
@@ -124,5 +127,176 @@ void FindAVCDimensions(
     }
 }
 
+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
 
index 6602852..62cfc36 100644 (file)
@@ -29,6 +29,16 @@ void FindAVCDimensions(
 
 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_
index 4a75ee4..a13287e 100644 (file)
@@ -40,81 +40,6 @@ sp<MetaData> ElementaryStreamQueue::getFormat() {
     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();
@@ -433,79 +358,4 @@ sp<ABuffer> ElementaryStreamQueue::dequeueAccessUnitH264() {
     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
index 246c390..9eaf834 100644 (file)
@@ -56,9 +56,6 @@ private:
             unsigned profile, unsigned sampling_freq_index,
             unsigned channel_configuration);
 
-    static sp<MetaData> MakeAVCCodecSpecificData(
-            const sp<ABuffer> &accessUnit);
-
     DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
 };
 
index 78754e6..10cc88b 100644 (file)
@@ -490,6 +490,8 @@ APacketSource::APacketSource(
     : mInitCheck(NO_INIT),
       mFormat(new MetaData),
       mEOSResult(OK),
+      mIsAVC(false),
+      mScanForIDR(true),
       mRTPTimeBase(0),
       mNormalPlayTimeBaseUs(0),
       mLastNormalPlayTimeUs(0) {
@@ -509,6 +511,8 @@ APacketSource::APacketSource(
 
     mInitCheck = OK;
     if (!strncmp(desc.c_str(), "H264/", 5)) {
+        mIsAVC = true;
+
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
         int32_t width, height;
@@ -719,6 +723,20 @@ void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
         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();
@@ -735,6 +753,8 @@ void APacketSource::signalEOS(status_t result) {
 void APacketSource::flushQueue() {
     Mutex::Autolock autoLock(mLock);
     mBuffers.clear();
+
+    mScanForIDR = true;
 }
 
 int64_t APacketSource::getNormalPlayTimeUs() {
index 076ddc4..7a77fc6 100644 (file)
@@ -65,6 +65,9 @@ private:
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
 
+    bool mIsAVC;
+    bool mScanForIDR;
+
     uint32_t mClockRate;
 
     uint32_t mRTPTimeBase;