webp_parser.cc 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "media/parsers/webp_parser.h"
  5. #include <limits.h>
  6. #include <stddef.h>
  7. #include <string.h>
  8. #include "base/bits.h"
  9. #include "base/check_op.h"
  10. #include "base/numerics/safe_conversions.h"
  11. #include "build/build_config.h"
  12. #include "media/parsers/vp8_parser.h"
  13. #if !defined(ARCH_CPU_LITTLE_ENDIAN)
  14. #error Big-Endian architecture not supported.
  15. #endif
  16. namespace media {
  17. namespace {
  18. // The byte position storing the size of the file.
  19. constexpr size_t kFileSizeBytePosition = 4u;
  20. // The byte position in which the WebP image data begins.
  21. constexpr size_t kWebPFileBeginBytePosition = 8u;
  22. // The byte position storing the size of the VP8 frame.
  23. constexpr size_t kVp8FrameSizePosition = 16u;
  24. // The 12 bytes that include the FourCC "WEBPVP8 " plus the VP8 chunk size info.
  25. constexpr size_t kWebPFileHeaderByteSize = 12u;
  26. // A valid WebP image header and VP8 chunk header require 20 bytes.
  27. // The VP8 Key Frame's payload also begins at byte 20.
  28. constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
  29. // The max WebP file size is (2^32 - 10) per the WebP spec:
  30. // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
  31. constexpr uint32_t kMaxWebPFileSize = (1ull << 32) - 10u;
  32. constexpr size_t kSizeOfUint32t = sizeof(uint32_t);
  33. } // namespace
  34. bool IsLossyWebPImage(base::span<const uint8_t> encoded_data) {
  35. if (encoded_data.size() < kWebPFileAndVp8ChunkHeaderSizeInBytes)
  36. return false;
  37. DCHECK(encoded_data.data());
  38. return !memcmp(encoded_data.data(), "RIFF", 4) &&
  39. !memcmp(encoded_data.data() + kWebPFileBeginBytePosition, "WEBPVP8 ",
  40. 8);
  41. }
  42. std::unique_ptr<Vp8FrameHeader> ParseWebPImage(
  43. base::span<const uint8_t> encoded_data) {
  44. if (!IsLossyWebPImage(encoded_data))
  45. return nullptr;
  46. static_assert(CHAR_BIT == 8, "Size of a char is not 8 bits.");
  47. static_assert(kSizeOfUint32t == 4u, "Size of uint32_t is not 4 bytes.");
  48. // Try to acquire the WebP file size. IsLossyWebPImage() has ensured
  49. // that we have enough data to read the file size.
  50. DCHECK_GE(encoded_data.size(), kFileSizeBytePosition + kSizeOfUint32t);
  51. // No need to worry about endianness because we assert little-endianness.
  52. const uint32_t file_size = *reinterpret_cast<const uint32_t*>(
  53. encoded_data.data() + kFileSizeBytePosition);
  54. // Check that |file_size| is even, per the WebP spec:
  55. // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
  56. if (file_size % 2 != 0)
  57. return nullptr;
  58. // Check that |file_size| <= 2^32 - 10, per the WebP spec:
  59. // https://developers.google.com/speed/webp/docs/riff_container#webp_file_header
  60. if (file_size > kMaxWebPFileSize)
  61. return nullptr;
  62. // Check that the file size in the header matches the encoded data's size.
  63. if (base::strict_cast<size_t>(file_size) !=
  64. encoded_data.size() - kWebPFileBeginBytePosition) {
  65. return nullptr;
  66. }
  67. // Try to acquire the VP8 key frame size and validate that it fits within the
  68. // encoded data's size.
  69. DCHECK_GE(encoded_data.size(), kVp8FrameSizePosition + kSizeOfUint32t);
  70. const uint32_t vp8_frame_size = *reinterpret_cast<const uint32_t*>(
  71. encoded_data.data() + kVp8FrameSizePosition);
  72. // Check that the VP8 frame size is bounded by the WebP size.
  73. if (base::strict_cast<size_t>(file_size) - kWebPFileHeaderByteSize <
  74. base::strict_cast<size_t>(vp8_frame_size)) {
  75. return nullptr;
  76. }
  77. // Check that the size of the encoded data is consistent.
  78. const size_t vp8_padded_frame_size =
  79. base::bits::AlignUp(size_t{vp8_frame_size}, size_t{2});
  80. if (encoded_data.size() - kWebPFileAndVp8ChunkHeaderSizeInBytes !=
  81. vp8_padded_frame_size) {
  82. return nullptr;
  83. }
  84. // Check that the last byte is 0 if |vp8_frame_size| is odd per WebP specs:
  85. // https://developers.google.com/speed/webp/docs/riff_container#riff_file_format
  86. if (vp8_frame_size % 2 &&
  87. encoded_data.data()[encoded_data.size() - 1] != 0u) {
  88. return nullptr;
  89. }
  90. // Attempt to parse the VP8 frame.
  91. Vp8Parser vp8_parser;
  92. auto result = std::make_unique<Vp8FrameHeader>();
  93. if (vp8_parser.ParseFrame(
  94. encoded_data.data() + kWebPFileAndVp8ChunkHeaderSizeInBytes,
  95. base::strict_cast<size_t>(vp8_frame_size), result.get())) {
  96. return result;
  97. }
  98. return nullptr;
  99. }
  100. } // namespace media