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:
Jonathan Thomas
2018-02-03 01:57:18 -06:00
parent 7b13001bf7
commit f2b0f3a0f4
9 changed files with 101 additions and 13 deletions

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
};
/**

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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>;
}

View File

@@ -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>;
}