diff --git a/include/ClipBase.h b/include/ClipBase.h
index c8d517dd..661c26c7 100644
--- a/include/ClipBase.h
+++ b/include/ClipBase.h
@@ -49,6 +49,7 @@ namespace openshot {
*/
class ClipBase {
private:
+ string id; ///< ID Property for all derived Clip and Effect classes.
float position; ///< The position on the timeline where this clip should start playing
int layer; ///< The layer this clip is on. Lower clips are covered up by higher clips.
float start; ///< The position in seconds to start playing (used to trim the beginning of a clip)
@@ -63,6 +64,7 @@ namespace openshot {
bool operator>= ( ClipBase& a) { return (Position() >= a.Position()); }
/// Get basic properties
+ string Id() { return id; } ///< Get the Id of this clip object
float Position() { return position; } ///< Get position on timeline (in seconds)
int Layer() { return layer; } ///< Get layer of clip on timeline (lower number is covered by higher numbers)
float Start() { return start; } ///< Get start position (in seconds) of clip (trim start of video)
@@ -70,6 +72,7 @@ namespace openshot {
float Duration() { return End() - Start(); } ///< Get the length of this clip (in seconds)
/// Set basic properties
+ void Id(string value) { id = value; } ///> Set the Id of this clip object
void Position(float value) { position = value; } ///< Set position on timeline (in seconds)
void Layer(int value) { layer = value; } ///< Set layer of clip on timeline (lower number is covered by higher numbers)
void Start(float value) { start = value; } ///< Set start position (in seconds) of clip (trim start of video)
diff --git a/include/Exceptions.h b/include/Exceptions.h
index bae491e8..fc227ca2 100644
--- a/include/Exceptions.h
+++ b/include/Exceptions.h
@@ -178,6 +178,16 @@ namespace openshot {
virtual ~InvalidSampleRate() throw () {}
};
+ /// Exception for missing JSON Change key
+ class InvalidJSONKey : public BaseException
+ {
+ public:
+ string json;
+ InvalidJSONKey(string message, string json)
+ : BaseException(message), json(json) { }
+ virtual ~InvalidJSONKey() throw () {}
+ };
+
/// Exception when no streams are found in the file
class NoStreamsFound : public BaseException
{
diff --git a/include/Timeline.h b/include/Timeline.h
index a7e4eced..1456833b 100644
--- a/include/Timeline.h
+++ b/include/Timeline.h
@@ -156,6 +156,11 @@ namespace openshot {
/// Process a new layer of video or audio
void add_layer(tr1::shared_ptr new_frame, Clip* source_clip, int clip_frame_number, int timeline_frame_number);
+ /// Apply JSON Diffs to various objects contained in this timeline
+ void apply_json_to_clips(Json::Value change) throw(InvalidJSONKey); ///::iterator clip_itr;
+ for (clip_itr=clips.begin(); clip_itr != clips.end(); ++clip_itr)
+ {
+ // Get clip object from the iterator
+ Clip *c = (*clip_itr);
+ if (c->Id() == clip_id) {
+ existing_clip = c;
+ break; // clip found, exit loop
+ }
+ }
+ break; // id found, exit loop
+ }
+ }
+ }
+
+ // Determine type of change operation
+ if (change_type == "insert") {
+
+ // Create new clip
+ Clip *clip = new Clip();
+ clip->SetJsonValue(change["value"]); // Set properties of new clip from JSON
+ AddClip(clip); // Add clip to timeline
+
+ } else if (change_type == "update") {
+
+ // Update existing clip
+ if (existing_clip)
+ existing_clip->SetJsonValue(change["value"]); // Update clip properties from JSON
+
+ } else if (change_type == "delete") {
+
+ // Remove existing clip
+ if (existing_clip)
+ RemoveClip(existing_clip); // Remove clip from timeline
+
+ }
+
+}
+
+// Apply JSON diff to effects
+void Timeline::apply_json_to_effects(Json::Value change) throw(InvalidJSONKey) {
+
+ // Get key and type of change
+ string change_type = change["type"].asString();
+ string effect_id = "";
+ EffectBase *existing_effect = NULL;
+
+ // Find id of an effect (if any)
+ for (int x = 0; x < change["key"].size(); x++) {
+ // Get each change
+ Json::Value key_part = change["key"][x];
+
+ if (key_part.isObject()) {
+ // Check for id
+ if (!key_part["id"].isNull())
+ {
+ // Set the id
+ effect_id = key_part["id"].asString();
+
+ // Find matching effect in timeline (if any)
+ list::iterator effect_itr;
+ for (effect_itr=effects.begin(); effect_itr != effects.end(); ++effect_itr)
+ {
+ // Get effect object from the iterator
+ EffectBase *e = (*effect_itr);
+ if (e->Id() == effect_id) {
+ existing_effect = e;
+ break; // effect found, exit loop
+ }
+ }
+ break; // id found, exit loop
+ }
+ }
+ }
+
+ // Determine type of change operation
+ if (change_type == "insert") {
+
+ // Determine type of effect
+ string effect_type = change["value"]["type"].asString();
+
+ // Create Effect
+ EffectBase *e = NULL;
+
+ // Init the matching effect object
+ if (effect_type == "ChromaKey")
+ e = new ChromaKey();
+
+ else if (effect_type == "Deinterlace")
+ e = new Deinterlace();
+
+ else if (effect_type == "Mask")
+ e = new Mask();
+
+ else if (effect_type == "Negate")
+ e = new Negate();
+
+ // Load Json into Effect
+ e->SetJsonValue(change["value"]);
+
+ // Add Effect to Timeline
+ AddEffect(e);
+
+ } else if (change_type == "update") {
+
+ // Update existing effect
+ if (existing_effect)
+ existing_effect->SetJsonValue(change["value"]); // Update effect properties from JSON
+
+ } else if (change_type == "delete") {
+
+ // Remove existing effect
+ if (existing_effect)
+ RemoveEffect(existing_effect); // Remove effect from timeline
+
+ }
+
+}
+
+// Apply JSON diff to timeline properties
+void Timeline::apply_json_to_timeline(Json::Value change) throw(InvalidJSONKey) {
+
+ // Get key and type of change
+ string change_type = change["type"].asString();
+ string root_key = change["key"][(uint)0].asString();
+
+ // Determine type of change operation
+ if (change_type == "insert" || change_type == "update") {
+
+ // INSERT / UPDATE
+ // Check for valid property
+ if (root_key == "color")
+ // Set color
+ color.SetJsonValue(change["value"]);
+ else if (root_key == "viewport_scale")
+ // Set viewport scale
+ viewport_scale.SetJsonValue(change["value"]);
+ else if (root_key == "viewport_x")
+ // Set viewport x offset
+ viewport_x.SetJsonValue(change["value"]);
+ else if (root_key == "viewport_y")
+ // Set viewport y offset
+ viewport_y.SetJsonValue(change["value"]);
+ else
+ // Error parsing JSON (or missing keys)
+ throw InvalidJSONKey("JSON change key is invalid", change.toStyledString());
+
+
+ } else if (change["type"].asString() == "delete") {
+
+ // DELETE / RESET
+ // Reset the following properties (since we can't delete them)
+ if (root_key == "color") {
+ color = Color();
+ color.red = Keyframe(0.0);
+ color.green = Keyframe(0.0);
+ color.blue = Keyframe(0.0);
+ }
+ else if (root_key == "viewport_scale")
+ viewport_scale = Keyframe(1.0);
+ else if (root_key == "viewport_x")
+ viewport_x = Keyframe(0.0);
+ else if (root_key == "viewport_y")
+ viewport_y = Keyframe(0.0);
+ else
+ // Error parsing JSON (or missing keys)
+ throw InvalidJSONKey("JSON change key is invalid", change.toStyledString());
+
+ }
+
+}
+
+
+
+
diff --git a/src/WriterBase.cpp b/src/WriterBase.cpp
index 08a28342..534dcde5 100644
--- a/src/WriterBase.cpp
+++ b/src/WriterBase.cpp
@@ -209,60 +209,60 @@ void WriterBase::SetJson(string value) throw(InvalidJSON) {
void WriterBase::SetJsonValue(Json::Value root) {
// Set data from Json (if key is found)
- if (root["has_video"] != Json::nullValue)
+ if (!root["has_video"].isNull())
info.has_video = root["has_video"].asBool();
- if (root["has_audio"] != Json::nullValue)
+ if (!root["has_audio"].isNull())
info.has_audio = root["has_audio"].asBool();
- if (root["duration"] != Json::nullValue)
+ if (!root["duration"].isNull())
info.duration = root["duration"].asDouble();
- if (root["file_size"] != Json::nullValue)
+ if (!root["file_size"].isNull())
info.file_size = (long long) root["file_size"].asUInt();
- if (root["height"] != Json::nullValue)
+ if (!root["height"].isNull())
info.height = root["height"].asInt();
- if (root["width"] != Json::nullValue)
+ if (!root["width"].isNull())
info.width = root["width"].asInt();
- if (root["pixel_format"] != Json::nullValue)
+ if (!root["pixel_format"].isNull())
info.pixel_format = root["pixel_format"].asInt();
- if (root["fps"] != Json::nullValue) {
+ if (!root["fps"].isNull()) {
info.fps.num = root["fps"]["num"].asInt();
info.fps.den = root["fps"]["den"].asInt();
}
- if (root["video_bit_rate"] != Json::nullValue)
+ if (!root["video_bit_rate"].isNull())
info.video_bit_rate = root["video_bit_rate"].asInt();
- if (root["pixel_ratio"] != Json::nullValue) {
+ if (!root["pixel_ratio"].isNull()) {
info.pixel_ratio.num = root["pixel_ratio"]["num"].asInt();
info.pixel_ratio.den = root["pixel_ratio"]["den"].asInt();
}
- if (root["display_ratio"] != Json::nullValue) {
+ if (!root["display_ratio"].isNull()) {
info.display_ratio.num = root["display_ratio"]["num"].asInt();
info.display_ratio.den = root["display_ratio"]["den"].asInt();
}
- if (root["vcodec"] != Json::nullValue)
+ if (!root["vcodec"].isNull())
info.vcodec = root["vcodec"].asString();
- if (root["video_length"] != Json::nullValue)
+ if (!root["video_length"].isNull())
info.video_length = (long int) root["video_length"].asUInt();
- if (root["video_stream_index"] != Json::nullValue)
+ if (!root["video_stream_index"].isNull())
info.video_stream_index = root["video_stream_index"].asInt();
- if (root["video_timebase"] != Json::nullValue) {
+ if (!root["video_timebase"].isNull()) {
info.video_timebase.num = root["video_timebase"]["num"].asInt();
info.video_timebase.den = root["video_timebase"]["den"].asInt();
}
- if (root["interlaced_frame"] != Json::nullValue)
+ if (!root["interlaced_frame"].isNull())
info.interlaced_frame = root["interlaced_frame"].asBool();
- if (root["top_field_first"] != Json::nullValue)
+ if (!root["top_field_first"].isNull())
info.top_field_first = root["top_field_first"].asBool();
- if (root["acodec"] != Json::nullValue)
+ if (!root["acodec"].isNull())
info.acodec = root["acodec"].asString();
- if (root["audio_bit_rate"] != Json::nullValue)
+ if (!root["audio_bit_rate"].isNull())
info.audio_bit_rate = root["audio_bit_rate"].asInt();
- if (root["sample_rate"] != Json::nullValue)
+ if (!root["sample_rate"].isNull())
info.sample_rate = root["sample_rate"].asInt();
- if (root["channels"] != Json::nullValue)
+ if (!root["channels"].isNull())
info.channels = root["channels"].asInt();
- if (root["audio_stream_index"] != Json::nullValue)
+ if (!root["audio_stream_index"].isNull())
info.audio_stream_index = root["audio_stream_index"].asInt();
- if (root["audio_timebase"] != Json::nullValue) {
+ if (!root["audio_timebase"].isNull()) {
info.audio_timebase.num = root["audio_timebase"]["num"].asInt();
info.audio_timebase.den = root["audio_timebase"]["den"].asInt();
}
diff --git a/src/effects/ChromaKey.cpp b/src/effects/ChromaKey.cpp
index 833e1017..5c1c0dd7 100644
--- a/src/effects/ChromaKey.cpp
+++ b/src/effects/ChromaKey.cpp
@@ -112,8 +112,8 @@ void ChromaKey::SetJsonValue(Json::Value root) {
EffectBase::SetJsonValue(root);
// Set data from Json (if key is found)
- if (root["color"] != Json::nullValue)
+ if (!root["color"].isNull())
color.SetJsonValue(root["color"]);
- if (root["fuzz"] != Json::nullValue)
+ if (!root["fuzz"].isNull())
fuzz.SetJsonValue(root["fuzz"]);
}
diff --git a/src/effects/Deinterlace.cpp b/src/effects/Deinterlace.cpp
index 557be0b2..7725572a 100644
--- a/src/effects/Deinterlace.cpp
+++ b/src/effects/Deinterlace.cpp
@@ -120,6 +120,6 @@ void Deinterlace::SetJsonValue(Json::Value root) {
EffectBase::SetJsonValue(root);
// Set data from Json (if key is found)
- if (root["isOdd"] != Json::nullValue)
+ if (!root["isOdd"].isNull())
isOdd = root["isOdd"].asBool();
}
diff --git a/src/effects/Mask.cpp b/src/effects/Mask.cpp
index 1cc51190..eaa46fed 100644
--- a/src/effects/Mask.cpp
+++ b/src/effects/Mask.cpp
@@ -178,9 +178,9 @@ void Mask::SetJsonValue(Json::Value root) {
EffectBase::SetJsonValue(root);
// Set data from Json (if key is found)
- if (root["brightness"] != Json::nullValue)
+ if (!root["brightness"].isNull())
brightness.SetJsonValue(root["brightness"]);
- if (root["contrast"] != Json::nullValue)
+ if (!root["contrast"].isNull())
contrast.SetJsonValue(root["contrast"]);
}