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