Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,15 +250,12 @@ tuple<size_t, DataRange, MpvdBox> 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;
Expand All @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ std::tuple<size_t, image_io::DataRange, MpvdBox> 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(
Expand Down
43 changes: 19 additions & 24 deletions lib/core/motion_photo/src/motion_photo_extractor/function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<DataSource> data_source;
tie(data_source, file_size) =
Expand All @@ -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();
}
}
}
}
Expand Down Expand Up @@ -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<int64_t>(file_size)) {
if (!params.video_file_name_output.empty()) {
WriteToOutput(params.motion_photo_file_name,
params.video_file_name_output, offset, file_size);
Expand Down
55 changes: 55 additions & 0 deletions lib/core/motion_photo/tests/motion_photo_extractor_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
#include <absl/strings/match.h>
#include <absl/strings/string_view.h>

#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <ios>
#include <iostream>
#include <string>

Expand Down Expand Up @@ -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<char>(in)),
std::istreambuf_iterator<char>());
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
Loading