blob: ded3ee3a3cb550dd5ab669d5ad6b8fef1784560b [file]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "formats/id3/assessor.h"
#include <cstdint>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "formats/id3/constants.h"
#include "riegeli/bytes/reader.h"
#include "riegeli/endian/endian_reading.h"
namespace credentio {
namespace {
// The first 5 bytes of an ID3 v2.3 tag header in uint64_t format.
constexpr uint64_t kId3v23Header =
((uint64_t)kId3FileIdentifierAndVersionV23[0] << 32) |
((uint64_t)kId3FileIdentifierAndVersionV23[1] << 24) |
((uint64_t)kId3FileIdentifierAndVersionV23[2] << 16) |
((uint64_t)kId3FileIdentifierAndVersionV23[3] << 8) |
((uint64_t)kId3FileIdentifierAndVersionV23[4]);
// The first 5 bytes of an ID3 v2.4 tag header in uint64_t format.
constexpr uint64_t kId3v24Header =
((uint64_t)kId3FileIdentifierAndVersionV24[0] << 32) |
((uint64_t)kId3FileIdentifierAndVersionV24[1] << 24) |
((uint64_t)kId3FileIdentifierAndVersionV24[2] << 16) |
((uint64_t)kId3FileIdentifierAndVersionV24[3] << 8) |
((uint64_t)kId3FileIdentifierAndVersionV24[4]);
// The first 2 bytes of an MPEG Version 2 Layer 3 frame header, with CRC
// protection enabled.
constexpr uint64_t kMp3MpegFrameHeader = 0xFFF3ULL;
// The FLAC stream marker "fLaC".
constexpr uint32_t kFlacMarker = 0x664C6143;
// Returns true if the `header` starts with the ID3 v2.3 or v2.4 identifier.
//
// For a MP3 file that contains ID3 tags, the ID3 tags can be prepended to the
// audio data.
bool StartsWithId3Header(uint64_t header) {
// Compare the first 5 bytes.
return (header >> 24) == kId3v23Header || (header >> 24) == kId3v24Header;
}
// Returns true if the `header` starts with a MPEG frame, indicating a MP3 file.
bool StartsWithMp3MpegFrameHeader(uint64_t header) {
// Compare the first 15 bits, ignoring the protection bit.
return (header >> 49) == (kMp3MpegFrameHeader >> 1);
}
// Returns true if the `header` starts with FLAC marker.
bool StartsWithFlacMarker(uint64_t header) {
// Compare the first 4 bytes.
return (header >> 32) == kFlacMarker;
}
} // namespace
absl::StatusOr<bool> Id3Assessor::IsSupported(riegeli::Reader& input) const {
int64_t starting_position = input.pos();
uint64_t header;
if (!riegeli::ReadBigEndian<uint64_t>(input, header)) {
return input.StatusOrAnnotate(absl::DataLossError("kUnexpectedEof"));
}
if (!input.Seek(starting_position) || input.pos() != starting_position) {
return input.StatusOrAnnotate(
absl::DataLossError("Failed to seek to starting position"));
}
return StartsWithId3Header(header) || StartsWithMp3MpegFrameHeader(header) ||
StartsWithFlacMarker(header);
}
} // namespace credentio