| // 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 |