Bug 1113073 - Add H264 3-bytes NAL size support. r=kentuckyfriedtakahe, a=sledru
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 23 Dec 2014 14:41:21 +1100
changeset 242663 25674b68ea1c876285167f12a0e9c5e536dbd2a7
parent 242662 fe3f375b1d683fe4c838322a16ce4bd51e852fb1
child 242664 db860c3d4f4510a5cffd903b771bf411d0a81fd4
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe, sledru
bugs1113073
milestone36.0a2
Bug 1113073 - Add H264 3-bytes NAL size support. r=kentuckyfriedtakahe, a=sledru
media/libstagefright/binding/AnnexB.cpp
media/libstagefright/binding/DecoderData.cpp
media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
--- a/media/libstagefright/binding/AnnexB.cpp
+++ b/media/libstagefright/binding/AnnexB.cpp
@@ -16,26 +16,22 @@ namespace mp4_demuxer
 
 static const uint8_t kAnnexBDelimiter[] = { 0, 0, 0, 1 };
 
 void
 AnnexB::ConvertSampleToAnnexB(MP4Sample* aSample)
 {
   MOZ_ASSERT(aSample);
 
-  if (aSample->size < 4) {
+  if (!IsAVCC(aSample)) {
     return;
   }
   MOZ_ASSERT(aSample->data);
 
-  if (!aSample->extra_data || aSample->extra_data->Length() < 7 ||
-      (*aSample->extra_data)[0] != 1) {
-    // Not AVCC sample, can't convert.
-    return;
-  }
+  ConvertSampleTo4BytesAVCC(aSample);
 
   uint8_t* d = aSample->data;
   while (d + 4 < aSample->data + aSample->size) {
     uint32_t nalLen = mozilla::BigEndian::readUint32(d);
     // Overwrite the NAL length with the Annex B separator.
     memcpy(d, kAnnexBDelimiter, ArrayLength(kAnnexBDelimiter));
     d += 4 + nalLen;
   }
@@ -190,20 +186,18 @@ ParseNALUnits(ByteWriter& aBw, ByteReade
     aBw.WriteU32(sizeNAL);
     aBw.Write(aBr.Read(sizeNAL), sizeNAL);
   }
 }
 
 void
 AnnexB::ConvertSampleToAVCC(MP4Sample* aSample)
 {
-  if ((aSample->size <= 6) ||
-      (aSample->extra_data && !aSample->extra_data->IsEmpty() &&
-       (*aSample->extra_data)[0] == 1)) {
-    // Invalid or already in AVCC format.
+  if (IsAVCC(aSample)) {
+    ConvertSampleTo4BytesAVCC(aSample);
     return;
   }
 
   uint32_t header = mozilla::BigEndian::readUint32(aSample->data);
   if (header != 0x00000001 && (header >> 8) != 0x000001) {
     // Not AnnexB, can't convert.
     return;
   }
@@ -215,43 +209,59 @@ AnnexB::ConvertSampleToAVCC(MP4Sample* a
   ParseNALUnits(writer, reader);
   aSample->Replace(nalu.begin(), nalu.length());
 }
 
 already_AddRefed<ByteBuffer>
 AnnexB::ExtractExtraData(const MP4Sample* aSample)
 {
   nsRefPtr<ByteBuffer> extradata = new ByteBuffer;
+  if (!IsAVCC(aSample)) {
+    return extradata.forget();
+  }
+  // SPS content
   mozilla::Vector<uint8_t> sps;
+  ByteWriter spsw(sps);
   int numSps = 0;
+  // PPS content
   mozilla::Vector<uint8_t> pps;
+  ByteWriter ppsw(pps);
   int numPps = 0;
 
+  int nalLenSize = ((*aSample->extra_data)[4] & 3) + 1;
+  ByteReader reader(aSample->data, aSample->size);
+
   // Find SPS and PPS NALUs in AVCC data
   uint8_t* d = aSample->data;
-  while (d + 4 < aSample->data + aSample->size) {
-    uint32_t nalLen = mozilla::BigEndian::readUint32(d);
-    uint8_t nalType = d[4] & 0x1f;
-    if (nalType == 7) { /* SPS */
+  while (reader.Remaining() > nalLenSize) {
+    uint32_t nalLen;
+    switch (nalLenSize) {
+      case 1: nalLen = reader.ReadU8();  break;
+      case 2: nalLen = reader.ReadU16(); break;
+      case 3: nalLen = reader.ReadU24(); break;
+      case 4: nalLen = reader.ReadU32(); break;
+    }
+    uint8_t nalType = reader.PeekU8();
+    const uint8_t* p = reader.Read(nalLen);
+    if (!p) {
+      return extradata.forget();
+    }
+
+    if (nalType == 0x67) { /* SPS */
       numSps++;
-      uint8_t val[2];
-      mozilla::BigEndian::writeInt16(&val[0], nalLen);
-      sps.append(&val[0], 2); // 16 bits size
-      sps.append(d + 4, nalLen);
-    } else if (nalType == 8) { /* PPS */
+      spsw.WriteU16(nalLen);
+      spsw.Write(p, nalLen);
+    } else if (nalType == 0x68) { /* PPS */
       numPps++;
-      uint8_t val[2];
-      mozilla::BigEndian::writeInt16(&val[0], nalLen);
-      pps.append(&val[0], 2); // 16 bits size
-      pps.append(d + 4, nalLen);
+      ppsw.WriteU16(nalLen);
+      ppsw.Write(p, nalLen);
     }
-    d += 4 + nalLen;
   }
 
-  if (numSps) {
+  if (numSps && sps.length() > 5) {
     extradata->AppendElement(1);        // version
     extradata->AppendElement(sps[3]);   // profile
     extradata->AppendElement(sps[4]);   // profile compat
     extradata->AppendElement(sps[5]);   // level
     extradata->AppendElement(0xfc | 3); // nal size - 1
     extradata->AppendElement(0xe0 | numSps);
     extradata->AppendElements(sps.begin(), sps.length());
     extradata->AppendElement(numPps);
@@ -282,9 +292,48 @@ AnnexB::HasSPS(const ByteBuffer* aExtraD
     return false;
   }
   uint8_t numSps = reader.ReadU8() & 0x1f;
   reader.DiscardRemaining();
 
   return numSps > 0;
 }
 
+void
+AnnexB::ConvertSampleTo4BytesAVCC(MP4Sample* aSample)
+{
+  MOZ_ASSERT(IsAVCC(aSample));
+
+  int nalLenSize = ((*aSample->extra_data)[4] & 3) + 1;
+
+  if (nalLenSize == 4) {
+    return;
+  }
+  mozilla::Vector<uint8_t> dest;
+  ByteWriter writer(dest);
+  ByteReader reader(aSample->data, aSample->size);
+  while (reader.Remaining() > nalLenSize) {
+    uint32_t nalLen;
+    switch (nalLenSize) {
+      case 1: nalLen = reader.ReadU8();  break;
+      case 2: nalLen = reader.ReadU16(); break;
+      case 3: nalLen = reader.ReadU24(); break;
+      case 4: nalLen = reader.ReadU32(); break;
+    }
+    const uint8_t* p = reader.Read(nalLen);
+    if (!p) {
+      return;
+    }
+    writer.WriteU32(nalLen);
+    writer.Write(p, nalLen);
+  }
+  aSample->Replace(dest.begin(), dest.length());
+}
+
+bool
+AnnexB::IsAVCC(const MP4Sample* aSample)
+{
+  return aSample->size >= 3 && aSample->extra_data &&
+    aSample->extra_data->Length() >= 7 && (*aSample->extra_data)[0] == 1;
+}
+
+
 } // namespace mp4_demuxer
--- a/media/libstagefright/binding/DecoderData.cpp
+++ b/media/libstagefright/binding/DecoderData.cpp
@@ -183,21 +183,17 @@ void
 VideoDecoderConfig::Update(sp<MetaData>& aMetaData, const char* aMimeType)
 {
   TrackConfig::Update(aMetaData, aMimeType);
   display_width = FindInt32(aMetaData, kKeyDisplayWidth);
   display_height = FindInt32(aMetaData, kKeyDisplayHeight);
   image_width = FindInt32(aMetaData, kKeyWidth);
   image_height = FindInt32(aMetaData, kKeyHeight);
 
-  if (FindData(aMetaData, kKeyAVCC, extra_data) && extra_data->Length() >= 7) {
-    // Set size of the NAL length to 4. The demuxer formats its output with
-    // this NAL length size.
-    (*extra_data)[4] |= 3;
-  }
+  FindData(aMetaData, kKeyAVCC, extra_data);
 }
 
 bool
 VideoDecoderConfig::IsValid()
 {
   return display_width > 0 && display_height > 0;
 }
 
--- a/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/AnnexB.h
@@ -18,24 +18,26 @@ class AnnexB
 {
 public:
   // All conversions assume size of NAL length field is 4 bytes.
   // Convert a sample from AVCC format to Annex B.
   static void ConvertSampleToAnnexB(MP4Sample* aSample);
   // Convert a sample from Annex B to AVCC.
   // an AVCC extradata must not be set.
   static void ConvertSampleToAVCC(MP4Sample* aSample);
+  static void ConvertSampleTo4BytesAVCC(MP4Sample* aSample);
 
   // Parse an AVCC extradata and construct the Annex B sample header.
   static already_AddRefed<ByteBuffer> ConvertExtraDataToAnnexB(
     const ByteBuffer* aExtraData);
   static already_AddRefed<ByteBuffer> ExtractExtraData(
     const MP4Sample* aSample);
   static bool HasSPS(const MP4Sample* aSample);
   static bool HasSPS(const ByteBuffer* aExtraData);
+  static bool IsAVCC(const MP4Sample* aSample);
 
 private:
   // AVCC box parser helper.
   static void ConvertSPSOrPPS(ByteReader& aReader, uint8_t aCount,
                               ByteBuffer* aAnnexB);
 };
 
 } // namespace mp4_demuxer