Merge branch 'develop' into fix-ffmpeg3_2-empty-frames

This commit is contained in:
Jonathan Thomas
2018-08-08 02:11:34 -05:00
committed by GitHub
3 changed files with 87 additions and 104 deletions

View File

@@ -65,6 +65,7 @@ namespace openshot
private:
ReaderBase *reader;
std::shared_ptr<QImage> original_mask;
bool needs_refresh;
/// Init effect settings
void init_effect_details();

View File

@@ -339,19 +339,21 @@ void FFmpegReader::UpdateAudioInfo()
void FFmpegReader::UpdateVideoInfo()
{
if (check_fps)
// Already initialized all the video metadata, no reason to do it again
return;
// Set values of FileInfo struct
info.has_video = true;
info.file_size = pFormatCtx->pb ? avio_size(pFormatCtx->pb) : -1;
info.height = AV_GET_CODEC_ATTRIBUTES(pStream, pCodecCtx)->height;
info.width = AV_GET_CODEC_ATTRIBUTES(pStream, pCodecCtx)->width;
info.vcodec = pCodecCtx->codec->name;
info.video_bit_rate = pFormatCtx->bit_rate;
if (!check_fps)
{
// set frames per second (fps)
info.fps.num = pStream->avg_frame_rate.num;
info.fps.den = pStream->avg_frame_rate.den;
}
info.video_bit_rate = (pFormatCtx->bit_rate / 8);
// set frames per second (fps)
info.fps.num = pStream->avg_frame_rate.num;
info.fps.den = pStream->avg_frame_rate.den;
if (pStream->sample_aspect_ratio.num != 0)
{
@@ -415,16 +417,10 @@ void FFmpegReader::UpdateVideoInfo()
}
// Override an invalid framerate
if (info.fps.ToFloat() > 240.0f || (info.fps.num == 0 || info.fps.den == 0))
{
// Set a few important default video settings (so audio can be divided into frames)
info.fps.num = 24;
info.fps.den = 1;
info.video_timebase.num = 1;
info.video_timebase.den = 24;
// Calculate number of frames
info.video_length = round(info.duration * info.fps.ToDouble());
if (info.fps.ToFloat() > 240.0f || (info.fps.num == 0 || info.fps.den == 0)) {
// Calculate FPS, duration, video bit rate, and video length manually
// by scanning through all the video stream packets
CheckFPS();
}
// Add video metadata (if any)
@@ -1857,16 +1853,12 @@ void FFmpegReader::CheckWorkingFrames(bool end_of_stream, int64_t requested_fram
void FFmpegReader::CheckFPS()
{
check_fps = true;
AV_ALLOCATE_IMAGE(pFrame, AV_GET_CODEC_PIXEL_FORMAT(pStream, pCodecCtx), info.width, info.height);
int first_second_counter = 0;
int second_second_counter = 0;
int third_second_counter = 0;
int forth_second_counter = 0;
int fifth_second_counter = 0;
int iterations = 0;
int threshold = 500;
int frames_detected = 0;
// Loop through the stream
while (true)
@@ -1908,63 +1900,41 @@ void FFmpegReader::CheckFPS()
forth_second_counter++;
else if (video_seconds > 4.0 && video_seconds <= 5.0)
fifth_second_counter++;
else
// Too far
break;
// Increment counters
frames_detected++;
}
}
// Increment counters
iterations++;
// Give up (if threshold exceeded)
if (iterations > threshold)
break;
}
// Double check that all counters have greater than zero (or give up)
if (second_second_counter == 0 || third_second_counter == 0 || forth_second_counter == 0 || fifth_second_counter == 0)
{
// Seek to frame 1
Seek(1);
if (second_second_counter != 0 && third_second_counter != 0 && forth_second_counter != 0 && fifth_second_counter != 0) {
// Calculate average FPS
int sum_fps = second_second_counter + third_second_counter + forth_second_counter + fifth_second_counter;
int avg_fps = round(sum_fps / 4.0f);
// exit with no changes to FPS (not enough data to calculate)
return;
// Update FPS
info.fps = Fraction(avg_fps, 1);
// Update Duration and Length
info.video_length = frames_detected;
info.duration = frames_detected / round(sum_fps / 4.0f);
// Update video bit rate
info.video_bit_rate = info.file_size / info.duration;
} else {
// Too short to determine framerate, just default FPS
// Set a few important default video settings (so audio can be divided into frames)
info.fps.num = 30;
info.fps.den = 1;
info.video_timebase.num = info.fps.den;
info.video_timebase.den = info.fps.num;
// Calculate number of frames
info.video_length = frames_detected;
info.duration = frames_detected / info.video_timebase.ToFloat();
}
int sum_fps = second_second_counter + third_second_counter + forth_second_counter + fifth_second_counter;
int avg_fps = round(sum_fps / 4.0f);
// Sometimes the FPS is incorrectly detected by FFmpeg. If the 1st and 2nd seconds counters
// agree with each other, we are going to adjust the FPS of this reader instance. Otherwise, print
// a warning message.
// Get diff from actual frame rate
double fps = info.fps.ToDouble();
double diff = fps - double(avg_fps);
// Is difference bigger than 1 frame?
if (diff <= -1 || diff >= 1)
{
// Compare to half the frame rate (the most common type of issue)
double half_fps = Fraction(info.fps.num / 2, info.fps.den).ToDouble();
diff = half_fps - double(avg_fps);
// Is difference bigger than 1 frame?
if (diff <= -1 || diff >= 1)
{
// Update FPS for this reader instance
info.fps = Fraction(avg_fps, 1);
}
else
{
// Update FPS for this reader instance (to 1/2 the original framerate)
info.fps = Fraction(info.fps.num / 2, info.fps.den);
}
}
// Seek to frame 1
Seek(1);
}
// Remove AVFrame from cache (and deallocate it's memory)

View File

@@ -30,14 +30,14 @@
using namespace openshot;
/// Blank constructor, useful when using Json to load the effect properties
Mask::Mask() : reader(NULL), replace_image(false) {
Mask::Mask() : reader(NULL), replace_image(false), needs_refresh(true) {
// Init effect properties
init_effect_details();
}
// Default constructor
Mask::Mask(ReaderBase *mask_reader, Keyframe mask_brightness, Keyframe mask_contrast) :
reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false)
reader(mask_reader), brightness(mask_brightness), contrast(mask_contrast), replace_image(false), needs_refresh(true)
{
// Init effect properties
init_effect_details();
@@ -77,7 +77,7 @@ std::shared_ptr<Frame> Mask::GetFrame(std::shared_ptr<Frame> frame, int64_t fram
// Get mask image (if missing or different size than frame image)
#pragma omp critical (open_mask_reader)
{
if (!original_mask || !reader->info.has_single_image ||
if (!original_mask || !reader->info.has_single_image || needs_refresh ||
(original_mask && original_mask->size() != frame_image->size())) {
// Only get mask if needed
@@ -91,6 +91,9 @@ std::shared_ptr<Frame> Mask::GetFrame(std::shared_ptr<Frame> frame, int64_t fram
}
}
// Refresh no longer needed
needs_refresh = false;
// Get pixel arrays
unsigned char *pixels = (unsigned char *) frame_image->bits();
unsigned char *mask_pixels = (unsigned char *) original_mask->bits();
@@ -206,47 +209,51 @@ void Mask::SetJsonValue(Json::Value root) {
contrast.SetJsonValue(root["contrast"]);
if (!root["reader"].isNull()) // does Json contain a reader?
{
if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
#pragma omp critical (open_mask_reader)
{
// Close previous reader (if any)
if (reader)
// This reader has changed, so refresh cached assets
needs_refresh = true;
if (!root["reader"]["type"].isNull()) // does the reader Json contain a 'type'?
{
// Close and delete existing reader (if any)
reader->Close();
delete reader;
reader = NULL;
}
// Close previous reader (if any)
if (reader) {
// Close and delete existing reader (if any)
reader->Close();
delete reader;
reader = NULL;
}
// Create new reader (and load properties)
string type = root["reader"]["type"].asString();
// Create new reader (and load properties)
string type = root["reader"]["type"].asString();
if (type == "FFmpegReader") {
if (type == "FFmpegReader") {
// Create new reader
reader = new FFmpegReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
// Create new reader
reader = new FFmpegReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
#ifdef USE_IMAGEMAGICK
} else if (type == "ImageReader") {
#ifdef USE_IMAGEMAGICK
} else if (type == "ImageReader") {
// Create new reader
reader = new ImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
#endif
// Create new reader
reader = new ImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
#endif
} else if (type == "QtImageReader") {
} else if (type == "QtImageReader") {
// Create new reader
reader = new QtImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
// Create new reader
reader = new QtImageReader(root["reader"]["path"].asString());
reader->SetJsonValue(root["reader"]);
} else if (type == "ChunkReader") {
} else if (type == "ChunkReader") {
// Create new reader
reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
reader->SetJsonValue(root["reader"]);
// Create new reader
reader = new ChunkReader(root["reader"]["path"].asString(), (ChunkVersion) root["reader"]["chunk_version"].asInt());
reader->SetJsonValue(root["reader"]);
}
}
}
@@ -275,6 +282,11 @@ string Mask::PropertiesJSON(int64_t requested_frame) {
root["brightness"] = add_property_json("Brightness", brightness.GetValue(requested_frame), "float", "", &brightness, -1.0, 1.0, false, requested_frame);
root["contrast"] = add_property_json("Contrast", contrast.GetValue(requested_frame), "float", "", &contrast, 0, 20, false, requested_frame);
if (reader)
root["reader"] = add_property_json("Source", 0.0, "reader", reader->Json(), NULL, 0, 1, false, requested_frame);
else
root["reader"] = add_property_json("Source", 0.0, "reader", "{}", NULL, 0, 1, false, requested_frame);
// Return formatted string
return root.toStyledString();
}