-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Initial work on C++ examples * Start conversion of example flatten_video_tracks.py * Add option to build CXX examples * Use Retainers instead of raw pointers * Implement Windows functionality * Separate Python adapter examples * Add POSIX code * Clone the audio tracks * Avoid temp files by using Python directly * Separate CMake logic for pure C++ examples and C++/Python examples
- Loading branch information
1 parent
c66ff11
commit d25cd13
Showing
9 changed files
with
780 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
find_package(PythonLibs REQUIRED) | ||
|
||
include_directories(${PROJECT_SOURCE_DIR}/src | ||
${PROJECT_SOURCE_DIR}/src/deps | ||
${PROJECT_SOURCE_DIR}/src/deps/optional-lite/include | ||
${PYTHON_INCLUDE_DIRS}) | ||
|
||
list(APPEND examples flatten_video_tracks) | ||
list(APPEND examples summarize_timing) | ||
if(OTIO_PYTHON_INSTALL) | ||
list(APPEND examples python_adapters_child_process) | ||
list(APPEND examples python_adapters_embed) | ||
endif() | ||
foreach(example ${examples}) | ||
add_executable(${example} ${example}.cpp util.h util.cpp) | ||
target_link_libraries(${example} OTIO::opentimelineio ${PYTHON_LIBRARIES}) | ||
set_target_properties(${example} PROPERTIES FOLDER examples) | ||
endforeach() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#include "util.h" | ||
|
||
#include <opentimelineio/stackAlgorithm.h> | ||
#include <opentimelineio/timeline.h> | ||
|
||
#include <iostream> | ||
#include <sstream> | ||
|
||
namespace otio = opentimelineio::OPENTIMELINEIO_VERSION; | ||
|
||
int main(int argc, char** argv) | ||
{ | ||
if (argc != 3) | ||
{ | ||
std::cout << "Usage: flatten_video_tracks (inputpath) (outputpath)" << std::endl; | ||
return 1; | ||
} | ||
|
||
// Read the file | ||
otio::ErrorStatus error_status; | ||
otio::SerializableObject::Retainer<otio::Timeline> timeline(dynamic_cast<otio::Timeline*>(otio::Timeline::from_json_file(argv[1], &error_status))); | ||
if (!timeline) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
auto video_tracks = timeline.value->video_tracks(); | ||
auto audio_tracks = timeline.value->audio_tracks(); | ||
|
||
std::cout << "Read " << video_tracks.size() << " video tracks and " << | ||
audio_tracks.size() << " audio tracks." << std::endl; | ||
|
||
// Take just the video tracks - and flatten them into one. | ||
// This will trim away any overlapping segments, collapsing everything | ||
// into a single track. | ||
std::cout << "Flattening " << video_tracks.size() << " video tracks into one..." << std::endl; | ||
auto onetrack = otio::flatten_stack(video_tracks, &error_status); | ||
if (!onetrack) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
|
||
// Now make a new empty Timeline and put that one Track into it | ||
std::string name; | ||
std::stringstream ss(name); | ||
ss << timeline.value->name() << " Flattened"; | ||
auto newtimeline = otio::SerializableObject::Retainer<otio::Timeline>(new otio::Timeline(ss.str())); | ||
auto stack = otio::SerializableObject::Retainer<otio::Stack>(new otio::Stack()); | ||
newtimeline.value->set_tracks(stack); | ||
if (!stack.value->append_child(onetrack, &error_status)) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
|
||
// keep the audio track(s) as-is | ||
for (const auto& audio_track : audio_tracks) | ||
{ | ||
auto clone = dynamic_cast<otio::Track*>(audio_track->clone(&error_status)); | ||
if (!clone) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
if (!stack.value->append_child(clone, &error_status)) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
} | ||
|
||
// ...and save it to disk. | ||
std::cout << "Saving " << newtimeline.value->video_tracks().size() << " video tracks and " << | ||
newtimeline.value->audio_tracks().size() << " audio tracks." << std::endl; | ||
if (!timeline.value->to_json_file(argv[2], &error_status)) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
// Example OTIO C++ code for reading and writing files supported by the OTIO | ||
// Python adapters. | ||
// | ||
// This example uses the "otioconvert" utility in a child process to convert | ||
// between input/output files and JSON that can be used from C++ code. | ||
// | ||
// To run this example make sure that the "otioconvert" utility is in your | ||
// search path and the environment variable PYTHONPATH is set correctly. | ||
|
||
#include "util.h" | ||
|
||
#include <opentimelineio/timeline.h> | ||
|
||
#include <iostream> | ||
#include <sstream> | ||
|
||
#if defined(_WINDOWS) | ||
#ifndef WIN32_LEAN_AND_MEAN | ||
#define WIN32_LEAN_AND_MEAN | ||
#endif // WIN32_LEAN_AND_MEAN | ||
#include <cctype> | ||
#include <codecvt> | ||
#include <locale> | ||
#include <windows.h> | ||
#include <combaseapi.h> | ||
#else // _WINDOWS | ||
#include <stdio.h> | ||
#endif // _WINDOWS | ||
|
||
namespace otio = opentimelineio::OPENTIMELINEIO_VERSION; | ||
|
||
class PythonAdapters | ||
{ | ||
public: | ||
static otio::SerializableObject::Retainer<otio::Timeline> read_from_file( | ||
std::string const&, | ||
otio::ErrorStatus*); | ||
|
||
static bool write_to_file( | ||
otio::SerializableObject::Retainer<otio::Timeline> const&, | ||
std::string const&, | ||
otio::ErrorStatus*); | ||
|
||
private: | ||
static bool _run_process(std::string const& cmd_line, otio::ErrorStatus*); | ||
}; | ||
|
||
otio::SerializableObject::Retainer<otio::Timeline> PythonAdapters::read_from_file( | ||
std::string const& file_name, | ||
otio::ErrorStatus* error_status) | ||
{ | ||
// Convert the input file to a temporary JSON file. | ||
const std::string temp_file_name = create_temp_dir() + "/temp.otio"; | ||
std::stringstream ss; | ||
ss << "otioconvert" << " -i " << normalize_path(file_name) << " -o " << temp_file_name; | ||
_run_process(ss.str(), error_status); | ||
|
||
// Read the temporary JSON file. | ||
return dynamic_cast<otio::Timeline*>(otio::Timeline::from_json_file(temp_file_name, error_status)); | ||
} | ||
|
||
bool PythonAdapters::write_to_file( | ||
otio::SerializableObject::Retainer<otio::Timeline> const& timeline, | ||
std::string const& file_name, | ||
otio::ErrorStatus* error_status) | ||
{ | ||
// Write the temporary JSON file. | ||
const std::string temp_file_name = create_temp_dir() + "/temp.otio"; | ||
if (!timeline.value->to_json_file(temp_file_name, error_status)) | ||
{ | ||
return false; | ||
} | ||
|
||
// Convert the temporary JSON file to the output file. | ||
std::stringstream ss; | ||
ss << "otioconvert" << " -i " << temp_file_name << " -o " << normalize_path(file_name); | ||
_run_process(ss.str(), error_status); | ||
|
||
return true; | ||
} | ||
|
||
#if defined(_WINDOWS) | ||
|
||
class WCharBuffer | ||
{ | ||
public: | ||
WCharBuffer(const WCHAR* data, size_t size) | ||
{ | ||
p = new WCHAR[(size + 1) * sizeof(WCHAR)]; | ||
memcpy(p, data, size * sizeof(WCHAR)); | ||
p[size] = 0; | ||
} | ||
|
||
~WCharBuffer() | ||
{ | ||
delete[] p; | ||
} | ||
|
||
WCHAR* p = nullptr; | ||
}; | ||
|
||
bool PythonAdapters::_run_process(const std::string& cmd_line, otio::ErrorStatus* error_status) | ||
{ | ||
// Convert the command-line to UTF16. | ||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> utf16; | ||
std::wstring w_cmd_line = utf16.from_bytes("/c " + cmd_line); | ||
WCharBuffer w_cmd_line_buf(w_cmd_line.c_str(), w_cmd_line.size()); | ||
|
||
// Create the process and wait for it to complete. | ||
STARTUPINFOW si; | ||
ZeroMemory(&si, sizeof(si)); | ||
PROCESS_INFORMATION pi; | ||
si.cb = sizeof(si); | ||
ZeroMemory(&pi, sizeof(pi)); | ||
if (0 == CreateProcessW( | ||
// TODO: MSDN documentation says to use "cmd.exe" for the "lpApplicationName" | ||
// argument, but that gives the error: "The system cannot find the file specified." | ||
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw | ||
//L"cmd.exe", | ||
L"C:\\windows\\system32\\cmd.exe", | ||
w_cmd_line_buf.p, | ||
NULL, | ||
NULL, | ||
FALSE, | ||
0, | ||
NULL, | ||
NULL, | ||
&si, | ||
&pi)) | ||
{ | ||
const DWORD error = GetLastError(); | ||
TCHAR error_buf[4096]; | ||
FormatMessage( | ||
FORMAT_MESSAGE_FROM_SYSTEM | | ||
FORMAT_MESSAGE_IGNORE_INSERTS, | ||
NULL, | ||
error, | ||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||
error_buf, | ||
4096, | ||
NULL); | ||
error_status->outcome = otio::ErrorStatus::Outcome::FILE_OPEN_FAILED; | ||
error_status->details = "cannot create process: " + std::string(error_buf, lstrlen(error_buf)); | ||
return false; | ||
} | ||
WaitForSingleObject(pi.hProcess, INFINITE); | ||
CloseHandle(pi.hProcess); | ||
CloseHandle(pi.hThread); | ||
|
||
return true; | ||
} | ||
|
||
#else // _WINDOWS | ||
|
||
bool PythonAdapters::_run_process(const std::string& cmd_line, otio::ErrorStatus* error_status) | ||
{ | ||
FILE* f = popen(cmd_line.c_str(), "r"); | ||
if (!f) | ||
{ | ||
error_status->outcome = otio::ErrorStatus::Outcome::FILE_OPEN_FAILED; | ||
error_status->details = "cannot create process"; | ||
return false; | ||
} | ||
if (-1 == pclose(f)) | ||
{ | ||
error_status->outcome = otio::ErrorStatus::Outcome::FILE_OPEN_FAILED; | ||
error_status->details = "cannot execute process"; | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
#endif // _WINDOWS | ||
|
||
int main(int argc, char** argv) | ||
{ | ||
if (argc != 3) | ||
{ | ||
std::cout << "Usage: python_adapters_child_process (inputpath) (outputpath)" << std::endl; | ||
return 1; | ||
} | ||
|
||
otio::ErrorStatus error_status; | ||
auto timeline = PythonAdapters::read_from_file(argv[1], &error_status); | ||
if (!timeline) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
|
||
std::cout << "Video tracks: " << timeline.value->video_tracks().size() << std::endl; | ||
std::cout << "Audio tracks: " << timeline.value->audio_tracks().size() << std::endl; | ||
|
||
if (!PythonAdapters::write_to_file(timeline, argv[2], &error_status)) | ||
{ | ||
print_error(error_status); | ||
return 1; | ||
} | ||
|
||
return 0; | ||
} |
Oops, something went wrong.