25 #ifdef USE_IMAGEMAGICK
68 wave_color =
Color((
unsigned char)0, (
unsigned char)123, (
unsigned char)255, (
unsigned char)255);
93 parentTrackedObject =
nullptr;
94 parentClipObject = NULL;
120 float rotate_angle = 0.0f;
121 if (reader && reader->
info.
metadata.count(
"rotate") > 0) {
123 rotate_angle = strtof(reader->
info.
metadata[
"rotate"].c_str(),
nullptr);
124 }
catch (
const std::exception& e) {
132 float w =
static_cast<float>(reader->
info.
width);
133 float h =
static_cast<float>(reader->
info.
height);
134 float rad = rotate_angle * M_PI / 180.0f;
137 float new_width = fabs(w * cos(rad)) + fabs(h * sin(rad));
138 float new_height = fabs(w * sin(rad)) + fabs(h * cos(rad));
142 float uniform_scale = std::min(w / new_width, h / new_height);
150 Clip::Clip() : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
157 Clip::Clip(
ReaderBase* new_reader) : resampler(NULL), reader(new_reader), allocated_reader(NULL), is_open(false)
176 Clip::Clip(std::string
path) : resampler(NULL), reader(NULL), allocated_reader(NULL), is_open(false)
182 std::string ext = get_file_extension(
path);
183 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
186 if (ext==
"avi" || ext==
"mov" || ext==
"mkv" || ext==
"mpg" || ext==
"mpeg" || ext==
"mp3" || ext==
"mp4" || ext==
"mts" ||
187 ext==
"ogg" || ext==
"wav" || ext==
"wmv" || ext==
"webm" || ext==
"vob" || ext==
"gif" ||
path.find(
"%") != std::string::npos)
229 allocated_reader = reader;
239 if (allocated_reader) {
240 delete allocated_reader;
241 allocated_reader = NULL;
261 if (parentTimeline) {
263 std::shared_ptr<openshot::TrackedObjectBase> trackedObject = parentTimeline->
GetTrackedObject(object_id);
264 Clip* clipObject = parentTimeline->
GetClip(object_id);
269 parentClipObject = NULL;
271 else if (clipObject) {
273 parentTrackedObject =
nullptr;
280 parentTrackedObject = trackedObject;
285 parentClipObject = clipObject;
293 bool is_same_reader =
false;
294 if (new_reader && allocated_reader) {
295 if (new_reader->
Name() ==
"FrameMapper") {
298 if (allocated_reader == clip_mapped_reader->
Reader()) {
299 is_same_reader =
true;
304 if (allocated_reader && !is_same_reader) {
306 allocated_reader->
Close();
307 delete allocated_reader;
309 allocated_reader = NULL;
331 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
352 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
358 if (is_open && reader) {
383 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
409 return GetFrame(NULL, clip_frame_number, NULL);
414 std::shared_ptr<Frame>
Clip::GetFrame(std::shared_ptr<openshot::Frame> background_frame, int64_t clip_frame_number)
417 return GetFrame(background_frame, clip_frame_number, NULL);
425 throw ReaderClosed(
"The Clip is closed. Call Open() before calling this method.");
430 std::shared_ptr<Frame> frame = NULL;
433 frame = final_cache.
GetFrame(clip_frame_number);
436 frame = GetOrCreateFrame(clip_frame_number);
439 int64_t timeline_frame_number = clip_frame_number;
440 QSize timeline_size(frame->GetWidth(), frame->GetHeight());
441 if (background_frame) {
443 timeline_frame_number = background_frame->number;
444 timeline_size.setWidth(background_frame->GetWidth());
445 timeline_size.setHeight(background_frame->GetHeight());
449 apply_timemapping(frame);
452 apply_waveform(frame, timeline_size);
455 apply_effects(frame, timeline_frame_number, options,
true);
458 apply_keyframes(frame, timeline_size);
461 apply_effects(frame, timeline_frame_number, options,
false);
464 final_cache.
Add(frame);
467 if (!background_frame) {
469 background_frame = std::make_shared<Frame>(frame->number, frame->GetWidth(), frame->GetHeight(),
470 "#00000000", frame->GetAudioSamplesCount(),
471 frame->GetAudioChannelsCount());
475 apply_background(frame, background_frame);
482 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
489 for (
const auto& effect : effects) {
490 if (effect->Id() ==
id) {
499 if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
503 return parentClipObject;
508 if (!parentObjectId.empty() && (!parentClipObject && !parentTrackedObject)) {
512 return parentTrackedObject;
516 std::string Clip::get_file_extension(std::string
path)
519 return path.substr(
path.find_last_of(
".") + 1);
523 void Clip::apply_timemapping(std::shared_ptr<Frame> frame)
528 throw ReaderClosed(
"No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.");
533 const std::lock_guard<std::recursive_mutex> lock(
getFrameMutex);
535 int64_t clip_frame_number = frame->number;
536 int64_t new_frame_number = adjust_frame_number_minimum(
time.
GetLong(clip_frame_number));
552 int source_sample_count = round(target_sample_count * fabs(delta));
558 location.
frame = new_frame_number;
572 init_samples.clear();
573 resampler->
SetBuffer(&init_samples, 1.0);
581 if (source_sample_count <= 0) {
583 frame->AddAudioSilence(target_sample_count);
589 source_samples->clear();
592 int remaining_samples = source_sample_count;
594 while (remaining_samples > 0) {
595 std::shared_ptr<Frame> source_frame = GetOrCreateFrame(location.
frame,
false);
596 int frame_sample_count = source_frame->GetAudioSamplesCount() - location.
sample_start;
598 if (frame_sample_count == 0) {
608 if (remaining_samples - frame_sample_count >= 0) {
610 for (
int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
611 source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.
sample_start, frame_sample_count, 1.0f);
619 remaining_samples -= frame_sample_count;
620 source_pos += frame_sample_count;
624 for (
int channel = 0; channel < source_frame->GetAudioChannelsCount(); channel++) {
625 source_samples->addFrom(channel, source_pos, source_frame->GetAudioSamples(channel) + location.
sample_start, remaining_samples, 1.0f);
628 remaining_samples = 0;
629 source_pos += remaining_samples;
636 frame->AddAudioSilence(target_sample_count);
638 if (source_sample_count != target_sample_count) {
640 double resample_ratio = double(source_sample_count) / double(target_sample_count);
641 resampler->
SetBuffer(source_samples, resample_ratio);
649 frame->AddAudio(
true, channel, 0, resampled_buffer->getReadPointer(channel, 0), std::min(resampled_buffer->getNumSamples(), target_sample_count), 1.0f);
655 frame->AddAudio(
true, channel, 0, source_samples->getReadPointer(channel, 0), target_sample_count, 1.0f);
660 delete source_samples;
668 int64_t Clip::adjust_frame_number_minimum(int64_t frame_number)
671 if (frame_number < 1)
679 std::shared_ptr<Frame> Clip::GetOrCreateFrame(int64_t number,
bool enable_time)
683 int64_t clip_frame_number = adjust_frame_number_minimum(number);
687 clip_frame_number = adjust_frame_number_minimum(
time.
GetLong(clip_frame_number));
692 "Clip::GetOrCreateFrame (from reader)",
693 "number", number,
"clip_frame_number", clip_frame_number);
696 auto reader_frame = reader->
GetFrame(clip_frame_number);
697 reader_frame->number = number;
704 auto reader_copy = std::make_shared<Frame>(*reader_frame.get());
707 reader_copy->AddColor(QColor(Qt::transparent));
711 reader_copy->AddAudioSilence(reader_copy->GetAudioSamplesCount());
727 "Clip::GetOrCreateFrame (create blank)",
729 "estimated_samples_in_frame", estimated_samples_in_frame);
732 auto new_frame = std::make_shared<Frame>(
734 "#000000", estimated_samples_in_frame, reader->
info.
channels);
737 new_frame->AddAudioSilence(estimated_samples_in_frame);
753 root[
"id"] =
add_property_json(
"ID", 0.0,
"string",
Id(), NULL, -1, -1,
true, requested_frame);
754 root[
"position"] =
add_property_json(
"Position",
Position(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
756 root[
"start"] =
add_property_json(
"Start",
Start(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
757 root[
"end"] =
add_property_json(
"End",
End(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
false, requested_frame);
758 root[
"duration"] =
add_property_json(
"Duration", Duration(),
"float",
"", NULL, 0, 30 * 60 * 60 * 48,
true, requested_frame);
763 root[
"waveform"] =
add_property_json(
"Waveform", waveform,
"int",
"", NULL, 0, 1,
false, requested_frame);
764 root[
"parentObjectId"] =
add_property_json(
"Parent", 0.0,
"string", parentObjectId, NULL, -1, -1,
false, requested_frame);
799 if (parentClipObject)
804 double timeline_frame_number = requested_frame + clip_start_position - clip_start_frame;
807 float parentObject_location_x = parentClipObject->
location_x.
GetValue(timeline_frame_number);
808 float parentObject_location_y = parentClipObject->
location_y.
GetValue(timeline_frame_number);
809 float parentObject_scale_x = parentClipObject->
scale_x.
GetValue(timeline_frame_number);
810 float parentObject_scale_y = parentClipObject->
scale_y.
GetValue(timeline_frame_number);
811 float parentObject_shear_x = parentClipObject->
shear_x.
GetValue(timeline_frame_number);
812 float parentObject_shear_y = parentClipObject->
shear_y.
GetValue(timeline_frame_number);
813 float parentObject_rotation = parentClipObject->
rotation.
GetValue(timeline_frame_number);
816 root[
"location_x"] =
add_property_json(
"Location X", parentObject_location_x,
"float",
"", &
location_x, -1.0, 1.0,
false, requested_frame);
817 root[
"location_y"] =
add_property_json(
"Location Y", parentObject_location_y,
"float",
"", &
location_y, -1.0, 1.0,
false, requested_frame);
818 root[
"scale_x"] =
add_property_json(
"Scale X", parentObject_scale_x,
"float",
"", &
scale_x, 0.0, 1.0,
false, requested_frame);
819 root[
"scale_y"] =
add_property_json(
"Scale Y", parentObject_scale_y,
"float",
"", &
scale_y, 0.0, 1.0,
false, requested_frame);
820 root[
"rotation"] =
add_property_json(
"Rotation", parentObject_rotation,
"float",
"", &
rotation, -360, 360,
false, requested_frame);
821 root[
"shear_x"] =
add_property_json(
"Shear X", parentObject_shear_x,
"float",
"", &
shear_x, -1.0, 1.0,
false, requested_frame);
822 root[
"shear_y"] =
add_property_json(
"Shear Y", parentObject_shear_y,
"float",
"", &
shear_y, -1.0, 1.0,
false, requested_frame);
862 return root.toStyledString();
870 root[
"parentObjectId"] = parentObjectId;
872 root[
"scale"] =
scale;
876 root[
"waveform"] = waveform;
904 root[
"effects"] = Json::Value(Json::arrayValue);
907 for (
auto existing_effect : effects)
909 root[
"effects"].append(existing_effect->JsonValue());
915 root[
"reader"] = Json::Value(Json::objectValue);
931 catch (
const std::exception& e)
934 throw InvalidJSON(
"JSON is invalid (missing keys or invalid data types)");
945 if (!root[
"parentObjectId"].isNull()){
946 parentObjectId = root[
"parentObjectId"].asString();
947 if (parentObjectId.size() > 0 && parentObjectId !=
""){
950 parentTrackedObject =
nullptr;
951 parentClipObject = NULL;
954 if (!root[
"gravity"].isNull())
956 if (!root[
"scale"].isNull())
958 if (!root[
"anchor"].isNull())
960 if (!root[
"display"].isNull())
962 if (!root[
"mixing"].isNull())
964 if (!root[
"waveform"].isNull())
965 waveform = root[
"waveform"].asBool();
966 if (!root[
"scale_x"].isNull())
968 if (!root[
"scale_y"].isNull())
970 if (!root[
"location_x"].isNull())
972 if (!root[
"location_y"].isNull())
974 if (!root[
"alpha"].isNull())
976 if (!root[
"rotation"].isNull())
978 if (!root[
"time"].isNull())
980 if (!root[
"volume"].isNull())
982 if (!root[
"wave_color"].isNull())
984 if (!root[
"shear_x"].isNull())
986 if (!root[
"shear_y"].isNull())
988 if (!root[
"origin_x"].isNull())
990 if (!root[
"origin_y"].isNull())
992 if (!root[
"channel_filter"].isNull())
994 if (!root[
"channel_mapping"].isNull())
996 if (!root[
"has_audio"].isNull())
998 if (!root[
"has_video"].isNull())
1000 if (!root[
"perspective_c1_x"].isNull())
1002 if (!root[
"perspective_c1_y"].isNull())
1004 if (!root[
"perspective_c2_x"].isNull())
1006 if (!root[
"perspective_c2_y"].isNull())
1008 if (!root[
"perspective_c3_x"].isNull())
1010 if (!root[
"perspective_c3_y"].isNull())
1012 if (!root[
"perspective_c4_x"].isNull())
1014 if (!root[
"perspective_c4_y"].isNull())
1016 if (!root[
"effects"].isNull()) {
1022 for (
const auto existing_effect : root[
"effects"]) {
1024 if (existing_effect.isNull()) {
1030 if (!existing_effect[
"type"].isNull()) {
1033 if ( (e =
EffectInfo().CreateEffect(existing_effect[
"type"].asString()))) {
1044 if (!root[
"reader"].isNull())
1046 if (!root[
"reader"][
"type"].isNull())
1049 bool already_open =
false;
1053 already_open = reader->
IsOpen();
1060 std::string type = root[
"reader"][
"type"].asString();
1062 if (type ==
"FFmpegReader") {
1068 }
else if (type ==
"QtImageReader") {
1074 #ifdef USE_IMAGEMAGICK
1075 }
else if (type ==
"ImageReader") {
1078 reader =
new ImageReader(root[
"reader"][
"path"].asString(),
false);
1081 }
else if (type ==
"TextReader") {
1088 }
else if (type ==
"ChunkReader") {
1094 }
else if (type ==
"DummyReader") {
1100 }
else if (type ==
"Timeline") {
1110 allocated_reader = reader;
1121 final_cache.
Clear();
1125 void Clip::sort_effects()
1138 effects.push_back(effect);
1147 effect->ParentTimeline(parentTimeline);
1154 if (parentTimeline){
1156 effect->ParentTimeline(parentTimeline);
1162 std::shared_ptr<TrackedObjectBBox> trackedObjectBBox = std::static_pointer_cast<TrackedObjectBBox>(trackedObject.second);
1165 trackedObjectBBox->ParentClip(
this);
1175 final_cache.
Clear();
1181 effects.remove(effect);
1184 final_cache.
Clear();
1188 void Clip::apply_background(std::shared_ptr<openshot::Frame> frame, std::shared_ptr<openshot::Frame> background_frame) {
1190 std::shared_ptr<QImage> background_canvas = background_frame->GetImage();
1191 QPainter painter(background_canvas.get());
1192 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing,
true);
1195 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
1196 painter.drawImage(0, 0, *frame->GetImage());
1200 frame->AddImage(background_canvas);
1204 void Clip::apply_effects(std::shared_ptr<Frame> frame, int64_t timeline_frame_number,
TimelineInfoStruct* options,
bool before_keyframes)
1206 for (
auto effect : effects)
1209 if (effect->info.apply_before_clip && before_keyframes) {
1210 effect->GetFrame(frame, frame->number);
1211 }
else if (!effect->info.apply_before_clip && !before_keyframes) {
1212 effect->GetFrame(frame, frame->number);
1216 if (
timeline != NULL && options != NULL) {
1225 bool Clip::isNear(
double a,
double b)
1227 return fabs(a - b) < 0.000001;
1231 void Clip::apply_keyframes(std::shared_ptr<Frame> frame, QSize timeline_size) {
1233 if (!frame->has_image_data) {
1239 std::shared_ptr<QImage> source_image = frame->GetImage();
1240 std::shared_ptr<QImage> background_canvas = std::make_shared<QImage>(timeline_size.width(),
1241 timeline_size.height(),
1242 QImage::Format_RGBA8888_Premultiplied);
1243 background_canvas->fill(QColor(Qt::transparent));
1246 QTransform transform = get_transform(frame, background_canvas->width(), background_canvas->height());
1249 QPainter painter(background_canvas.get());
1250 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing,
true);
1253 painter.setTransform(transform);
1256 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
1257 painter.drawImage(0, 0, *source_image);
1264 std::stringstream frame_number_str;
1271 frame_number_str << frame->number;
1284 painter.setPen(QColor(
"#ffffff"));
1285 painter.drawText(20, 20, QString(frame_number_str.str().c_str()));
1291 frame->AddImage(background_canvas);
1295 void Clip::apply_waveform(std::shared_ptr<Frame> frame, QSize timeline_size) {
1303 std::shared_ptr<QImage> source_image = frame->GetImage();
1307 "frame->number", frame->number,
1308 "Waveform()", Waveform(),
1309 "width", timeline_size.width(),
1310 "height", timeline_size.height());
1319 source_image = frame->GetWaveform(timeline_size.width(), timeline_size.height(), red, green, blue,
alpha);
1320 frame->AddImage(source_image);
1324 QSize Clip::scale_size(QSize source_size,
ScaleType source_scale,
int target_width,
int target_height) {
1325 switch (source_scale)
1328 source_size.scale(target_width, target_height, Qt::KeepAspectRatio);
1332 source_size.scale(target_width, target_height, Qt::IgnoreAspectRatio);
1336 source_size.scale(target_width, target_height, Qt::KeepAspectRatioByExpanding);;
1345 QTransform Clip::get_transform(std::shared_ptr<Frame> frame,
int width,
int height)
1348 std::shared_ptr<QImage> source_image = frame->GetImage();
1356 unsigned char *pixels = source_image->bits();
1359 for (
int pixel = 0, byte_index=0; pixel < source_image->width() * source_image->height(); pixel++, byte_index+=4)
1363 pixels[byte_index + 0] *= alpha_value;
1364 pixels[byte_index + 1] *= alpha_value;
1365 pixels[byte_index + 2] *= alpha_value;
1366 pixels[byte_index + 3] *= alpha_value;
1371 "alpha_value", alpha_value,
1372 "frame->number", frame->number);
1376 QSize source_size = scale_size(source_image->size(),
scale, width, height);
1379 float parentObject_location_x = 0.0;
1380 float parentObject_location_y = 0.0;
1381 float parentObject_scale_x = 1.0;
1382 float parentObject_scale_y = 1.0;
1383 float parentObject_shear_x = 0.0;
1384 float parentObject_shear_y = 0.0;
1385 float parentObject_rotation = 0.0;
1391 long parent_frame_number = frame->number + parent_start_offset;
1394 parentObject_location_x = parentClipObject->
location_x.
GetValue(parent_frame_number);
1395 parentObject_location_y = parentClipObject->
location_y.
GetValue(parent_frame_number);
1396 parentObject_scale_x = parentClipObject->
scale_x.
GetValue(parent_frame_number);
1397 parentObject_scale_y = parentClipObject->
scale_y.
GetValue(parent_frame_number);
1398 parentObject_shear_x = parentClipObject->
shear_x.
GetValue(parent_frame_number);
1399 parentObject_shear_y = parentClipObject->
shear_y.
GetValue(parent_frame_number);
1400 parentObject_rotation = parentClipObject->
rotation.
GetValue(parent_frame_number);
1406 Clip* parentClip = (
Clip*) parentTrackedObject->ParentClip();
1411 long parent_frame_number = frame->number + parent_start_offset;
1414 std::map<std::string, float> trackedObjectProperties = parentTrackedObject->GetBoxValues(parent_frame_number);
1418 parentClip->
scale, width, height);
1421 int trackedWidth = trackedObjectProperties[
"w"] * trackedObjectProperties[
"sx"] * parent_size.width() *
1423 int trackedHeight = trackedObjectProperties[
"h"] * trackedObjectProperties[
"sy"] * parent_size.height() *
1427 source_size = scale_size(source_size,
scale, trackedWidth, trackedHeight);
1430 parentObject_location_x = parentClip->
location_x.
GetValue(parent_frame_number) + ((trackedObjectProperties[
"cx"] - 0.5) * parentClip->
scale_x.
GetValue(parent_frame_number));
1431 parentObject_location_y = parentClip->
location_y.
GetValue(parent_frame_number) + ((trackedObjectProperties[
"cy"] - 0.5) * parentClip->
scale_y.
GetValue(parent_frame_number));
1432 parentObject_rotation = trackedObjectProperties[
"r"] + parentClip->
rotation.
GetValue(parent_frame_number);
1445 if(parentObject_scale_x != 0.0 && parentObject_scale_y != 0.0){
1446 sx*= parentObject_scale_x;
1447 sy*= parentObject_scale_y;
1450 float scaled_source_width = source_size.width() * sx;
1451 float scaled_source_height = source_size.height() * sy;
1459 x = (width - scaled_source_width) / 2.0;
1462 x = width - scaled_source_width;
1465 y = (height - scaled_source_height) / 2.0;
1468 x = (width - scaled_source_width) / 2.0;
1469 y = (height - scaled_source_height) / 2.0;
1472 x = width - scaled_source_width;
1473 y = (height - scaled_source_height) / 2.0;
1476 y = (height - scaled_source_height);
1479 x = (width - scaled_source_width) / 2.0;
1480 y = (height - scaled_source_height);
1483 x = width - scaled_source_width;
1484 y = (height - scaled_source_height);
1490 "Clip::get_transform (Gravity)",
1491 "frame->number", frame->number,
1492 "source_clip->gravity",
gravity,
1493 "scaled_source_width", scaled_source_width,
1494 "scaled_source_height", scaled_source_height);
1496 QTransform transform;
1502 float shear_x_value =
shear_x.
GetValue(frame->number) + parentObject_shear_x;
1503 float shear_y_value =
shear_y.
GetValue(frame->number) + parentObject_shear_y;
1509 "Clip::get_transform (Build QTransform - if needed)",
1510 "frame->number", frame->number,
1513 "sx", sx,
"sy", sy);
1515 if (!isNear(x, 0) || !isNear(y, 0)) {
1517 transform.translate(x, y);
1519 if (!isNear(r, 0) || !isNear(shear_x_value, 0) || !isNear(shear_y_value, 0)) {
1521 float origin_x_offset = (scaled_source_width * origin_x_value);
1522 float origin_y_offset = (scaled_source_height * origin_y_value);
1523 transform.translate(origin_x_offset, origin_y_offset);
1524 transform.rotate(r);
1525 transform.shear(shear_x_value, shear_y_value);
1526 transform.translate(-origin_x_offset,-origin_y_offset);
1529 float source_width_scale = (float(source_size.width()) / float(source_image->width())) * sx;
1530 float source_height_scale = (float(source_size.height()) / float(source_image->height())) * sy;
1531 if (!isNear(source_width_scale, 1.0) || !isNear(source_height_scale, 1.0)) {
1532 transform.scale(source_width_scale, source_height_scale);
1539 int64_t Clip::adjust_timeline_framenumber(int64_t clip_frame_number) {
1556 int64_t frame_number = clip_frame_number + clip_start_position - clip_start_frame;
1558 return frame_number;