From 88b7f8181eeef2ada36b623617e05b3d7810d97d Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 20 May 2025 16:02:23 -0500 Subject: [PATCH] Updating git ignore rules, adding new 360 spherical property to profiles, and new unit tests for Profiles. --- .gitignore | 1 + src/Profiles.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++---- src/Profiles.h | 9 ++++---- tests/Profiles.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 107 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index b89ccf59..eed73b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ cmake-build-debug/* tags *~ +**/.claude/* diff --git a/src/Profiles.cpp b/src/Profiles.cpp index c462ffc8..b80b467c 100644 --- a/src/Profiles.cpp +++ b/src/Profiles.cpp @@ -30,6 +30,7 @@ Profile::Profile() { info.display_ratio.num = 0; info.display_ratio.den = 0; info.interlaced_frame = false; + info.spherical = false; // Default to non-spherical (regular) video } // @brief Constructor for Profile. @@ -38,8 +39,19 @@ Profile::Profile(std::string path) { bool read_file = false; - // Call default constructor - Profile(); + // Initialize all values to defaults (same as default constructor) + info.description = ""; + info.height = 0; + info.width = 0; + info.pixel_format = 0; + info.fps.num = 0; + info.fps.den = 0; + info.pixel_ratio.num = 0; + info.pixel_ratio.den = 0; + info.display_ratio.num = 0; + info.display_ratio.den = 0; + info.interlaced_frame = false; + info.spherical = false; // Default to non-spherical (regular) video try { @@ -104,6 +116,10 @@ Profile::Profile(std::string path) { std::stringstream(value) >> value_int; info.pixel_format = value_int; } + else if (setting == "spherical") { + std::stringstream(value) >> value_int; + info.spherical = (bool)value_int; + } } read_file = true; inputFile.close(); @@ -156,6 +172,12 @@ std::string Profile::Key() { output << std::setfill('0') << std::setw(4) << fps_string << std::setfill('\0') << "_"; output << std::setfill('0') << std::setw(2) << info.display_ratio.num << std::setfill('\0') << "-"; output << std::setfill('0') << std::setw(2) << info.display_ratio.den << std::setfill('\0'); + + // Add spherical indicator + if (info.spherical) { + output << "_360"; + } + return output.str(); } @@ -168,6 +190,12 @@ std::string Profile::ShortName() { } std::string fps_string = formattedFPS(true); output << info.width << "x" << info.height << progressive_str << fps_string; + + // Add 360° indicator for spherical videos + if (info.spherical) { + output << " 360°"; + } + return output.str(); } @@ -181,6 +209,12 @@ std::string Profile::LongName() { std::string fps_string = formattedFPS(true); output << info.width << "x" << info.height << progressive_str << " @ " << fps_string << " fps (" << info.display_ratio.num << ":" << info.display_ratio.den << ")"; + + // Add 360° indicator for spherical videos + if (info.spherical) { + output << " 360°"; + } + return output.str(); } @@ -193,7 +227,14 @@ std::string Profile::LongNameWithDesc() { } std::string fps_string = formattedFPS(true); output << info.width << "x" << info.height << progressive_str << " @ " << fps_string - << " fps (" << info.display_ratio.num << ":" << info.display_ratio.den << ") " << info.description; + << " fps (" << info.display_ratio.num << ":" << info.display_ratio.den << ")"; + + // Add 360° indicator for spherical videos + if (info.spherical) { + output << " 360°"; + } + + output << " " << info.description; return output.str(); } @@ -214,7 +255,8 @@ void Profile::Save(const std::string& file_path) const { file << "sample_aspect_den=" << info.pixel_ratio.den << "\n"; file << "display_aspect_num=" << info.display_ratio.num << "\n"; file << "display_aspect_den=" << info.display_ratio.den << "\n"; - file << "pixel_format=" << info.pixel_format; + file << "pixel_format=" << info.pixel_format << "\n"; + file << "spherical=" << info.spherical; file.close(); } @@ -245,6 +287,7 @@ Json::Value Profile::JsonValue() const { root["display_ratio"]["num"] = info.display_ratio.num; root["display_ratio"]["den"] = info.display_ratio.den; root["progressive"] = !info.interlaced_frame; + root["spherical"] = info.spherical; // return JsonValue return root; @@ -294,5 +337,7 @@ void Profile::SetJsonValue(const Json::Value root) { } if (!root["progressive"].isNull()) info.interlaced_frame = !root["progressive"].asBool(); + if (!root["spherical"].isNull()) + info.spherical = root["spherical"].asBool(); } diff --git a/src/Profiles.h b/src/Profiles.h index 1181431d..5782441a 100644 --- a/src/Profiles.h +++ b/src/Profiles.h @@ -45,7 +45,8 @@ namespace openshot Fraction fps; ///< Frames per second, as a fraction (i.e. 24/1 = 24 fps) Fraction pixel_ratio; ///< The pixel ratio of the video stream as a fraction (i.e. some pixels are not square) Fraction display_ratio; ///< The ratio of width to height of the video stream (i.e. 640x480 has a ratio of 4/3) - bool interlaced_frame; // Are the contents of this frame interlaced + bool interlaced_frame; ///< Are the contents of this frame interlaced + bool spherical; ///< Is this video a spherical/360° video }; /** @@ -127,8 +128,8 @@ namespace openshot /// Equality operator (compare profile objects) friend bool operator==(const Profile& l, const Profile& r) { - return std::tie(l.info.width, l.info.height, l.info.fps.num, l.info.fps.den, l.info.display_ratio.num, l.info.display_ratio.den, l.info.interlaced_frame) - == std::tie(r.info.width, r.info.height, r.info.fps.num, r.info.fps.den, r.info.display_ratio.num, r.info.display_ratio.den, r.info.interlaced_frame); + return std::tie(l.info.width, l.info.height, l.info.fps.num, l.info.fps.den, l.info.display_ratio.num, l.info.display_ratio.den, l.info.interlaced_frame, l.info.spherical) + == std::tie(r.info.width, r.info.height, r.info.fps.num, r.info.fps.den, r.info.display_ratio.num, r.info.display_ratio.den, r.info.interlaced_frame, r.info.spherical); } public: @@ -160,4 +161,4 @@ namespace openshot } -#endif +#endif \ No newline at end of file diff --git a/tests/Profiles.cpp b/tests/Profiles.cpp index b9f41a4a..d515655c 100644 --- a/tests/Profiles.cpp +++ b/tests/Profiles.cpp @@ -30,6 +30,7 @@ TEST_CASE( "empty constructor", "[libopenshot][profile]" ) CHECK(p1.info.pixel_ratio.num == 0); CHECK(p1.info.pixel_ratio.den == 0); CHECK(p1.info.interlaced_frame == false); + CHECK(p1.info.spherical == false); } @@ -51,6 +52,7 @@ TEST_CASE( "constructor with example profiles", "[libopenshot][profile]" ) CHECK(p1.info.pixel_ratio.num == 1); CHECK(p1.info.pixel_ratio.den == 1); CHECK(p1.info.interlaced_frame == false); + CHECK(p1.info.spherical == false); // Export to JSON openshot::Profile p1_json = openshot::Profile(); @@ -66,6 +68,7 @@ TEST_CASE( "constructor with example profiles", "[libopenshot][profile]" ) CHECK(p1_json.info.pixel_ratio.num == 1); CHECK(p1_json.info.pixel_ratio.den == 1); CHECK(p1_json.info.interlaced_frame == false); + CHECK(p1_json.info.spherical == false); std::stringstream profile2; profile2 << TEST_MEDIA_PATH << "example_profile2"; @@ -83,6 +86,7 @@ TEST_CASE( "constructor with example profiles", "[libopenshot][profile]" ) CHECK(p2.info.pixel_ratio.num == 1); CHECK(p2.info.pixel_ratio.den == 1); CHECK(p2.info.interlaced_frame == true); + CHECK(p2.info.spherical == false); } TEST_CASE( "24 fps names", "[libopenshot][profile]" ) @@ -163,7 +167,6 @@ TEST_CASE( "save profiles", "[libopenshot][profile]" ) // Save copy std::stringstream profile1_copy; profile1_copy << TEST_MEDIA_PATH << "example_profile1_copy"; - std::cout << profile1_copy.str() << std::endl; p1.Save(profile1_copy.str()); // Load saved copy @@ -180,4 +183,52 @@ TEST_CASE( "save profiles", "[libopenshot][profile]" ) CHECK(p1_load_copy.info.pixel_ratio.num == 1); CHECK(p1_load_copy.info.pixel_ratio.den == 1); CHECK(p1_load_copy.info.interlaced_frame == false); + CHECK(p1_load_copy.info.spherical == false); +} + +TEST_CASE( "spherical profiles", "[libopenshot][profile]" ) +{ + // Create a new profile with spherical=true + openshot::Profile p; + p.info.description = "360° Test Profile"; + p.info.width = 3840; + p.info.height = 1920; + p.info.fps.num = 30; + p.info.fps.den = 1; + p.info.display_ratio.num = 2; + p.info.display_ratio.den = 1; + p.info.pixel_ratio.num = 1; + p.info.pixel_ratio.den = 1; + p.info.interlaced_frame = false; + p.info.spherical = true; + + // Test the name methods for spherical content + CHECK(p.Key() == "03840x1920p0030_02-01_360"); + CHECK(p.ShortName() == "3840x1920p30 360°"); + CHECK(p.LongName() == "3840x1920p @ 30 fps (2:1) 360°"); + CHECK(p.LongNameWithDesc() == "3840x1920p @ 30 fps (2:1) 360° 360° Test Profile"); + + // Test JSON serialization and deserialization + std::string json = p.Json(); + openshot::Profile p_json; + p_json.SetJson(json); + + CHECK(p_json.info.spherical == true); + CHECK(p_json.ShortName() == "3840x1920p30 360°"); + + // Save and reload to test file I/O + std::stringstream profile_path; + profile_path << TEST_MEDIA_PATH << "example_profile_360"; + p.Save(profile_path.str()); + + // Load the saved profile + openshot::Profile p_loaded(profile_path.str()); + CHECK(p_loaded.info.spherical == true); + CHECK(p_loaded.ShortName() == "3840x1920p30 360°"); + + // Test comparison operators + openshot::Profile p_non_spherical = p; + p_non_spherical.info.spherical = false; + + CHECK_FALSE(p == p_non_spherical); } \ No newline at end of file