26 #include <QRegularExpression>
27 #include <unordered_map>
35 is_open(false), auto_map_clips(true), managed_cache(true),
path(
""), max_time(0.0), cache_epoch(0)
81 info.width, info.height, info.fps, info.sample_rate,
82 info.channels, info.channel_layout) {}
86 is_open(false), auto_map_clips(true), managed_cache(true),
path(projectPath), max_time(0.0), cache_epoch(0) {
105 QFileInfo filePath(QString::fromStdString(path));
106 if (!filePath.exists()) {
107 throw InvalidFile(
"Timeline project file could not be opened.", path);
113 if (!openshotPath.exists()) {
116 QDir openshotTransPath(openshotPath.filePath(
"transitions"));
117 if (!openshotTransPath.exists()) {
118 throw InvalidFile(
"PATH_OPENSHOT_INSTALL/transitions could not be found.", openshotTransPath.path().toStdString());
122 QString asset_name = filePath.baseName().left(30) +
"_assets";
123 QDir asset_folder(filePath.dir().filePath(asset_name));
124 if (!asset_folder.exists()) {
126 asset_folder.mkpath(
".");
130 QFile projectFile(QString::fromStdString(path));
131 projectFile.open(QFile::ReadOnly);
132 QString projectContents = QString::fromUtf8(projectFile.readAll());
135 if (convert_absolute_paths) {
139 QRegularExpression allPathsRegex(QStringLiteral(
"\"(image|path)\":.*?\"(.*?)\""));
140 std::vector<QRegularExpressionMatch> matchedPositions;
141 QRegularExpressionMatchIterator i = allPathsRegex.globalMatch(projectContents);
142 while (i.hasNext()) {
143 QRegularExpressionMatch match = i.next();
144 if (match.hasMatch()) {
146 matchedPositions.push_back(match);
151 std::vector<QRegularExpressionMatch>::reverse_iterator itr;
152 for (itr = matchedPositions.rbegin(); itr != matchedPositions.rend(); itr++) {
153 QRegularExpressionMatch match = *itr;
154 QString relativeKey = match.captured(1);
155 QString relativePath = match.captured(2);
156 QString absolutePath =
"";
159 if (relativePath.startsWith(
"@assets")) {
160 absolutePath = QFileInfo(asset_folder.absoluteFilePath(relativePath.replace(
"@assets",
"."))).canonicalFilePath();
161 }
else if (relativePath.startsWith(
"@transitions")) {
162 absolutePath = QFileInfo(openshotTransPath.absoluteFilePath(relativePath.replace(
"@transitions",
"."))).canonicalFilePath();
164 absolutePath = QFileInfo(filePath.absoluteDir().absoluteFilePath(relativePath)).canonicalFilePath();
168 if (!absolutePath.isEmpty()) {
169 projectContents.replace(match.capturedStart(0), match.capturedLength(0),
"\"" + relativeKey +
"\": \"" + absolutePath +
"\"");
173 matchedPositions.clear();
177 SetJson(projectContents.toStdString());
181 float calculated_duration = 0.0;
182 for (
auto clip : clips)
185 if (clip_last_frame > calculated_duration)
186 calculated_duration = clip_last_frame;
187 if (
clip->Reader() &&
clip->Reader()->info.has_audio)
189 if (
clip->Reader() &&
clip->Reader()->info.has_video)
222 if (managed_cache && final_cache) {
232 auto iterator = tracked_objects.find(trackedObject->Id());
234 if (iterator != tracked_objects.end()){
236 iterator->second = trackedObject;
240 tracked_objects[trackedObject->Id()] = trackedObject;
250 auto iterator = tracked_objects.find(
id);
252 if (iterator != tracked_objects.end()){
254 std::shared_ptr<openshot::TrackedObjectBase> trackedObject = iterator->second;
255 return trackedObject;
267 std::list<std::string> trackedObjects_ids;
270 for (
auto const& it: tracked_objects){
272 trackedObjects_ids.push_back(it.first);
275 return trackedObjects_ids;
283 Json::Value trackedObjectJson;
286 auto iterator = tracked_objects.find(
id);
288 if (iterator != tracked_objects.end())
291 std::shared_ptr<TrackedObjectBBox> trackedObject = std::static_pointer_cast<TrackedObjectBBox>(iterator->second);
294 if (trackedObject->ExactlyContains(frame_number)){
295 BBox box = trackedObject->GetBox(frame_number);
296 float x1 = box.
cx - (box.
width/2);
298 float x2 = box.
cx + (box.
width/2);
300 float rotation = box.
angle;
302 trackedObjectJson[
"x1"] = x1;
303 trackedObjectJson[
"y1"] = y1;
304 trackedObjectJson[
"x2"] = x2;
305 trackedObjectJson[
"y2"] = y2;
306 trackedObjectJson[
"rotation"] = rotation;
309 BBox box = trackedObject->BoxVec.begin()->second;
310 float x1 = box.
cx - (box.
width/2);
312 float x2 = box.
cx + (box.
width/2);
314 float rotation = box.
angle;
316 trackedObjectJson[
"x1"] = x1;
317 trackedObjectJson[
"y1"] = y1;
318 trackedObjectJson[
"x2"] = x2;
319 trackedObjectJson[
"y2"] = y2;
320 trackedObjectJson[
"rotation"] = rotation;
326 trackedObjectJson[
"x1"] = 0;
327 trackedObjectJson[
"y1"] = 0;
328 trackedObjectJson[
"x2"] = 0;
329 trackedObjectJson[
"y2"] = 0;
330 trackedObjectJson[
"rotation"] = 0;
333 return trackedObjectJson.toStyledString();
341 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
344 clip->ParentTimeline(
this);
347 if (
clip->Reader() &&
clip->Reader()->GetCache())
348 clip->Reader()->GetCache()->Clear();
351 if (auto_map_clips) {
353 apply_mapper_to_clip(
clip);
356 InvalidateCacheForClip(
clip);
359 clips.push_back(
clip);
369 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
372 effect->ParentTimeline(
this);
375 effects.push_back(effect);
385 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
387 effects.remove(effect);
390 if (allocated_effects.count(effect)) {
391 allocated_effects.erase(effect);
404 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
409 if (allocated_clips.count(
clip)) {
410 allocated_clips.erase(
clip);
423 for (
const auto&
clip : clips) {
435 for (
const auto& effect : effects) {
436 if (effect->Id() ==
id) {
446 for (
const auto&
clip : clips) {
447 const auto e =
clip->GetEffect(
id);
459 std::list<EffectBase*> timelineEffectsList;
462 for (
const auto&
clip : clips) {
465 std::list<EffectBase*> clipEffectsList =
clip->Effects();
468 timelineEffectsList.insert(timelineEffectsList.end(), clipEffectsList.begin(), clipEffectsList.end());
471 return timelineEffectsList;
484 const double frames = t * fps;
485 constexpr
double frame_boundary_epsilon = 1e-4;
488 if (frames > 0.0 && frames < frame_boundary_epsilon)
490 return static_cast<int64_t
>(std::ceil(frames - frame_boundary_epsilon));
498 return static_cast<int64_t
>(std::floor(t * fps)) + 1;
508 void Timeline::apply_mapper_to_clip(
Clip* clip)
511 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
515 if (
clip->Reader()->Name() ==
"FrameMapper")
528 allocated_frame_mappers.insert(mapper);
533 clip->Reader(clip_reader);
543 for (
auto clip : clips)
546 apply_mapper_to_clip(
clip);
551 double Timeline::calculate_time(int64_t number,
Fraction rate)
554 double raw_fps = rate.
ToFloat();
557 return double(number - 1) / raw_fps;
565 "Timeline::apply_effects",
566 "frame->number", frame->number,
567 "timeline_frame_number", timeline_frame_number,
571 for (
auto effect : effects)
575 int64_t effect_start_position =
static_cast<int64_t
>(std::llround(effect->Position() * fpsD)) + 1;
576 int64_t effect_end_position =
static_cast<int64_t
>(std::llround((effect->Position() + effect->Duration()) * fpsD));
578 bool does_effect_intersect = (effect_start_position <= timeline_frame_number && effect_end_position >= timeline_frame_number && effect->Layer() == layer);
581 if (does_effect_intersect)
584 int64_t effect_start_frame =
static_cast<int64_t
>(std::llround(effect->Start() * fpsD)) + 1;
585 int64_t effect_frame_number = timeline_frame_number - effect_start_position + effect_start_frame;
595 "Timeline::apply_effects (Process Effect)",
596 "effect_frame_number", effect_frame_number,
597 "does_effect_intersect", does_effect_intersect);
600 frame = effect->ProcessFrame(frame, effect_frame_number);
610 std::shared_ptr<Frame> Timeline::GetOrCreateFrame(std::shared_ptr<Frame> background_frame,
Clip* clip, int64_t number,
openshot::TimelineInfoStruct* options)
612 std::shared_ptr<Frame> new_frame;
620 "Timeline::GetOrCreateFrame (from reader)",
622 "samples_in_frame", samples_in_frame);
625 new_frame = std::shared_ptr<Frame>(
clip->
GetFrame(background_frame, number, options));
638 "Timeline::GetOrCreateFrame (create blank)",
640 "samples_in_frame", samples_in_frame);
647 void Timeline::add_layer(std::shared_ptr<Frame> new_frame,
Clip* source_clip, int64_t clip_frame_number,
bool is_top_clip,
float max_volume)
655 std::shared_ptr<Frame> source_frame;
656 source_frame = GetOrCreateFrame(new_frame, source_clip, clip_frame_number, &options);
664 "Timeline::add_layer",
665 "new_frame->number", new_frame->number,
666 "clip_frame_number", clip_frame_number);
669 if (source_clip->
Reader()->info.has_audio) {
672 "Timeline::add_layer (Copy Audio)",
673 "source_clip->Reader()->info.has_audio", source_clip->
Reader()->info.has_audio,
674 "source_frame->GetAudioChannelsCount()", source_frame->GetAudioChannelsCount(),
676 "clip_frame_number", clip_frame_number);
681 if (new_frame->GetAudioSamplesCount() != source_frame->GetAudioSamplesCount()){
686 const auto transition_audio_gains = ResolveTransitionAudioGains(source_clip, new_frame->number, is_top_clip);
688 for (
int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++)
691 float previous_volume = source_clip->
volume.
GetValue(clip_frame_number - 1);
693 previous_volume *= transition_audio_gains.first;
694 volume *= transition_audio_gains.second;
701 previous_volume = previous_volume / max_volume;
702 volume = volume / max_volume;
706 previous_volume = previous_volume * 0.77;
707 volume = volume * 0.77;
711 if (channel_filter != -1 && channel_filter != channel)
715 if (previous_volume == 0.0 && volume == 0.0)
719 if (channel_mapping == -1)
720 channel_mapping = channel;
723 if (!isEqual(previous_volume, 1.0) || !isEqual(volume, 1.0))
724 source_frame->ApplyGainRamp(channel_mapping, 0, source_frame->GetAudioSamplesCount(), previous_volume, volume);
728 new_frame->AddAudio(
false, channel_mapping, 0, source_frame->GetAudioSamples(channel), source_frame->GetAudioSamplesCount(), 1.0);
734 "Timeline::add_layer (No Audio Copied - Wrong # of Channels)",
735 "source_clip->Reader()->info.has_audio",
736 source_clip->
Reader()->info.has_audio,
737 "source_frame->GetAudioChannelsCount()",
738 source_frame->GetAudioChannelsCount(),
740 "clip_frame_number", clip_frame_number);
745 "Timeline::add_layer (Transform: Composite Image Layer: Completed)",
746 "source_frame->number", source_frame->number,
747 "new_frame->GetImage()->width()", new_frame->GetWidth(),
748 "new_frame->GetImage()->height()", new_frame->GetHeight());
752 void Timeline::update_open_clips(
Clip *clip,
bool does_clip_intersect)
755 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
758 "Timeline::update_open_clips (before)",
759 "does_clip_intersect", does_clip_intersect,
760 "closing_clips.size()", closing_clips.size(),
761 "open_clips.size()", open_clips.size());
764 bool clip_found = open_clips.count(
clip);
766 if (clip_found && !does_clip_intersect)
769 open_clips.erase(
clip);
774 else if (!clip_found && does_clip_intersect)
789 "Timeline::update_open_clips (after)",
790 "does_clip_intersect", does_clip_intersect,
791 "clip_found", clip_found,
792 "closing_clips.size()", closing_clips.size(),
793 "open_clips.size()", open_clips.size());
797 void Timeline::calculate_max_duration() {
798 double last_clip = 0.0;
799 double last_effect = 0.0;
800 double first_clip = std::numeric_limits<double>::max();
801 double first_effect = std::numeric_limits<double>::max();
804 if (!clips.empty()) {
806 const auto max_clip = std::max_element(
808 last_clip = (*max_clip)->Position() + (*max_clip)->Duration();
811 const auto min_clip = std::min_element(
813 return lhs->Position() < rhs->Position();
815 first_clip = (*min_clip)->Position();
819 if (!effects.empty()) {
821 const auto max_effect = std::max_element(
823 last_effect = (*max_effect)->Position() + (*max_effect)->Duration();
826 const auto min_effect = std::min_element(
828 return lhs->Position() < rhs->Position();
830 first_effect = (*min_effect)->Position();
834 max_time = std::max(last_clip, last_effect);
835 min_time = std::min(first_clip, first_effect);
838 if (clips.empty() && effects.empty()) {
845 void Timeline::sort_clips()
848 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
852 "Timeline::SortClips",
853 "clips.size()", clips.size());
859 calculate_max_duration();
863 void Timeline::sort_effects()
866 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
872 calculate_max_duration();
881 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
884 for (
auto clip : clips)
886 update_open_clips(
clip,
false);
889 bool allocated = allocated_clips.count(
clip);
896 allocated_clips.clear();
899 for (
auto effect : effects)
902 bool allocated = allocated_effects.count(effect);
909 allocated_effects.clear();
912 for (
auto mapper : allocated_frame_mappers)
918 allocated_frame_mappers.clear();
927 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
930 for (
auto clip : clips)
933 update_open_clips(
clip,
false);
950 bool Timeline::isEqual(
double a,
double b)
952 return fabs(a - b) < 0.000001;
959 if (requested_frame < 1)
962 const bool past_timeline_end = (max_frame > 0 && requested_frame > max_frame);
965 std::shared_ptr<Frame> frame;
966 if (!past_timeline_end)
967 frame = final_cache->
GetFrame(requested_frame);
971 "Timeline::GetFrame (Cached frame found)",
972 "requested_frame", requested_frame);
980 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
983 std::shared_ptr<Frame> frame;
984 if (!past_timeline_end)
985 frame = final_cache->
GetFrame(requested_frame);
989 "Timeline::GetFrame (Cached frame found on 2nd check)",
990 "requested_frame", requested_frame);
997 std::vector<Clip *> nearby_clips;
998 nearby_clips = find_intersecting_clips(requested_frame, 1,
true);
1002 "Timeline::GetFrame (processing frame)",
1003 "requested_frame", requested_frame,
1004 "omp_get_thread_num()", omp_get_thread_num());
1011 new_frame->AddAudioSilence(samples_in_frame);
1017 "Timeline::GetFrame (Adding solid color)",
1018 "requested_frame", requested_frame,
1030 "Timeline::GetFrame (Loop through clips)",
1031 "requested_frame", requested_frame,
1032 "clips.size()", clips.size(),
1033 "nearby_clips.size()", nearby_clips.size());
1040 int64_t start_frame;
1041 int64_t frame_number;
1044 std::vector<ClipInfo> clip_infos;
1045 clip_infos.reserve(nearby_clips.size());
1048 for (
auto clip : nearby_clips) {
1049 int64_t start_pos =
static_cast<int64_t
>(std::llround(
clip->
Position() * fpsD)) + 1;
1050 int64_t end_pos =
static_cast<int64_t
>(std::llround((
clip->
Position() +
clip->Duration()) * fpsD));
1051 bool intersects = (start_pos <= requested_frame && end_pos >= requested_frame);
1052 int64_t start_frame =
static_cast<int64_t
>(std::llround(
clip->
Start() * fpsD)) + 1;
1053 int64_t frame_number = requested_frame - start_pos + start_frame;
1054 clip_infos.push_back({
clip, start_pos, end_pos, start_frame, frame_number, intersects});
1058 std::unordered_map<int, int64_t> top_start_for_layer;
1059 std::unordered_map<int, Clip*> top_clip_for_layer;
1060 for (
const auto& ci : clip_infos) {
1061 if (!ci.intersects)
continue;
1062 const int layer = ci.clip->Layer();
1063 auto it = top_start_for_layer.find(layer);
1064 if (it == top_start_for_layer.end() || ci.start_pos > it->second) {
1065 top_start_for_layer[layer] = ci.start_pos;
1066 top_clip_for_layer[layer] = ci.clip;
1071 float max_volume_sum = 0.0f;
1072 for (
const auto& ci : clip_infos) {
1073 if (!ci.intersects)
continue;
1074 if (ci.clip->Reader() && ci.clip->Reader()->info.has_audio &&
1075 ci.clip->has_audio.GetInt(ci.frame_number) != 0) {
1076 max_volume_sum +=
static_cast<float>(ci.clip->volume.GetValue(ci.frame_number));
1081 for (
const auto& ci : clip_infos) {
1084 "Timeline::GetFrame (Does clip intersect)",
1085 "requested_frame", requested_frame,
1086 "clip->Position()", ci.clip->Position(),
1087 "clip->Duration()", ci.clip->Duration(),
1088 "does_clip_intersect", ci.intersects);
1091 if (ci.intersects) {
1093 bool is_top_clip =
false;
1094 const int layer = ci.clip->Layer();
1095 auto top_it = top_clip_for_layer.find(layer);
1096 if (top_it != top_clip_for_layer.end())
1097 is_top_clip = (top_it->second == ci.clip);
1100 int64_t clip_frame_number = ci.frame_number;
1104 "Timeline::GetFrame (Calculate clip's frame #)",
1105 "clip->Position()", ci.clip->Position(),
1106 "clip->Start()", ci.clip->Start(),
1108 "clip_frame_number", clip_frame_number);
1111 add_layer(new_frame, ci.clip, clip_frame_number, is_top_clip, max_volume_sum);
1116 "Timeline::GetFrame (clip does not intersect)",
1117 "requested_frame", requested_frame,
1118 "does_clip_intersect", ci.intersects);
1125 "Timeline::GetFrame (Add frame to cache)",
1126 "requested_frame", requested_frame,
1131 new_frame->SetFrameNumber(requested_frame);
1134 if (!past_timeline_end)
1135 final_cache->
Add(new_frame);
1144 std::vector<Clip*> Timeline::find_intersecting_clips(int64_t requested_frame,
int number_of_frames,
bool include)
1147 std::vector<Clip*> matching_clips;
1150 const int64_t min_requested_frame = requested_frame;
1151 const int64_t max_requested_frame = requested_frame + (number_of_frames - 1);
1154 matching_clips.reserve(clips.size());
1156 for (
auto clip : clips)
1159 int64_t clip_start_position =
static_cast<int64_t
>(std::llround(
clip->
Position() * fpsD)) + 1;
1160 int64_t clip_end_position =
static_cast<int64_t
>(std::llround((
clip->
Position() +
clip->Duration()) * fpsD)) + 1;
1162 bool does_clip_intersect =
1163 (clip_start_position <= min_requested_frame || clip_start_position <= max_requested_frame) &&
1164 (clip_end_position >= min_requested_frame || clip_end_position >= max_requested_frame);
1168 "Timeline::find_intersecting_clips (Is clip near or intersecting)",
1169 "requested_frame", requested_frame,
1170 "min_requested_frame", min_requested_frame,
1171 "max_requested_frame", max_requested_frame,
1173 "does_clip_intersect", does_clip_intersect);
1176 update_open_clips(
clip, does_clip_intersect);
1179 if (does_clip_intersect && include)
1181 matching_clips.push_back(
clip);
1183 else if (!does_clip_intersect && !include)
1185 matching_clips.push_back(
clip);
1190 return matching_clips;
1196 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1199 if (managed_cache && final_cache) {
1202 managed_cache =
false;
1206 final_cache = new_cache;
1221 root[
"type"] =
"Timeline";
1226 root[
"path"] = path;
1229 root[
"clips"] = Json::Value(Json::arrayValue);
1232 for (
const auto existing_clip : clips)
1234 root[
"clips"].append(existing_clip->JsonValue());
1238 root[
"effects"] = Json::Value(Json::arrayValue);
1241 for (
const auto existing_effect: effects)
1243 root[
"effects"].append(existing_effect->JsonValue());
1254 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1263 catch (
const std::exception& e)
1266 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
1274 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1277 bool was_open = is_open;
1284 if (!root[
"path"].isNull())
1285 path = root[
"path"].asString();
1287 if (!root[
"clips"].isNull()) {
1292 for (
const Json::Value existing_clip : root[
"clips"]) {
1294 if (existing_clip.isNull()) {
1302 allocated_clips.insert(c);
1319 if (!root[
"effects"].isNull()) {
1324 for (
const Json::Value existing_effect :root[
"effects"]) {
1326 if (existing_effect.isNull()) {
1333 if (!existing_effect[
"type"].isNull()) {
1335 if ( (e =
EffectInfo().CreateEffect(existing_effect[
"type"].asString())) ) {
1338 allocated_effects.insert(e);
1350 if (!root[
"duration"].isNull()) {
1376 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
1382 const uint64_t initial_cache_epoch =
CacheEpoch();
1384 for (
const Json::Value change : root) {
1385 std::string change_key = change[
"key"][(uint)0].asString();
1388 if (change_key ==
"clips")
1390 apply_json_to_clips(change);
1392 else if (change_key ==
"effects")
1394 apply_json_to_effects(change);
1398 apply_json_to_timeline(change);
1403 if (!root.empty() &&
CacheEpoch() == initial_cache_epoch) {
1407 catch (
const std::exception& e)
1410 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
1414 void Timeline::BumpCacheEpoch() {
1415 cache_epoch.fetch_add(1, std::memory_order_relaxed);
1418 void Timeline::InvalidateCacheForClip(
const Clip* clip) {
1419 if (!
clip || !final_cache) {
1424 const int64_t starting_frame =
static_cast<int64_t
>(std::llround(
clip->
Position() * fpsD)) + 1;
1425 const int64_t ending_frame =
static_cast<int64_t
>(std::llround((
clip->
Position() +
clip->Duration()) * fpsD)) + 1;
1426 final_cache->
Remove(starting_frame - 8, ending_frame + 8);
1431 void Timeline::apply_json_to_clips(Json::Value change) {
1434 std::string change_type = change[
"type"].asString();
1435 std::string clip_id =
"";
1436 Clip *existing_clip = NULL;
1439 for (
auto key_part : change[
"key"]) {
1441 if (key_part.isObject()) {
1443 if (!key_part[
"id"].isNull()) {
1445 clip_id = key_part[
"id"].asString();
1448 for (
auto c : clips)
1450 if (c->Id() == clip_id) {
1462 if (existing_clip && change[
"key"].size() == 4 && change[
"key"][2] ==
"effects")
1465 Json::Value key_part = change[
"key"][3];
1467 if (key_part.isObject()) {
1469 if (!key_part[
"id"].isNull())
1472 std::string effect_id = key_part[
"id"].asString();
1475 std::list<EffectBase*> effect_list = existing_clip->
Effects();
1476 for (
auto e : effect_list)
1478 if (e->Id() == effect_id) {
1480 apply_json_to_effects(change, e);
1489 int64_t new_ending_frame = ((existing_clip->
Position() + existing_clip->Duration()) *
info.
fps.
ToDouble()) + 1;
1490 final_cache->
Remove(new_starting_frame - 8, new_ending_frame + 8);
1500 if (change_type ==
"insert") {
1506 allocated_clips.insert(
clip);
1510 clip->ParentTimeline(
this);
1518 }
else if (change_type ==
"update") {
1521 if (existing_clip) {
1524 int64_t old_ending_frame = ((existing_clip->
Position() + existing_clip->Duration()) *
info.
fps.
ToDouble()) + 1;
1531 int64_t new_ending_frame = ((existing_clip->
Position() + existing_clip->Duration()) *
info.
fps.
ToDouble()) + 1;
1534 final_cache->
Remove(old_starting_frame - 8, old_ending_frame + 8);
1535 final_cache->
Remove(new_starting_frame - 8, new_ending_frame + 8);
1538 if (auto_map_clips) {
1539 apply_mapper_to_clip(existing_clip);
1543 }
else if (change_type ==
"delete") {
1546 if (existing_clip) {
1552 int64_t old_ending_frame = ((existing_clip->
Position() + existing_clip->Duration()) *
info.
fps.
ToDouble()) + 1;
1553 final_cache->
Remove(old_starting_frame - 8, old_ending_frame + 8);
1563 void Timeline::apply_json_to_effects(Json::Value change) {
1566 std::string change_type = change[
"type"].asString();
1570 for (
auto key_part : change[
"key"]) {
1572 if (key_part.isObject()) {
1574 if (!key_part[
"id"].isNull())
1577 std::string effect_id = key_part[
"id"].asString();
1580 for (
auto e : effects)
1582 if (e->Id() == effect_id) {
1583 existing_effect = e;
1593 if (existing_effect || change_type ==
"insert") {
1595 apply_json_to_effects(change, existing_effect);
1600 void Timeline::apply_json_to_effects(Json::Value change,
EffectBase* existing_effect) {
1603 std::string change_type = change[
"type"].asString();
1606 if (!change[
"value"].isArray() && !change[
"value"][
"position"].isNull()) {
1607 int64_t new_starting_frame = (change[
"value"][
"position"].asDouble() *
info.
fps.
ToDouble()) + 1;
1608 int64_t new_ending_frame = ((change[
"value"][
"position"].asDouble() + change[
"value"][
"end"].asDouble() - change[
"value"][
"start"].asDouble()) *
info.
fps.
ToDouble()) + 1;
1609 final_cache->
Remove(new_starting_frame - 8, new_ending_frame + 8);
1613 if (change_type ==
"insert") {
1616 std::string effect_type = change[
"value"][
"type"].asString();
1625 allocated_effects.insert(e);
1634 }
else if (change_type ==
"update") {
1637 if (existing_effect) {
1641 int64_t old_ending_frame = ((existing_effect->
Position() + existing_effect->Duration()) *
info.
fps.
ToDouble()) + 1;
1642 final_cache->
Remove(old_starting_frame - 8, old_ending_frame + 8);
1648 }
else if (change_type ==
"delete") {
1651 if (existing_effect) {
1655 int64_t old_ending_frame = ((existing_effect->
Position() + existing_effect->Duration()) *
info.
fps.
ToDouble()) + 1;
1656 final_cache->
Remove(old_starting_frame - 8, old_ending_frame + 8);
1669 void Timeline::apply_json_to_timeline(Json::Value change) {
1670 bool cache_dirty =
true;
1673 std::string change_type = change[
"type"].asString();
1674 std::string root_key = change[
"key"][(uint)0].asString();
1675 std::string sub_key =
"";
1676 if (change[
"key"].size() >= 2)
1677 sub_key = change[
"key"][(uint)1].asString();
1680 if (change_type ==
"insert" || change_type ==
"update") {
1684 if (root_key ==
"color")
1687 else if (root_key ==
"viewport_scale")
1690 else if (root_key ==
"viewport_x")
1693 else if (root_key ==
"viewport_y")
1696 else if (root_key ==
"duration") {
1702 cache_dirty =
false;
1704 else if (root_key ==
"width") {
1709 else if (root_key ==
"height") {
1714 else if (root_key ==
"fps" && sub_key ==
"" && change[
"value"].isObject()) {
1716 if (!change[
"value"][
"num"].isNull())
1717 info.
fps.
num = change[
"value"][
"num"].asInt();
1718 if (!change[
"value"][
"den"].isNull())
1719 info.
fps.
den = change[
"value"][
"den"].asInt();
1721 else if (root_key ==
"fps" && sub_key ==
"num")
1724 else if (root_key ==
"fps" && sub_key ==
"den")
1727 else if (root_key ==
"display_ratio" && sub_key ==
"" && change[
"value"].isObject()) {
1729 if (!change[
"value"][
"num"].isNull())
1731 if (!change[
"value"][
"den"].isNull())
1734 else if (root_key ==
"display_ratio" && sub_key ==
"num")
1737 else if (root_key ==
"display_ratio" && sub_key ==
"den")
1740 else if (root_key ==
"pixel_ratio" && sub_key ==
"" && change[
"value"].isObject()) {
1742 if (!change[
"value"][
"num"].isNull())
1744 if (!change[
"value"][
"den"].isNull())
1747 else if (root_key ==
"pixel_ratio" && sub_key ==
"num")
1750 else if (root_key ==
"pixel_ratio" && sub_key ==
"den")
1754 else if (root_key ==
"sample_rate")
1757 else if (root_key ==
"channels")
1760 else if (root_key ==
"channel_layout")
1765 throw InvalidJSONKey(
"JSON change key is invalid", change.toStyledString());
1768 }
else if (change[
"type"].asString() ==
"delete") {
1772 if (root_key ==
"color") {
1778 else if (root_key ==
"viewport_scale")
1780 else if (root_key ==
"viewport_x")
1782 else if (root_key ==
"viewport_y")
1786 throw InvalidJSONKey(
"JSON change key is invalid", change.toStyledString());
1799 const std::lock_guard<std::recursive_mutex> guard(
getFrameMutex);
1803 final_cache->
Clear();
1808 for (
const auto clip : clips) {
1810 if (
clip->Reader()) {
1811 if (
auto rc =
clip->Reader()->GetCache())
1815 if (deep &&
clip->Reader()->Name() ==
"FrameMapper") {
1817 if (nested_reader->
Reader()) {
1825 if (
auto cc =
clip->GetCache())
1844 display_ratio_size.scale(proposed_size, Qt::KeepAspectRatio);
1852 std::pair<float, float> Timeline::ResolveTransitionAudioGains(
Clip* source_clip, int64_t timeline_frame_number,
bool is_top_clip)
const
1854 constexpr
double half_pi = 1.57079632679489661923;
1857 return {1.0f, 1.0f};
1860 Mask* active_mask =
nullptr;
1861 int64_t effect_start_position = 0;
1862 int64_t effect_end_position = 0;
1865 for (
auto effect : effects) {
1866 if (effect->Layer() != source_clip->
Layer())
1869 auto* mask =
dynamic_cast<Mask*
>(effect);
1870 if (!mask || !mask->fade_audio_hint)
1873 const int64_t start_pos =
static_cast<int64_t
>(std::llround(effect->Position() * fpsD)) + 1;
1874 const int64_t end_pos =
static_cast<int64_t
>(std::llround((effect->Position() + effect->Duration()) * fpsD));
1875 if (start_pos > timeline_frame_number || end_pos < timeline_frame_number)
1879 return {1.0f, 1.0f};
1882 effect_start_position = start_pos;
1883 effect_end_position = end_pos;
1887 return {1.0f, 1.0f};
1889 struct AudibleClipInfo {
1895 std::vector<AudibleClipInfo> audible_clips;
1896 audible_clips.reserve(2);
1899 for (
auto clip : clips) {
1902 if (!
clip->Reader() || !
clip->Reader()->info.has_audio)
1905 const int64_t clip_start_pos =
static_cast<int64_t
>(std::llround(
clip->
Position() * fpsD)) + 1;
1906 const int64_t clip_end_pos =
static_cast<int64_t
>(std::llround((
clip->
Position() +
clip->Duration()) * fpsD));
1907 if (clip_start_pos > timeline_frame_number || clip_end_pos < timeline_frame_number)
1910 const int64_t clip_start_frame =
static_cast<int64_t
>(std::llround(
clip->
Start() * fpsD)) + 1;
1911 const int64_t clip_frame_number = timeline_frame_number - clip_start_pos + clip_start_frame;
1912 if (
clip->has_audio.GetInt(clip_frame_number) == 0)
1915 audible_clips.push_back({
clip, clip_start_pos, clip_end_pos});
1916 if (audible_clips.size() > 2)
1917 return {1.0f, 1.0f};
1920 if (audible_clips.empty())
1921 return {1.0f, 1.0f};
1924 const auto source_it = std::find_if(
1925 audible_clips.begin(),
1926 audible_clips.end(),
1927 [source_clip](
const AudibleClipInfo&
info) {
1928 return info.clip == source_clip;
1930 if (source_it == audible_clips.end())
1931 return {1.0f, 1.0f};
1934 if (audible_clips.size() == 2) {
1935 auto top_it = std::max_element(
1936 audible_clips.begin(),
1937 audible_clips.end(),
1938 [](
const AudibleClipInfo& lhs,
const AudibleClipInfo& rhs) {
1939 if (lhs.start_pos != rhs.start_pos)
1940 return lhs.start_pos < rhs.start_pos;
1941 return std::less<Clip*>()(lhs.clip, rhs.clip);
1943 if ((is_top_clip && source_clip != top_it->clip) || (!is_top_clip && source_clip == top_it->clip))
1944 return {1.0f, 1.0f};
1948 const int64_t left_distance = std::llabs(effect_start_position - source_it->start_pos);
1949 const int64_t right_distance = std::llabs(effect_end_position - source_it->end_pos);
1950 const bool clip_fades_in = left_distance <= right_distance;
1953 const auto compute_gain = [&](int64_t frame_number) ->
float {
1954 if (effect_end_position <= effect_start_position)
1957 const double span =
static_cast<double>(effect_end_position - effect_start_position);
1958 double t =
static_cast<double>(frame_number - effect_start_position) / span;
1964 return static_cast<float>(clip_fades_in ? std::sin(t * half_pi) : std::cos(t * half_pi));
1967 return {compute_gain(timeline_frame_number - 1), compute_gain(timeline_frame_number)};