blob: e17f9609e7a7be4f084e6cfa160b107f741b87c4 [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/bmff/assessor.h"
#include <cstdint>
#include <string>
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/string_view.h"
#include "formats/bmff/box_header.h"
#include "riegeli/bytes/reader.h"
namespace credentio {
namespace {
bool IsSupportedBrand(absl::string_view brand) {
return brand == "qt " || absl::StartsWith(brand, "mp4") || brand == "heic" ||
brand == "avif" || brand == "M4A " || absl::StartsWith(brand, "iso");
}
absl::StatusOr<std::string> ExtractBrand(riegeli::Reader& input) {
std::string brand;
if (!input.Read(4, brand)) {
return input.StatusOrAnnotate(absl::InternalError("failed to read brand"));
}
return brand;
}
} // namespace
absl::StatusOr<bool> BmffAssessor::IsSupported(riegeli::Reader& input) const {
int64_t starting_position = input.pos();
bool is_supported = false;
absl::Status iteration_status = IterateOverBmffBoxes(
input,
[&input,
&is_supported](const BmffBoxHeader& box_header) -> absl::StatusOr<bool> {
if (box_header.type == "ftyp") {
uint64_t box_end = box_header.start + box_header.box_size;
if (box_header.box_size < box_header.header_size + 8) {
return false; // Terminate loop
}
ABSL_ASSIGN_OR_RETURN(auto major_brand, ExtractBrand(input));
if (IsSupportedBrand(major_brand)) {
is_supported = true;
return false; // Terminate loop
}
if (!input.Skip(4)) {
return input.StatusOrAnnotate(
absl::InternalError("failed to skip minor brand"));
}
std::string compatible_brand;
while (input.pos() <= box_end && box_end - input.pos() >= 4) {
ABSL_ASSIGN_OR_RETURN(compatible_brand, ExtractBrand(input));
if (IsSupportedBrand(compatible_brand)) {
is_supported = true;
return false; // Terminate loop
}
}
// We didn't find a supported brand.
return false; // Terminate loop
} else if (box_header.type == "moov") {
// We found a moov box, confident that this file is a QuickTime Movie.
is_supported = true;
return false; // Terminate loop
}
// Only process top-level atoms.
if (!input.Seek(box_header.start + box_header.box_size)) {
return input.StatusOrAnnotate(
absl::InternalError("failed to seek to end of box"));
}
return true; // Continue
});
if (!input.Seek(starting_position) || input.pos() != starting_position) {
return input.StatusOrAnnotate(
absl::InternalError("failed to seek to start of asset"));
}
if (!iteration_status.ok() && !absl::IsDataLoss(iteration_status)) {
return iteration_status;
}
if (absl::IsDataLoss(iteration_status)) {
return false;
}
return is_supported;
}
} // namespace credentio