diff --git a/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.cc b/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.cc index e7d7546..ac28ae1 100644 --- a/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.cc +++ b/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.cc @@ -250,15 +250,12 @@ tuple GetImageSizeAndXmpRange( return make_tuple(image_size, primary_xmp_range, mpvd_box); } -bool ParseAndCheckXmpMetadata(DataSource* data_source, size_t file_size, - FileType file_type, - const string& motion_photo_file_name, - const DataRange& xmp_range, size_t image_size, - const std::string& metadata_file_name_output, - MotionPhoto* motion_photo, - MotionPhotoReader* motion_photo_reader, - MotionPhotoChecker* motion_photo_checker, - const StringOutputter& outputter) { +bool ParseXmpMetadata(DataSource* data_source, + const string& motion_photo_file_name, + const DataRange& xmp_range, + const std::string& metadata_file_name_output, + MotionPhotoReader* motion_photo_reader, + const StringOutputter& outputter) { outputter("\nParsing XMP metadata\n"); const size_t kMaxXmpLineLength = 100; DataLineMap data_line_map; @@ -271,8 +268,22 @@ bool ParseAndCheckXmpMetadata(DataSource* data_source, size_t file_size, WriteToOutput(motion_photo_file_name, metadata_file_name_output, xmp_range.GetBegin(), xmp_range.GetEnd()); } - if (!motion_photo_reader->ReadImageMetadata({xmp_range}, data_source, - &bytes_parsed)) { + return motion_photo_reader->ReadImageMetadata({xmp_range}, data_source, + &bytes_parsed); +} + +bool ParseAndCheckXmpMetadata(DataSource* data_source, size_t file_size, + FileType file_type, + const string& motion_photo_file_name, + const DataRange& xmp_range, size_t image_size, + const std::string& metadata_file_name_output, + MotionPhoto* motion_photo, + MotionPhotoReader* motion_photo_reader, + MotionPhotoChecker* motion_photo_checker, + const StringOutputter& outputter) { + if (!ParseXmpMetadata(data_source, motion_photo_file_name, xmp_range, + metadata_file_name_output, motion_photo_reader, + outputter)) { return false; } diff --git a/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.h b/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.h index 444755f..2ebc2c2 100644 --- a/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.h +++ b/lib/core/motion_photo/src/motion_photo_checker_extractor_common/motion_photo_checker_extractor_common.h @@ -53,6 +53,16 @@ std::tuple GetImageSizeAndXmpRange( const image_io::StringOutputter& outputter, image_io::MessageHandler* message_handler); +// Parses the XMP metadata without running MotionPhotoChecker validations. +// Optionally writes the metadata to metadata_file_name_output. +bool ParseXmpMetadata( + image_io::DataSource* data_source, + const std::string& motion_photo_file_name, + const image_io::DataRange& xmp_range, + const std::string& metadata_file_name_output, + MotionPhotoReader* motion_photo_reader, + const image_io::StringOutputter& outputter); + // Parses and checks the XMP metadata. // Optionally writes the metadata to metadata_file_name_output. bool ParseAndCheckXmpMetadata( diff --git a/lib/core/motion_photo/src/motion_photo_extractor/function.cc b/lib/core/motion_photo/src/motion_photo_extractor/function.cc index 301e975..380d082 100644 --- a/lib/core/motion_photo/src/motion_photo_extractor/function.cc +++ b/lib/core/motion_photo/src/motion_photo_extractor/function.cc @@ -30,7 +30,6 @@ #include "image_io/utils/string_outputter_message_writer.h" #include "metadata_collection.pb.h" #include "motion_photo/metadata_engine.h" -#include "motion_photo/motion_photo_checker.h" #include "motion_photo/motion_photo_reader.h" #include "motion_photo/motion_photo_utils.h" #include "motion_photo/motion_photo_writer.h" @@ -81,7 +80,7 @@ string GetProgramName(std::string_view arg0) { program_name = arg0.substr(last_slash + 1); } if (program_name.empty()) { - program_name = "motion_photo_checker"; + program_name = "motion_photo_extractor"; } return program_name; } @@ -255,7 +254,6 @@ int ExtractMotionPhoto(const MotionPhotoExtractorParams& params, MotionPhoto motion_photo(file_type); MotionPhotoReader motion_photo_reader(&motion_photo, &message_handler); - MotionPhotoChecker motion_photo_checker(motion_photo, &message_handler); unique_ptr data_source; tie(data_source, file_size) = @@ -272,12 +270,14 @@ int ExtractMotionPhoto(const MotionPhotoExtractorParams& params, int64_t video_length = -1; int64_t image_size = -1; - for (const auto& block : collection.blocks()) { - if (block.format_identifier() == "container.trailer") { - image_size = block.offset(); - if (block.has_video_offset_in_block() && block.has_video_length()) { - video_offset = block.offset() + block.video_offset_in_block(); - video_length = block.video_length(); + if (engine.IsMotionPhoto(collection)) { + for (const auto& block : collection.blocks()) { + if (block.format_identifier() == "container.trailer") { + image_size = block.offset(); + if (block.has_video_offset_in_block() && block.has_video_length()) { + video_offset = block.offset() + block.video_offset_in_block(); + video_length = block.video_length(); + } } } } @@ -337,26 +337,21 @@ int ExtractMotionPhoto(const MotionPhotoExtractorParams& params, } motion_photo.SetMpvdBox(mpvd_box); - // Parse the XMP metadata string from the data source and check the values. - if (!ParseAndCheckXmpMetadata(data_source.get(), file_size, file_type, - params.motion_photo_file_name, xmp_range, - std_image_size, params.metadata_file_name_output, - &motion_photo, &motion_photo_reader, - &motion_photo_checker, outputter)) { + // Parse the XMP metadata string without running strict MotionPhotoChecker rules. + if (!ParseXmpMetadata(data_source.get(), params.motion_photo_file_name, + xmp_range, params.metadata_file_name_output, + &motion_photo_reader, outputter)) { + return 1; + } + if (!motion_photo.IsMotionPhoto() && !mpvd_box.IsValid()) { + message_handler.ReportMessage(Message::kValueError, + "Input file is not a motion photo"); return 1; } - // Read and decode and check the MP4 track data and metadata. int64_t offset = motion_photo.GetMp4FileOffset(file_size); - if (offset < file_size) { - if (!ReadDecodeAndCheckVideoMetadata(params.motion_photo_file_name, offset, - &motion_photo, &motion_photo_reader, - &motion_photo_checker, outputter, - &message_handler)) { - return 0; - } - + if (offset >= 0 && offset < static_cast(file_size)) { if (!params.video_file_name_output.empty()) { WriteToOutput(params.motion_photo_file_name, params.video_file_name_output, offset, file_size); diff --git a/lib/core/motion_photo/tests/motion_photo_extractor_test.cc b/lib/core/motion_photo/tests/motion_photo_extractor_test.cc index 52c0d24..6c299c1 100644 --- a/lib/core/motion_photo/tests/motion_photo_extractor_test.cc +++ b/lib/core/motion_photo/tests/motion_photo_extractor_test.cc @@ -15,8 +15,11 @@ #include #include +#include #include #include +#include +#include #include #include @@ -219,5 +222,57 @@ TEST(MotionPhotoExtractorFunctionTest, StillOnlyImageRejection) { fs::remove_all(kTestDir); } +// Ensures extractor succeeds directly without running strict MotionPhotoChecker +// validation rules (e.g. when GCamera:MotionPhotoVersion is "2" instead of "1", +// which MotionPhotoChecker rejects with an error). +TEST(MotionPhotoExtractorFunctionTest, ExtractionBypassesCheckerValidation) { + const string kTestDir = CreateTestDir("ExtractionBypassesCheckerValidation"); + const string kSrcJpg = + "lib/core/motion_photo/testdata/motion_photo_single_video_track.MP.jpg"; + const string kModifiedJpg = + (fs::path(kTestDir) / "non_conforming_version.jpg").string(); + const string kOutputFileImage = + (fs::path(kTestDir) / "extracted_still.jpg").string(); + const string kOutputFileVideo = + (fs::path(kTestDir) / "extracted_video.mp4").string(); + + std::ifstream in(kSrcJpg, std::ios::binary); + string bytes((std::istreambuf_iterator(in)), + std::istreambuf_iterator()); + const string kTarget = "MotionPhotoVersion=\"1\""; + const string kReplacement = "MotionPhotoVersion=\"2\""; + size_t pos = bytes.find(kTarget); + EXPECT_NE(pos, string::npos); + if (pos != string::npos) { + bytes.replace(pos, kTarget.size(), kReplacement); + } + { + std::ofstream out(kModifiedJpg, std::ios::binary); + out.write(bytes.data(), bytes.size()); + } + + const char* argv[] = {"MotionPhotoExtractor", "-mi", + kModifiedJpg.c_str(), "-po", + kOutputFileImage.c_str(), "-vo", + kOutputFileVideo.c_str()}; + + bool has_zero_errors = false; + int status = MotionPhotoExtractorFunction(7, argv, [&](absl::string_view str) { + if (absl::StrContains(str, "0 errors")) { + has_zero_errors = true; + } + }); + + EXPECT_EQ(status, 0); + EXPECT_TRUE(has_zero_errors); + EXPECT_TRUE(fs::exists(kOutputFileImage)); + EXPECT_TRUE(fs::exists(kOutputFileVideo)); + + MotionPhotoMp4FileReader mp4_reader; + EXPECT_TRUE(mp4_reader.Read(kOutputFileVideo, 0)); + + fs::remove_all(kTestDir); +} + } // namespace motion_photo } // namespace libmotionphoto