blob: 55f326872730c894978294b58902be5d4e536da2 [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 "tools/asset_writer.h"
#include <memory>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/status_macros.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "claim/local_signer.h"
#include "crypto/crypto_write_handler.h"
#include "crypto/default/hasher.h"
#include "crypto/hash.h"
#include "crypto/templated_crypto_write_handler.h"
#include "formats/bmff/writer.h"
#include "formats/gif/writer.h"
#include "formats/id3/writer.h"
#include "formats/jpeg/writer.h"
#include "formats/pdf/writer.h"
#include "formats/png/writer.h"
#include "formats/riff/writer.h"
#include "formats/tiff/writer.h"
#include "formats/zip/writer.h"
#include "generator/generator.h"
#include "generator/generator_factory.h"
#include "generator/generator_options.h"
#include "proto/generator.pb.h"
#include "riegeli/bytes/cfile_reader.h"
#include "riegeli/bytes/cfile_writer.h"
#include "riegeli/bytes/reader.h"
#include "riegeli/bytes/writer.h"
#include "testing/local_key_crypto_write_handler.h"
#include "utils/media_type.h"
namespace credentio {
absl::Status WriteAsset(LocalKeyCryptoWriteHandlerOptions crypto_options,
GenerateManifestStoreRequest request,
absl::string_view media_type, riegeli::Reader& source,
riegeli::Writer& destination) {
ABSL_ASSIGN_OR_RETURN(
std::unique_ptr<CryptoWriteHandler> crypto_write_handler,
CreateLocalKeyCryptoWriteHandler(std::move(crypto_options)));
const HasherFactory& hash_factory = DefaultHasherFactory();
TemplatedHasherFactory template_hash_factory(hash_factory.algorithm());
TemplatedCryptoWriteHandler template_crypto_write_handler(
crypto_write_handler.get());
LocalClaimSigner template_claim_signer(&template_crypto_write_handler,
&template_hash_factory);
LocalClaimSigner signing_claim_signer(crypto_write_handler.get(),
&hash_factory);
GeneratorOptions generator_options;
generator_options.generator_info.set_name("c2pa_asset_write");
generator_options.signing_claim_signer = &signing_claim_signer;
generator_options.template_claim_signer = &template_claim_signer;
generator_options.hash_factory = &hash_factory;
ABSL_ASSIGN_OR_RETURN(std::unique_ptr<ManifestStoreGenerator> generator,
CreateGenerator(std::move(generator_options)));
if (!request.has_actions()) {
request.mutable_actions()->mutable_created();
}
if (media_type == "image/png") {
PngWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "image/jpeg") {
JpegWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "image/gif") {
GifWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "video/mp4" || media_type == "video/quicktime" ||
media_type == "image/heic" || media_type == "image/heif" ||
media_type == "image/avif" || media_type == "audio/mp4") {
BmffWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "image/tiff" || media_type == "image/tiff-fx" ||
media_type == "image/x-adobe-dng") {
TiffWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "image/webp" || media_type == "audio/wav" ||
media_type == "audio/x-wav" || media_type == "audio/vnd.wave" ||
media_type == "video/x-msvideo" || media_type == "video/avi") {
RiffWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination,
/*output_metadata=*/nullptr);
}
if (media_type == "audio/mpeg" || media_type == "audio/mp3" ||
media_type == "audio/flac" || media_type == "audio/x-flac") {
Id3Writer writer(generator.get());
return writer.Write(std::move(request), &source, &destination);
}
if (media_type == "application/pdf") {
PdfWriter writer(generator.get());
return writer.Write(std::move(request), &source, &destination);
}
if (media_type == "application/zip" ||
media_type ==
"application/"
"vnd.openxmlformats-officedocument.presentationml.presentation" ||
media_type ==
"application/"
"vnd.openxmlformats-officedocument.wordprocessingml.document" ||
media_type ==
"application/"
"vnd.openxmlformats-officedocument.spreadsheetml.sheet") {
ZipWriter writer(generator.get());
return writer.Write(request, &source, &destination);
}
return absl::UnimplementedError(
absl::StrCat("Unsupported media type for asset_write: ", media_type));
}
absl::Status WriteAssetFile(LocalKeyCryptoWriteHandlerOptions crypto_options,
GenerateManifestStoreRequest request,
absl::string_view input_path,
absl::string_view output_path) {
// Writing in place would truncate the input asset before it is read.
if (input_path == output_path) {
return absl::InvalidArgumentError(absl::StrCat(
"In-place writing is not supported; the output path must differ from "
"the input path: ",
input_path));
}
ABSL_ASSIGN_OR_RETURN(std::string media_type, MediaType(input_path));
riegeli::CFileReader<> source(input_path);
if (!source.ok()) {
return source.status();
}
// Some writers (e.g. `ZipWriter`) read back the destination, so it must be
// opened for reading as well.
riegeli::CFileWriter<> destination(
output_path, riegeli::CFileWriterBase::Options().set_read(true));
if (!destination.ok()) {
return destination.status();
}
ABSL_RETURN_IF_ERROR(WriteAsset(std::move(crypto_options), std::move(request),
media_type, source, destination));
if (!source.Close()) {
return source.status();
}
if (!destination.Close()) {
return destination.status();
}
return absl::OkStatus();
}
} // namespace credentio