You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Adding metadata from format, audio stream, and video streams to ReaderBase.info, which in some cases includes the 'rotate' metadata added by certain cameras, and audio metadata like title, album, artist, copyright, dates, etc... Auto-Rotates any Clip with Reader metadata 'rotate' attribute.
This commit is contained in:
@@ -141,6 +141,9 @@ namespace openshot {
|
||||
/// Init default settings for a clip
|
||||
void init_settings();
|
||||
|
||||
/// Update default rotation from reader
|
||||
void init_reader_rotation();
|
||||
|
||||
/// Sort effects by order
|
||||
void sort_effects();
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ namespace openshot
|
||||
/// Thumbnail the frame image with tons of options to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG).
|
||||
/// This method allows for masks, overlays, background color, and much more accurate resizing (including padding and centering)
|
||||
void Thumbnail(string path, int new_width, int new_height, string mask_path, string overlay_path,
|
||||
string background_color, bool ignore_aspect, string format="png", int quality=100);
|
||||
string background_color, bool ignore_aspect, string format="png", int quality=100, float rotate=0.0);
|
||||
|
||||
/// Play audio samples for this frame
|
||||
void Play();
|
||||
|
||||
@@ -83,6 +83,7 @@ namespace openshot
|
||||
ChannelLayout channel_layout; ///< The channel layout (mono, stereo, 5 point surround, etc...)
|
||||
int audio_stream_index; ///< The index of the audio stream
|
||||
Fraction audio_timebase; ///< The audio timebase determines how long each audio packet should be played
|
||||
std::map<string, string> metadata; ///< An optional map/dictionary of metadata for this reader
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
44
src/Clip.cpp
44
src/Clip.cpp
@@ -52,9 +52,11 @@ void Clip::init_settings()
|
||||
location_x = Keyframe(0.0);
|
||||
location_y = Keyframe(0.0);
|
||||
|
||||
// Init alpha & rotation
|
||||
// Init alpha
|
||||
alpha = Keyframe(1.0);
|
||||
rotation = Keyframe(0.0);
|
||||
|
||||
// Init rotation
|
||||
init_reader_rotation();
|
||||
|
||||
// Init time & volume
|
||||
time = Keyframe(1.0);
|
||||
@@ -97,8 +99,32 @@ void Clip::init_settings()
|
||||
manage_reader = false;
|
||||
}
|
||||
|
||||
// Init reader's rotation (if any)
|
||||
void Clip::init_reader_rotation() {
|
||||
// Only init rotation from reader when needed
|
||||
if (rotation.Points.size() > 1)
|
||||
// Do nothing if more than 1 rotation Point
|
||||
return;
|
||||
else if (rotation.Points.size() == 1 && rotation.GetValue(1) != 0.0)
|
||||
// Do nothing if 1 Point, and it's not the default value
|
||||
return;
|
||||
|
||||
// Init rotation
|
||||
if (reader && reader->info.metadata.count("rotate") > 0) {
|
||||
// Use reader metadata rotation (if any)
|
||||
// This is typical with cell phone videos filmed in different orientations
|
||||
try {
|
||||
float rotate_metadata = strtof(reader->info.metadata["rotate"].c_str(), 0);
|
||||
rotation = Keyframe(rotate_metadata);
|
||||
} catch (exception e) {}
|
||||
}
|
||||
else
|
||||
// Default no rotation
|
||||
rotation = Keyframe(0.0);
|
||||
}
|
||||
|
||||
// Default Constructor for a clip
|
||||
Clip::Clip()
|
||||
Clip::Clip() : reader(NULL)
|
||||
{
|
||||
// Init all default settings
|
||||
init_settings();
|
||||
@@ -107,12 +133,12 @@ Clip::Clip()
|
||||
// Constructor with reader
|
||||
Clip::Clip(ReaderBase* new_reader)
|
||||
{
|
||||
// Init all default settings
|
||||
init_settings();
|
||||
|
||||
// Set the reader
|
||||
reader = new_reader;
|
||||
|
||||
// Init all default settings
|
||||
init_settings();
|
||||
|
||||
// Open and Close the reader (to set the duration of the clip)
|
||||
Open();
|
||||
Close();
|
||||
@@ -122,7 +148,7 @@ Clip::Clip(ReaderBase* new_reader)
|
||||
}
|
||||
|
||||
// Constructor with filepath
|
||||
Clip::Clip(string path)
|
||||
Clip::Clip(string path) : reader(NULL)
|
||||
{
|
||||
// Init all default settings
|
||||
init_settings();
|
||||
@@ -165,6 +191,7 @@ Clip::Clip(string path)
|
||||
if (reader) {
|
||||
End(reader->info.duration);
|
||||
manage_reader = true;
|
||||
init_reader_rotation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +216,9 @@ void Clip::Reader(ReaderBase* new_reader)
|
||||
{
|
||||
// set reader pointer
|
||||
reader = new_reader;
|
||||
|
||||
// Init rotation (if any)
|
||||
init_reader_rotation();
|
||||
}
|
||||
|
||||
/// Get the current reader
|
||||
|
||||
@@ -186,6 +186,14 @@ void FFmpegReader::Open()
|
||||
UpdateAudioInfo();
|
||||
}
|
||||
|
||||
// Add format metadata (if any)
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
while ((tag = av_dict_get(pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
QString str_key = tag->key;
|
||||
QString str_value = tag->value;
|
||||
info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
|
||||
}
|
||||
|
||||
// Init previous audio location to zero
|
||||
previous_packet_location.frame = -1;
|
||||
previous_packet_location.sample_start = 0;
|
||||
@@ -294,9 +302,15 @@ void FFmpegReader::UpdateAudioInfo()
|
||||
info.video_length = info.duration * info.fps.ToDouble();
|
||||
info.width = 720;
|
||||
info.height = 480;
|
||||
|
||||
}
|
||||
|
||||
// Add audio metadata (if any found)
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
while ((tag = av_dict_get(aStream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
QString str_key = tag->key;
|
||||
QString str_value = tag->value;
|
||||
info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
void FFmpegReader::UpdateVideoInfo()
|
||||
@@ -390,6 +404,13 @@ void FFmpegReader::UpdateVideoInfo()
|
||||
info.video_length = round(info.duration * info.fps.ToDouble());
|
||||
}
|
||||
|
||||
// Add video metadata (if any)
|
||||
AVDictionaryEntry *tag = NULL;
|
||||
while ((tag = av_dict_get(pStream->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
QString str_key = tag->key;
|
||||
QString str_value = tag->value;
|
||||
info.metadata[str_key.toStdString()] = str_value.trimmed().toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -578,18 +578,16 @@ void Frame::Save(string path, float scale, string format, int quality)
|
||||
|
||||
// Thumbnail the frame image to the specified path. The image format is determined from the extension (i.e. image.PNG, image.JPEG)
|
||||
void Frame::Thumbnail(string path, int new_width, int new_height, string mask_path, string overlay_path,
|
||||
string background_color, bool ignore_aspect, string format, int quality) {
|
||||
string background_color, bool ignore_aspect, string format, int quality, float rotate) {
|
||||
|
||||
// Create blank thumbnail image & fill background color
|
||||
std::shared_ptr<QImage> thumbnail = std::shared_ptr<QImage>(new QImage(new_width, new_height, QImage::Format_RGBA8888));
|
||||
thumbnail->fill(QColor(QString::fromStdString(background_color)));
|
||||
|
||||
// Create transform and painter
|
||||
QTransform transform;
|
||||
// Create painter
|
||||
QPainter painter(thumbnail.get());
|
||||
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true);
|
||||
|
||||
|
||||
// Get preview image
|
||||
std::shared_ptr<QImage> previewImage = GetImage();
|
||||
|
||||
@@ -616,6 +614,18 @@ void Frame::Thumbnail(string path, int new_width, int new_height, string mask_pa
|
||||
int x = (new_width - previewImage->size().width()) / 2.0; // center
|
||||
int y = (new_height - previewImage->size().height()) / 2.0; // center
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
|
||||
|
||||
// Create transform and rotate (if needed)
|
||||
QTransform transform;
|
||||
float origin_x = previewImage->width() / 2.0;
|
||||
float origin_y = previewImage->height() / 2.0;
|
||||
transform.translate(origin_x, origin_y);
|
||||
transform.rotate(rotate);
|
||||
transform.translate(-origin_x,-origin_y);
|
||||
painter.setTransform(transform);
|
||||
|
||||
// Draw image onto QImage
|
||||
painter.drawImage(x, y, *previewImage);
|
||||
|
||||
|
||||
|
||||
@@ -100,6 +100,13 @@ void ReaderBase::DisplayInfo() {
|
||||
cout << "--> Audio Stream Index: " << info.audio_stream_index << endl;
|
||||
cout << "--> Audio Timebase: " << info.audio_timebase.ToDouble() << " (" << info.audio_timebase.num << "/" << info.audio_timebase.den << ")" << endl;
|
||||
cout << "----------------------------" << endl;
|
||||
cout << "--------- Metadata ---------" << endl;
|
||||
cout << "----------------------------" << endl;
|
||||
|
||||
// Iterate through metadata
|
||||
map<string, string>::iterator it;
|
||||
for (it = info.metadata.begin(); it != info.metadata.end(); it++)
|
||||
cout << "--> " << it->first << ": " << it->second << endl;
|
||||
}
|
||||
|
||||
// Generate Json::JsonValue for this object
|
||||
@@ -147,6 +154,12 @@ Json::Value ReaderBase::JsonValue() {
|
||||
root["audio_timebase"]["num"] = info.audio_timebase.num;
|
||||
root["audio_timebase"]["den"] = info.audio_timebase.den;
|
||||
|
||||
// Append metadata map
|
||||
root["metadata"] = Json::Value(Json::objectValue);
|
||||
map<string, string>::iterator it;
|
||||
for (it = info.metadata.begin(); it != info.metadata.end(); it++)
|
||||
root["metadata"][it->first] = it->second;
|
||||
|
||||
// return JsonValue
|
||||
return root;
|
||||
}
|
||||
@@ -226,4 +239,10 @@ void ReaderBase::SetJsonValue(Json::Value root) {
|
||||
if (!root["audio_timebase"]["den"].isNull())
|
||||
info.audio_timebase.den = root["audio_timebase"]["den"].asInt();
|
||||
}
|
||||
if (!root["metadata"].isNull() && root["metadata"].isObject()) {
|
||||
for( Json::Value::iterator itr = root["metadata"].begin() ; itr != root["metadata"].end() ; itr++ ) {
|
||||
string key = itr.key().asString();
|
||||
info.metadata[key] = root["metadata"][key].asString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
%include "std_string.i"
|
||||
%include "std_list.i"
|
||||
%include "std_vector.i"
|
||||
%include "std_map.i"
|
||||
%include <stdint.i>
|
||||
|
||||
/* Unhandled STL Exception Handling */
|
||||
@@ -176,4 +177,5 @@ namespace std {
|
||||
%template(PointsVector) vector<Point>;
|
||||
%template(FieldVector) vector<Field>;
|
||||
%template(MappedFrameVector) vector<MappedFrame>;
|
||||
%template(MappedMetadata) map<string, string>;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
%include "std_string.i"
|
||||
%include "std_list.i"
|
||||
%include "std_vector.i"
|
||||
%include "std_map.i"
|
||||
|
||||
/* Unhandled STL Exception Handling */
|
||||
%include <std_except.i>
|
||||
@@ -169,4 +170,5 @@ namespace std {
|
||||
%template(PointsVector) vector<Point>;
|
||||
%template(FieldVector) vector<Field>;
|
||||
%template(MappedFrameVector) vector<MappedFrame>;
|
||||
%template(MappedMetadata) map<string, string>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user