tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AnnexB.cpp (19204B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "AnnexB.h"
      6 
      7 #include "BufferReader.h"
      8 #include "ByteWriter.h"
      9 #include "H264.h"
     10 #include "H265.h"
     11 #include "MediaData.h"
     12 #include "mozilla/EndianUtils.h"
     13 #include "mozilla/Try.h"
     14 
     15 mozilla::LazyLogModule gAnnexB("AnnexB");
     16 
     17 #define LOG(msg, ...) MOZ_LOG(gAnnexB, LogLevel::Debug, (msg, ##__VA_ARGS__))
     18 #define LOGV(msg, ...) MOZ_LOG(gAnnexB, LogLevel::Verbose, (msg, ##__VA_ARGS__))
     19 
     20 namespace mozilla {
     21 
     22 static const uint8_t kAnnexBDelimiter[] = {0, 0, 0, 1};
     23 
     24 /* static */
     25 Result<Ok, nsresult> AnnexB::ConvertAVCCSampleToAnnexB(
     26    mozilla::MediaRawData* aSample, bool aAddSPS) {
     27  MOZ_ASSERT(aSample);
     28 
     29  if (!IsAVCC(aSample)) {
     30    return Ok();
     31  }
     32  MOZ_ASSERT(aSample->Data());
     33 
     34  MOZ_TRY(ConvertAVCCTo4BytesAVCC(aSample));
     35 
     36  if (aSample->Size() < 4) {
     37    // Nothing to do, it's corrupted anyway.
     38    return Ok();
     39  }
     40 
     41  BufferReader reader(aSample->Data(), aSample->Size());
     42 
     43  nsTArray<uint8_t> tmp;
     44  ByteWriter<BigEndian> writer(tmp);
     45 
     46  while (reader.Remaining() >= 4) {
     47    uint32_t nalLen = MOZ_TRY(reader.ReadU32());
     48    const uint8_t* p = reader.Read(nalLen);
     49 
     50    if (!writer.Write(kAnnexBDelimiter, std::size(kAnnexBDelimiter))) {
     51      return Err(NS_ERROR_OUT_OF_MEMORY);
     52    }
     53    if (!p) {
     54      break;
     55    }
     56    if (!writer.Write(p, nalLen)) {
     57      return Err(NS_ERROR_OUT_OF_MEMORY);
     58    }
     59  }
     60 
     61  UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
     62 
     63  if (!samplewriter->Replace(tmp.Elements(), tmp.Length())) {
     64    return Err(NS_ERROR_OUT_OF_MEMORY);
     65  }
     66 
     67  // Prepend the Annex B NAL with SPS and PPS tables to keyframes.
     68  // TODO: Avoid appending duplicate SPS and PPS NALUs. See bug 1961082.
     69  if (aAddSPS && aSample->mKeyframe) {
     70    RefPtr<MediaByteBuffer> annexB =
     71        ConvertAVCCExtraDataToAnnexB(aSample->mExtraData);
     72    if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) {
     73      return Err(NS_ERROR_OUT_OF_MEMORY);
     74    }
     75 
     76    // Prepending the NAL with SPS/PPS will mess up the encryption subsample
     77    // offsets. So we need to account for the extra bytes by increasing
     78    // the length of the first clear data subsample. Otherwise decryption
     79    // will fail.
     80    if (aSample->mCrypto.IsEncrypted()) {
     81      if (aSample->mCrypto.mPlainSizes.Length() == 0) {
     82        CheckedUint32 plainSize{annexB->Length()};
     83        CheckedUint32 encryptedSize{samplewriter->Size()};
     84        encryptedSize -= annexB->Length();
     85        samplewriter->mCrypto.mPlainSizes.AppendElement(plainSize.value());
     86        samplewriter->mCrypto.mEncryptedSizes.AppendElement(
     87            encryptedSize.value());
     88      } else {
     89        CheckedUint32 newSize{samplewriter->mCrypto.mPlainSizes[0]};
     90        newSize += annexB->Length();
     91        samplewriter->mCrypto.mPlainSizes[0] = newSize.value();
     92      }
     93    }
     94    LOG("Appended extradata %zu bytes", annexB->Length());
     95  }
     96 
     97  return Ok();
     98 }
     99 
    100 /* static */
    101 Result<Ok, nsresult> AnnexB::ConvertHVCCSampleToAnnexB(
    102    mozilla::MediaRawData* aSample, bool aAddSPS) {
    103  MOZ_ASSERT(aSample);
    104  if (!IsHVCC(aSample)) {
    105    LOG("Not HVCC?");
    106    return Ok();
    107  }
    108  MOZ_ASSERT(aSample->Data());
    109 
    110  MOZ_TRY(ConvertHVCCTo4BytesHVCC(aSample));
    111  if (aSample->Size() < 4) {
    112    // Nothing to do, it's corrupted anyway.
    113    LOG("Corrupted HVCC sample?");
    114    return Ok();
    115  }
    116 
    117  BufferReader reader(aSample->Data(), aSample->Size());
    118  nsTArray<uint8_t> tmp;
    119  ByteWriter<BigEndian> writer(tmp);
    120  while (reader.Remaining() >= 4) {
    121    uint32_t nalLen = MOZ_TRY(reader.ReadU32());
    122    const uint8_t* p = reader.Read(nalLen);
    123    if (!writer.Write(kAnnexBDelimiter, std::size(kAnnexBDelimiter))) {
    124      LOG("Failed to write kAnnexBDelimiter, OOM?");
    125      return Err(NS_ERROR_OUT_OF_MEMORY);
    126    }
    127    if (!p) {
    128      break;
    129    }
    130    if (!writer.Write(p, nalLen)) {
    131      LOG("Failed to write nalu, OOM?");
    132      return Err(NS_ERROR_OUT_OF_MEMORY);
    133    }
    134  }
    135 
    136  UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
    137  if (!samplewriter->Replace(tmp.Elements(), tmp.Length())) {
    138    LOG("Failed to write sample, OOM?");
    139    return Err(NS_ERROR_OUT_OF_MEMORY);
    140  }
    141 
    142  // Prepend the Annex B NAL with SPS and PPS tables to keyframes.
    143  // TODO: Avoid appending duplicate SPS and PPS NALUs. See bug 1961082.
    144  if (aAddSPS && aSample->mKeyframe) {
    145    RefPtr<MediaByteBuffer> annexB =
    146        ConvertHVCCExtraDataToAnnexB(aSample->mExtraData);
    147    if (!annexB) {
    148      LOG("Failed to convert HVCC extradata to AnnexB");
    149      return Err(NS_ERROR_FAILURE);
    150    }
    151    if (!samplewriter->Prepend(annexB->Elements(), annexB->Length())) {
    152      LOG("Failed to append annexB extradata");
    153      return Err(NS_ERROR_OUT_OF_MEMORY);
    154    }
    155 
    156    // Prepending the NAL with SPS/PPS will mess up the encryption subsample
    157    // offsets. So we need to account for the extra bytes by increasing
    158    // the length of the first clear data subsample. Otherwise decryption
    159    // will fail.
    160    if (aSample->mCrypto.IsEncrypted()) {
    161      if (aSample->mCrypto.mPlainSizes.Length() == 0) {
    162        CheckedUint32 plainSize{annexB->Length()};
    163        CheckedUint32 encryptedSize{samplewriter->Size()};
    164        encryptedSize -= annexB->Length();
    165        samplewriter->mCrypto.mPlainSizes.AppendElement(plainSize.value());
    166        samplewriter->mCrypto.mEncryptedSizes.AppendElement(
    167            encryptedSize.value());
    168      } else {
    169        CheckedUint32 newSize{samplewriter->mCrypto.mPlainSizes[0]};
    170        newSize += annexB->Length();
    171        samplewriter->mCrypto.mPlainSizes[0] = newSize.value();
    172      }
    173    }
    174    LOG("Appended extradata %zu bytes", annexB->Length());
    175  }
    176  return Ok();
    177 }
    178 
    179 already_AddRefed<mozilla::MediaByteBuffer> AnnexB::ConvertAVCCExtraDataToAnnexB(
    180    const mozilla::MediaByteBuffer* aExtraData) {
    181  // AVCC 6 byte header looks like:
    182  //     +------+------+------+------+------+------+------+------+
    183  // [0] |   0  |   0  |   0  |   0  |   0  |   0  |   0  |   1  |
    184  //     +------+------+------+------+------+------+------+------+
    185  // [1] | profile                                               |
    186  //     +------+------+------+------+------+------+------+------+
    187  // [2] | compatiblity                                          |
    188  //     +------+------+------+------+------+------+------+------+
    189  // [3] | level                                                 |
    190  //     +------+------+------+------+------+------+------+------+
    191  // [4] | unused                                  | nalLenSiz-1 |
    192  //     +------+------+------+------+------+------+------+------+
    193  // [5] | unused             | numSps                           |
    194  //     +------+------+------+------+------+------+------+------+
    195 
    196  RefPtr<mozilla::MediaByteBuffer> annexB = new mozilla::MediaByteBuffer;
    197 
    198  BufferReader reader(*aExtraData);
    199  const uint8_t* ptr = reader.Read(5);
    200  if (ptr && ptr[0] == 1) {
    201    // Append SPS then PPS
    202    (void)reader.ReadU8().map(
    203        [&](uint8_t x) { return ConvertSPSOrPPS(reader, x & 31, annexB); });
    204    (void)reader.ReadU8().map(
    205        [&](uint8_t x) { return ConvertSPSOrPPS(reader, x, annexB); });
    206    // MP4Box adds extra bytes that we ignore. I don't know what they do.
    207  }
    208 
    209  return annexB.forget();
    210 }
    211 
    212 already_AddRefed<mozilla::MediaByteBuffer> AnnexB::ConvertHVCCExtraDataToAnnexB(
    213    const mozilla::MediaByteBuffer* aExtraData) {
    214  auto rv = HVCCConfig::Parse(aExtraData);
    215  if (rv.isErr()) {
    216    return nullptr;
    217  }
    218  const HVCCConfig hvcc = rv.unwrap();
    219  RefPtr<mozilla::MediaByteBuffer> annexB = new mozilla::MediaByteBuffer;
    220  for (const auto& nalu : hvcc.mNALUs) {
    221    annexB->AppendElements(kAnnexBDelimiter, std::size(kAnnexBDelimiter));
    222    annexB->AppendElements(nalu.mNALU.Elements(), nalu.mNALU.Length());
    223    LOGV("Insert NALU (type=%hhu, size=%zu) to AnnexB (size=%zu)",
    224         nalu.mNalUnitType, nalu.mNALU.Length(), annexB->Length());
    225  }
    226  return annexB.forget();
    227 }
    228 
    229 Result<mozilla::Ok, nsresult> AnnexB::ConvertSPSOrPPS(
    230    BufferReader& aReader, uint8_t aCount, mozilla::MediaByteBuffer* aAnnexB) {
    231  for (int i = 0; i < aCount; i++) {
    232    uint16_t length = MOZ_TRY(aReader.ReadU16());
    233 
    234    const uint8_t* ptr = aReader.Read(length);
    235    if (!ptr) {
    236      return Err(NS_ERROR_FAILURE);
    237    }
    238    aAnnexB->AppendElements(kAnnexBDelimiter, std::size(kAnnexBDelimiter));
    239    aAnnexB->AppendElements(ptr, length);
    240  }
    241  return Ok();
    242 }
    243 
    244 static Result<Ok, nsresult> FindStartCodeInternal(BufferReader& aBr) {
    245  size_t offset = aBr.Offset();
    246 
    247  for (uint32_t i = 0; i < aBr.Align() && aBr.Remaining() >= 3; i++) {
    248    auto res = aBr.PeekU24();
    249    if (res.isOk() && (res.unwrap() == 0x000001)) {
    250      return Ok();
    251    }
    252    (void)aBr.Read(1);
    253  }
    254 
    255  while (aBr.Remaining() >= 6) {
    256    uint32_t x32 = MOZ_TRY(aBr.PeekU32());
    257    if ((x32 - 0x01010101) & (~x32) & 0x80808080) {  // Has 0x00 byte(s).
    258      if ((x32 >> 8) == 0x000001) {                  // 0x000001??
    259        return Ok();
    260      }
    261      if ((x32 & 0xffffff) == 0x000001) {  // 0x??000001
    262        (void)aBr.Read(1);
    263        return Ok();
    264      }
    265      if ((x32 & 0xff) == 0) {  // 0x??????00
    266        const uint8_t* p = aBr.Peek(1);
    267        if ((x32 & 0xff00) == 0 && p[4] == 1) {  // 0x????0000,01
    268          (void)aBr.Read(2);
    269          return Ok();
    270        }
    271        if (p[4] == 0 && p[5] == 1) {  // 0x??????00,00,01
    272          (void)aBr.Read(3);
    273          return Ok();
    274        }
    275      }
    276    }
    277    (void)aBr.Read(4);
    278  }
    279 
    280  while (aBr.Remaining() >= 3) {
    281    uint32_t data = MOZ_TRY(aBr.PeekU24());
    282    if (data == 0x000001) {
    283      return Ok();
    284    }
    285    (void)aBr.Read(1);
    286  }
    287 
    288  // No start code were found; Go back to the beginning.
    289  (void)aBr.Seek(offset);
    290  return Err(NS_ERROR_FAILURE);
    291 }
    292 
    293 static Result<Ok, nsresult> FindStartCode(BufferReader& aBr,
    294                                          size_t& aStartSize) {
    295  if (FindStartCodeInternal(aBr).isErr()) {
    296    aStartSize = 0;
    297    return Err(NS_ERROR_FAILURE);
    298  }
    299 
    300  aStartSize = 3;
    301  if (aBr.Offset()) {
    302    // Check if it's 4-bytes start code
    303    aBr.Rewind(1);
    304    uint8_t data = MOZ_TRY(aBr.ReadU8());
    305    if (data == 0) {
    306      aStartSize = 4;
    307    }
    308  }
    309  (void)aBr.Read(3);
    310  return Ok();
    311 }
    312 
    313 /* static */
    314 void AnnexB::ParseNALEntries(const Span<const uint8_t>& aSpan,
    315                             nsTArray<AnnexB::NALEntry>& aEntries) {
    316  BufferReader reader(aSpan.data(), aSpan.Length());
    317  size_t startSize;
    318  auto rv = FindStartCode(reader, startSize);
    319  size_t startOffset = reader.Offset();
    320  if (rv.isOk()) {
    321    while (FindStartCode(reader, startSize).isOk()) {
    322      int64_t offset = reader.Offset();
    323      int64_t sizeNAL = offset - startOffset - startSize;
    324      aEntries.AppendElement(AnnexB::NALEntry(startOffset, sizeNAL));
    325      reader.Seek(startOffset);
    326      reader.Read(sizeNAL + startSize);
    327      startOffset = offset;
    328    }
    329  }
    330  int64_t sizeNAL = reader.Remaining();
    331  if (sizeNAL) {
    332    aEntries.AppendElement(AnnexB::NALEntry(startOffset, sizeNAL));
    333  }
    334 }
    335 
    336 /* static */
    337 size_t AnnexB::FindNalType(const Span<const uint8_t>& aSpan,
    338                           const nsTArray<AnnexB::NALEntry>& aNalEntries,
    339                           NAL_TYPES aType, size_t aStartIndex) {
    340  for (size_t i = aStartIndex; i < aNalEntries.Length(); ++i) {
    341    uint8_t nalUnitType = aSpan[aNalEntries[i].mOffset] & 0x1f;
    342    if (nalUnitType == aType) {
    343      return i;
    344    }
    345  }
    346  return SIZE_MAX;
    347 }
    348 
    349 /* static */
    350 bool AnnexB::FindAllNalTypes(const Span<const uint8_t>& aSpan,
    351                             const nsTArray<NAL_TYPES>& aTypes) {
    352  nsTArray<AnnexB::NALEntry> nalEntries;
    353  AnnexB::ParseNALEntries(aSpan, nalEntries);
    354  nsTArray<NAL_TYPES> checkTypes = aTypes.Clone();
    355  auto entry = nalEntries.cbegin();
    356  while (entry != nalEntries.cend()) {
    357    int64_t offset = (*entry).mOffset;
    358    uint8_t nalUnitType = (aSpan.cbegin() + offset)[0] & 0x1f;
    359    size_t index = checkTypes.IndexOf(nalUnitType);
    360    if (index != nsTArray<NAL_TYPES>::NoIndex) {
    361      checkTypes.RemoveElementAt(index);
    362      if (checkTypes.IsEmpty()) {
    363        return true;
    364      }
    365    }
    366    entry++;
    367  }
    368  return false;
    369 }
    370 
    371 static Result<mozilla::Ok, nsresult> ParseNALUnits(ByteWriter<BigEndian>& aBw,
    372                                                   BufferReader& aBr) {
    373  size_t startSize;
    374 
    375  auto rv = FindStartCode(aBr, startSize);
    376  if (rv.isOk()) {
    377    size_t startOffset = aBr.Offset();
    378    while (FindStartCode(aBr, startSize).isOk()) {
    379      size_t offset = aBr.Offset();
    380      size_t sizeNAL = offset - startOffset - startSize;
    381      aBr.Seek(startOffset);
    382      if (!aBw.WriteU32(sizeNAL) || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
    383        return Err(NS_ERROR_OUT_OF_MEMORY);
    384      }
    385      aBr.Read(startSize);
    386      startOffset = offset;
    387    }
    388  }
    389  size_t sizeNAL = aBr.Remaining();
    390  if (sizeNAL) {
    391    if (!aBw.WriteU32(sizeNAL) || !aBw.Write(aBr.Read(sizeNAL), sizeNAL)) {
    392      return Err(NS_ERROR_OUT_OF_MEMORY);
    393    }
    394  }
    395  return Ok();
    396 }
    397 
    398 /* static */
    399 RefPtr<MediaByteBuffer> AnnexB::ExtractExtraDataForAVCC(
    400    const Span<const uint8_t>& aSpan) {
    401  if (!IsAnnexB(aSpan)) {
    402    return nullptr;
    403  }
    404 
    405  nsTArray<NALEntry> paramSets;
    406  ParseNALEntries(aSpan, paramSets);
    407 
    408  size_t spsIndex =
    409      FindNalType(aSpan, paramSets, H264_NAL_SPS, /* aStartIndex */ 0);
    410  if (spsIndex == SIZE_MAX) {
    411    return nullptr;
    412  }
    413 
    414  size_t ppsIndex =
    415      FindNalType(aSpan, paramSets, H264_NAL_PPS, /* aStartIndex */ 0);
    416  if (ppsIndex == SIZE_MAX) {
    417    return nullptr;
    418  }
    419 
    420  auto avcc = MakeRefPtr<MediaByteBuffer>();
    421  const auto& spsEntry = paramSets.ElementAt(spsIndex);
    422  const auto& ppsEntry = paramSets.ElementAt(ppsIndex);
    423  const auto sps = aSpan.Subspan(spsEntry.mOffset, spsEntry.mSize);
    424  const auto pps = aSpan.Subspan(ppsEntry.mOffset, ppsEntry.mSize);
    425  H264::WriteExtraData(avcc, sps[1], sps[2], sps[3], sps, pps);
    426  return avcc;
    427 }
    428 
    429 bool AnnexB::ConvertSampleToAVCC(mozilla::MediaRawData* aSample,
    430                                 const RefPtr<MediaByteBuffer>& aAVCCHeader) {
    431  if (IsAVCC(aSample)) {
    432    return ConvertAVCCTo4BytesAVCC(aSample).isOk();
    433  }
    434  if (!IsAnnexB(*aSample)) {
    435    // Not AnnexB, nothing to convert.
    436    return true;
    437  }
    438 
    439  nsTArray<uint8_t> nalu;
    440  ByteWriter<BigEndian> writer(nalu);
    441  BufferReader reader(aSample->Data(), aSample->Size());
    442 
    443  if (ParseNALUnits(writer, reader).isErr()) {
    444    return false;
    445  }
    446  UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
    447  if (!samplewriter->Replace(nalu.Elements(), nalu.Length())) {
    448    return false;
    449  }
    450 
    451  if (aAVCCHeader) {
    452    aSample->mExtraData = aAVCCHeader;
    453    return true;
    454  }
    455 
    456  // Create the AVCC header.
    457  auto extradata = MakeRefPtr<mozilla::MediaByteBuffer>();
    458  static const uint8_t kFakeExtraData[] = {
    459      1 /* version */,
    460      0x64 /* profile (High) */,
    461      0 /* profile compat (0) */,
    462      40 /* level (40) */,
    463      0xfc | 3 /* nal size - 1 */,
    464      0xe0 /* num SPS (0) */,
    465      0 /* num PPS (0) */
    466  };
    467  // XXX(Bug 1631371) Check if this should use a fallible operation as it
    468  // pretended earlier.
    469  extradata->AppendElements(kFakeExtraData, std::size(kFakeExtraData));
    470  aSample->mExtraData = std::move(extradata);
    471  return true;
    472 }
    473 
    474 /* static */
    475 Result<mozilla::Ok, nsresult> AnnexB::ConvertSampleToHVCC(
    476    mozilla::MediaRawData* aSample) {
    477  if (IsHVCC(aSample)) {
    478    return ConvertHVCCTo4BytesHVCC(aSample);
    479  }
    480  if (!IsAnnexB(*aSample)) {
    481    // Not AnnexB, nothing to convert.
    482    return Ok();
    483  }
    484 
    485  nsTArray<uint8_t> nalu;
    486  ByteWriter<BigEndian> writer(nalu);
    487  BufferReader reader(aSample->Data(), aSample->Size());
    488  if (auto rv = ParseNALUnits(writer, reader); rv.isErr()) {
    489    LOG("Failed fo parse AnnexB NALU for HVCC");
    490    return rv;
    491  }
    492  UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
    493  if (!samplewriter->Replace(nalu.Elements(), nalu.Length())) {
    494    LOG("Failed fo replace NALU");
    495    return Err(NS_ERROR_OUT_OF_MEMORY);
    496  }
    497  if (aSample->mExtraData && HVCCConfig::Parse(aSample).isErr()) {
    498    LOG("Failed to parse invalid hvcc extradata");
    499    return Err(NS_ERROR_DOM_MEDIA_METADATA_ERR);
    500  }
    501 
    502  // Create the HEVC header as what we did for H264, which will later be
    503  // replaced with the one generated by MediaChangeMonitor.
    504  aSample->mExtraData = H265::CreateFakeExtraData();
    505 
    506  return Ok();
    507 }
    508 
    509 /* static */
    510 Result<mozilla::Ok, nsresult> AnnexB::ConvertAVCCTo4BytesAVCC(
    511    mozilla::MediaRawData* aSample) {
    512  auto avcc = AVCCConfig::Parse(aSample);
    513  MOZ_ASSERT(avcc.isOk());
    514  return ConvertNALUTo4BytesNALU(aSample, avcc.unwrap().NALUSize());
    515 }
    516 
    517 /* static */
    518 Result<mozilla::Ok, nsresult> AnnexB::ConvertHVCCTo4BytesHVCC(
    519    mozilla::MediaRawData* aSample) {
    520  auto hvcc = HVCCConfig::Parse(aSample);
    521  MOZ_ASSERT(hvcc.isOk());
    522  return ConvertNALUTo4BytesNALU(aSample, hvcc.unwrap().NALUSize());
    523 }
    524 
    525 /* static */
    526 bool AnnexB::IsAVCC(const mozilla::MediaRawData* aSample) {
    527  return AVCCConfig::Parse(aSample).isOk();
    528 }
    529 
    530 /* static */
    531 bool AnnexB::IsHVCC(const mozilla::MediaRawData* aSample) {
    532  return HVCCConfig::Parse(aSample).isOk();
    533 }
    534 
    535 /* static */
    536 bool AnnexB::IsAnnexB(const Span<const uint8_t>& aSpan) {
    537  if (aSpan.Length() < 4) {
    538    return false;
    539  }
    540  uint32_t header = mozilla::BigEndian::readUint32(aSpan.Elements());
    541  return header == 0x00000001 || (header >> 8) == 0x000001;
    542 }
    543 
    544 /*  static */ mozilla::Result<mozilla::Ok, nsresult>
    545 AnnexB::ConvertNALUTo4BytesNALU(mozilla::MediaRawData* aSample,
    546                                uint8_t aNALUSize) {
    547  // NALSize should be between 1 to 4.
    548  if (aNALUSize == 0 || aNALUSize > 4) {
    549    return Err(NS_ERROR_FAILURE);
    550  }
    551 
    552  // If the nalLenSize is already 4, we can only check if the data is corrupt
    553  // without replacing data in aSample.
    554  bool needConversion = aNALUSize != 4;
    555 
    556  MOZ_ASSERT(aSample);
    557  nsTArray<uint8_t> dest;
    558  ByteWriter<BigEndian> writer(dest);
    559  BufferReader reader(aSample->Data(), aSample->Size());
    560  while (reader.Remaining() > aNALUSize) {
    561    uint32_t nalLen;
    562    switch (aNALUSize) {
    563      case 1:
    564        nalLen = MOZ_TRY(reader.ReadU8());
    565        break;
    566      case 2:
    567        nalLen = MOZ_TRY(reader.ReadU16());
    568        break;
    569      case 3:
    570        nalLen = MOZ_TRY(reader.ReadU24());
    571        break;
    572      case 4:
    573        nalLen = MOZ_TRY(reader.ReadU32());
    574        break;
    575      default:
    576        MOZ_ASSERT_UNREACHABLE("Bytes of the NAL body length must be in [1,4]");
    577        return Err(NS_ERROR_ILLEGAL_VALUE);
    578    }
    579    const uint8_t* p = reader.Read(nalLen);
    580    if (!p) {
    581      // The data may be corrupt.
    582      return Err(NS_ERROR_UNEXPECTED);
    583    }
    584    if (!needConversion) {
    585      // We only parse aSample to see if it's corrupt.
    586      continue;
    587    }
    588    if (!writer.WriteU32(nalLen) || !writer.Write(p, nalLen)) {
    589      return Err(NS_ERROR_OUT_OF_MEMORY);
    590    }
    591  }
    592  if (!needConversion) {
    593    // We've parsed all the data, and it's all good.
    594    return Ok();
    595  }
    596  UniquePtr<MediaRawDataWriter> samplewriter(aSample->CreateWriter());
    597  if (!samplewriter->Replace(dest.Elements(), dest.Length())) {
    598    return Err(NS_ERROR_OUT_OF_MEMORY);
    599  }
    600  return Ok();
    601 }
    602 
    603 #undef LOG
    604 #undef LOGV
    605 
    606 }  // namespace mozilla