Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ target_sources(raven
editing.h
inspector.h
timeline.h
tools.h
colors.h
widgets.h

app.cpp
editing.cpp
inspector.cpp
timeline.cpp
tools.cpp
colors.cpp
widgets.cpp

Expand Down
62 changes: 62 additions & 0 deletions app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "imgui_internal.h"

#include "widgets.h"
#include "tools.h"

#ifndef EMSCRIPTEN
#include "nfd.h"
Expand Down Expand Up @@ -581,6 +582,14 @@ void MainInit(int argc, char** argv, int initial_width, int initial_height) {

LoadFonts();

// Check for otiotool
if (otiotool_found()) {
appState.otiotool_found = true;
Message("otiotool found, relevant tools have been enabled");
} else {
Message("oitotool not found, relevant tools have been disabled");
}

if (argc > 1) {
LoadFile(argv[1]);
}
Expand Down Expand Up @@ -1066,6 +1075,59 @@ void DrawMenu() {
ImGui::EndMenu();
}

if (ImGui::BeginMenu("Tools", GetActiveRoot() && appState.otiotool_found)) {
std::string current_file = appState.active_tab->file_path;
if (ImGui::MenuItem("Redact OTIO File")) {
if (Redact()) {
Message("Successfully redacted %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to redact %s\n", current_file.c_str());
}
}
if (ImGui::BeginMenu("Extract Track Type")) {
if (ImGui::MenuItem("Video Tracks Only")) {
if (VideoOnly()) {
Message("Sucessfully extracted video tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to extract video tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Audio Tracks Only")) {
if (AudioOnly()) {
Message("Sucessfully extracted audio tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to extract audio tracks from %s\n", current_file.c_str());
}
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Flatten Tracks")) {
if (ImGui::MenuItem("All")) {
if (FlattenAllTracks()) {
Message("Sucessfully flattened tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Video")) {
if (FlattenVideoTracks()) {
Message("Sucessfully flattened video tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten video tracks from %s\n", current_file.c_str());
}
}
if (ImGui::MenuItem("Audio")) {
if (FlattenAudioTracks()) {
Message("Sucessfully flattened audio tracks from %s\n", current_file.c_str());
} else {
ErrorMessage("Failed to flatten audio tracks from %s\n", current_file.c_str());
}
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}

if (ImGui::BeginMenu("View")) {
bool showTimecodeOnClips = appState.track_height >= appState.default_track_height * 2;
if (ImGui::MenuItem(
Expand Down
3 changes: 3 additions & 0 deletions app.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ struct AppState {
bool show_demo_window = false;
bool show_metrics = false;
bool show_implot_demo_window = false;

// Was otiotool found?
bool otiotool_found = false;
};

extern AppState appState;
Expand Down
2 changes: 1 addition & 1 deletion inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -803,7 +803,7 @@ void DrawInspector() {
}

// Set the active media ref key based on user selection
if (ImGui::Combo("", &appState.selected_reference_index, reference_names.data(), num_references)) {
if (ImGui::Combo("##", &appState.selected_reference_index, reference_names.data(), num_references)) {
if (appState.selected_reference_index >= 0 && appState.selected_reference_index < num_references) {
clip->set_active_media_reference_key(reference_names[appState.selected_reference_index]);
}
Expand Down
104 changes: 104 additions & 0 deletions tools.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Tools
#include "tools.h"

#include "app.h"

#include <map>
#include <filesystem>
#include <cstdio>

std::string run_subprocess(const std::string cmd, int& return_val)
{
#ifdef _WIN32
auto pipe = _popen(cmd.c_str(), "r");
#else
auto pipe = popen(cmd.c_str(), "r");
#endif

if (pipe == nullptr) {
std::cout << "Failed to open pipe" << std::endl;
return std::string();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to set return_val in this early exit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks that is a good point

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 1 a suitable return value here or would -1 be better to indicate it isn't returning the value from the subprocess but a prior error?

}

char buffer[128];
std::string result;
while (fgets(buffer, sizeof buffer, pipe) != NULL){
result += buffer;
}

#ifdef _WIN32
return_val = _pclose(pipe);
#else
return_val = pclose(pipe);
#endif

return result;
}

bool otiotool_found()
{
int result;

run_subprocess("otiotool -h", result);

return !result;
}

bool run_otiotool_command(std::string options, bool debug = false)
{
// Write the current root to a temp json file
std::filesystem::path file = std::filesystem::temp_directory_path();
file.replace_filename(std::tmpnam(nullptr));
file.replace_extension("otio");
if (debug) {
std::cout << file << std::endl;
}
GetActiveRoot()->to_json_file(file.generic_string());

// Build command
std::string command = "otiotool --input " + file.generic_string() + " " + options + " --output -";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if temp_directory_path() or tmpnam() would ever have spaces or punctuation in them, but if they did this command would get tripped up. In Python there's a variant of popen that takes an array of strings instead of relying on shell parsing to avoid this. Is there an equivalent in C++?

Alternately, if you can pass the input to the subprocess's stdin, then you could use "otiotool --input - " + options + " --output -"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like on both Linux and Windows the temp folder can be overridden using various enviroment variables, so yes in theory a path could have spaces in it. Presumably wrapping the path in quote marks would solve this?

I did look into passing to stdin but pipes in c++ can only go one way, either write to stdin or read from std out (which we are doing here). You can get around it by using two pipes, one for read and another for write but it gets a lot more complicated and less cross platform friendly. I also tried messing around with passing the input otio as a string but that failed straight away as there is a character limit to terminal commands.

if (debug) {
std::cout << command << std::endl;
}

// Run subproces
int return_val = 0;
std::string result = run_subprocess(command, return_val);

// Load new otio file
if (!result.empty() && return_val == 0) {
LoadString(result);
} else {
ErrorMessage("Error trying to redact file, see console");
return false;
}

// Clean up temp file
std::remove(file.generic_string().c_str());

return true;
}

bool Redact() {
return run_otiotool_command("--redact");
}

bool VideoOnly() {
return run_otiotool_command("--video-only");
}

bool AudioOnly() {
return run_otiotool_command("--audio-only");
}

bool FlattenAllTracks() {
return run_otiotool_command("--flatten all");
}

bool FlattenVideoTracks() {
return run_otiotool_command("--flatten video");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really elegant and simple way to capture otiotool's features, and I could see a future way to gather some more complex recipes into simple menu items like this too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I was thinking this would also be the place to put any UI code that might need to pop up to configure a command or display a result.

}

bool FlattenAudioTracks() {
return run_otiotool_command("--flatten audio");
}
23 changes: 23 additions & 0 deletions tools.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Tools


bool otiotool_found();

// Remove all metadata, names, or other identifying information from this
// timeline. Only the structure, schema and timing will remain.
bool Redact();

// Output only video tracks
bool VideoOnly();

// Output only audio tracks
bool AudioOnly();

// Flatten all tracks
bool FlattenAllTracks();

// Flatten vieo tracks
bool FlattenVideoTracks();

// Flatten audio tracks
bool FlattenAudioTracks();