diff --git a/cmake/Modules/FindFFmpeg.cmake b/cmake/Modules/FindFFmpeg.cmake
index 495adb2f..452454b3 100644
--- a/cmake/Modules/FindFFmpeg.cmake
+++ b/cmake/Modules/FindFFmpeg.cmake
@@ -9,65 +9,66 @@
# FindAvformat
FIND_PATH( AVFORMAT_INCLUDE_DIR libavformat/avformat.h
- PATHS /usr/include/
- /usr/include/ffmpeg/
- $ENV{FFMPEGDIR}/include/
- $ENV{FFMPEGDIR}/include/ffmpeg/ )
+ PATHS /usr/include/
+ /usr/include/ffmpeg/
+ $ENV{FFMPEGDIR}/include/
+ $ENV{FFMPEGDIR}/include/ffmpeg/ )
FIND_LIBRARY( AVFORMAT_LIBRARY avformat
- PATHS /usr/lib/
- /usr/lib/ffmpeg/
- $ENV{FFMPEGDIR}/lib/
- $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+ PATHS /usr/lib/
+ /usr/lib/ffmpeg/
+ $ENV{FFMPEGDIR}/lib/
+ $ENV{FFMPEGDIR}/lib/ffmpeg/ )
#FindAvcodec
FIND_PATH( AVCODEC_INCLUDE_DIR libavcodec/avcodec.h
PATHS /usr/include/
- /usr/include/ffmpeg/
- $ENV{FFMPEGDIR}/include/
- $ENV{FFMPEGDIR}/include/ffmpeg/ )
+ /usr/include/ffmpeg/
+ $ENV{FFMPEGDIR}/include/
+ $ENV{FFMPEGDIR}/include/ffmpeg/ )
FIND_LIBRARY( AVCODEC_LIBRARY avcodec
- PATHS /usr/lib/
- /usr/lib/ffmpeg/
- $ENV{FFMPEGDIR}/lib/
- $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+ PATHS /usr/lib/
+ /usr/lib/ffmpeg/
+ $ENV{FFMPEGDIR}/lib/
+ $ENV{FFMPEGDIR}/lib/ffmpeg/ )
#FindAvutil
FIND_PATH( AVUTIL_INCLUDE_DIR libavutil/avutil.h
PATHS /usr/include/
- /usr/include/ffmpeg/
- $ENV{FFMPEGDIR}/include/
- $ENV{FFMPEGDIR}/include/ffmpeg/ )
+ /usr/include/ffmpeg/
+ $ENV{FFMPEGDIR}/include/
+ $ENV{FFMPEGDIR}/include/ffmpeg/ )
FIND_LIBRARY( AVUTIL_LIBRARY avutil
- PATHS /usr/lib/
- /usr/lib/ffmpeg/
- $ENV{FFMPEGDIR}/lib/
- $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+ PATHS /usr/lib/
+ /usr/lib/ffmpeg/
+ $ENV{FFMPEGDIR}/lib/
+ $ENV{FFMPEGDIR}/lib/ffmpeg/ )
#FindAvdevice
FIND_PATH( AVDEVICE_INCLUDE_DIR libavdevice/avdevice.h
PATHS /usr/include/
- /usr/include/ffmpeg/
- $ENV{FFMPEGDIR}/include/
- $ENV{FFMPEGDIR}/include/ffmpeg/ )
+ /usr/include/ffmpeg/
+ $ENV{FFMPEGDIR}/include/
+ $ENV{FFMPEGDIR}/include/ffmpeg/ )
FIND_LIBRARY( AVDEVICE_LIBRARY avdevice
- PATHS /usr/lib/
- /usr/lib/ffmpeg/
- $ENV{FFMPEGDIR}/lib/
- $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+ PATHS /usr/lib/
+ /usr/lib/ffmpeg/
+ $ENV{FFMPEGDIR}/lib/
+ $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+
#FindSwscale
FIND_PATH( SWSCALE_INCLUDE_DIR libswscale/swscale.h
PATHS /usr/include/
- /usr/include/ffmpeg/
- $ENV{FFMPEGDIR}/include/
- $ENV{FFMPEGDIR}/include/ffmpeg/ )
+ /usr/include/ffmpeg/
+ $ENV{FFMPEGDIR}/include/
+ $ENV{FFMPEGDIR}/include/ffmpeg/ )
FIND_LIBRARY( SWSCALE_LIBRARY swscale
- PATHS /usr/lib/
- /usr/lib/ffmpeg/
- $ENV{FFMPEGDIR}/lib/
- $ENV{FFMPEGDIR}/lib/ffmpeg/ )
+ PATHS /usr/lib/
+ /usr/lib/ffmpeg/
+ $ENV{FFMPEGDIR}/lib/
+ $ENV{FFMPEGDIR}/lib/ffmpeg/ )
SET( FFMPEG_FOUND FALSE )
@@ -121,4 +122,4 @@ include(FindPackageHandleStandardArgs)
# handle the QUIETLY and REQUIRED arguments and set FFMPEG_FOUND to TRUE
# if all listed variables are TRUE
find_package_handle_standard_args(FFMPEG DEFAULT_MSG
- FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIR)
\ No newline at end of file
+ FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIR)
diff --git a/doc/known_bugs.txt b/doc/known_bugs.txt
new file mode 100644
index 00000000..e50ea68a
--- /dev/null
+++ b/doc/known_bugs.txt
@@ -0,0 +1,12 @@
+These are the known bugs in the OpenShot Library
+-------------------------------------------------
+1) (CRITICAL) FFmpegReader - Issues with audio continuity and GetAudioPTSLocation(). For some reason, there are skipped
+ audio samples, gaps in PTS timecodes, and generally "Poppy" and "Crackly" audio in WebM files. WebM files
+ seem to have more audio issues than other codecs, for some reason.
+2) (CRITICAL) FFmpegReader - Issues with OpenMP and the nested ImageMagick++ OpenMP implementation. On my 8 core system,
+ If I don't call omp_set_num_threads(4... or less), an ImageMagick++ error is raised. I have posted this
+ issue on the ImageMagick forum: http://www.imagemagick.org/discourse-server/viewtopic.php?f=23&t=24036
+3) (CRITICAL) FFmpegReader - OpenMP issue if more tasks are created than the number of processors. For example, on
+ ReadStream(), if minimum_packets = 8 and omp_set_num_threads(4), than potentially more than 4 tasks will
+ be running at the same time, and some tasks just seem to disappear (or never actually start).
+4)
\ No newline at end of file
diff --git a/include/ChunkReader.h b/include/ChunkReader.h
index d60810c9..915f1365 100644
--- a/include/ChunkReader.h
+++ b/include/ChunkReader.h
@@ -58,6 +58,7 @@ namespace openshot
FFmpegReader *local_reader;
chunk_location previous_location;
ChunkVersion version;
+ tr1::shared_ptr last_frame;
/// Check if folder path existing
bool does_folder_exist(string path);
diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h
index b6d17491..66699378 100644
--- a/include/FFmpegReader.h
+++ b/include/FFmpegReader.h
@@ -92,6 +92,7 @@ namespace openshot
AVPacket *packet;
AVPicture *pFrame;
bool is_open;
+ bool is_duration_known;
bool check_interlace;
bool check_fps;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5480e036..75602dc4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -29,7 +29,8 @@ include_directories(${SDL_INCLUDE_DIR})
################# QT4 ###################
# Find QT4 libraries
-FIND_PACKAGE(Qt4 REQUIRED)
+SET(QT_QMAKE_EXECUTABLE '/usr/bin/qmake-qt4') # DEBUB, WORK-AROUND: Force the use of QT4 (when multiple versions are installed)
+FIND_PACKAGE(Qt4)
# Include Qt headers (needed for compile)
include(${QT_USE_FILE})
diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp
index 272eae61..aa7dca35 100644
--- a/src/ChunkReader.cpp
+++ b/src/ChunkReader.cpp
@@ -214,8 +214,14 @@ tr1::shared_ptr ChunkReader::GetFrame(int requested_frame) throw(ReaderCl
previous_location = location;
}
- // Return the frame (from the current reader)
- return local_reader->GetFrame(location.frame);
+ // Get the frame (from the current reader)
+ last_frame = local_reader->GetFrame(location.frame);
+
+ // Update the frame number property
+ last_frame->number = requested_frame;
+
+ // Return the frame
+ return last_frame;
}
diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp
index ad4efdc9..4a22ed51 100644
--- a/src/ChunkWriter.cpp
+++ b/src/ChunkWriter.cpp
@@ -10,7 +10,7 @@
using namespace openshot;
ChunkWriter::ChunkWriter(string path, FileReaderBase *reader) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory) :
- local_reader(reader), path(path), chunk_size(24 * 3), chunk_count(1), frame_count(1), is_writing(false),
+ local_reader(reader), path(path), chunk_size(24*3), chunk_count(1), frame_count(1), is_writing(false),
default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis")
{
// Init FileInfo struct (clear all values)
@@ -62,22 +62,21 @@ void ChunkWriter::WriteFrame(tr1::shared_ptr frame)
// Create FFmpegWriter (FINAL quality)
create_folder(get_chunk_path(chunk_count, "final", ""));
writer_final = new FFmpegWriter(get_chunk_path(chunk_count, "final", default_extension));
- writer_final->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.audio_bit_rate);
+ writer_final->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, 128000);
writer_final->SetVideoOptions(true, default_vcodec, info.fps, info.width, info.height, info.pixel_ratio, false, false, info.video_bit_rate);
// Create FFmpegWriter (PREVIEW quality)
create_folder(get_chunk_path(chunk_count, "preview", ""));
writer_preview = new FFmpegWriter(get_chunk_path(chunk_count, "preview", default_extension));
- writer_preview->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.audio_bit_rate);
+ writer_preview->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, 128000);
writer_preview->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.5, info.height * 0.5, info.pixel_ratio, false, false, info.video_bit_rate * 0.5);
// Create FFmpegWriter (LOW quality)
create_folder(get_chunk_path(chunk_count, "thumb", ""));
writer_thumb = new FFmpegWriter(get_chunk_path(chunk_count, "thumb", default_extension));
- writer_thumb->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, info.audio_bit_rate);
+ writer_thumb->SetAudioOptions(true, default_acodec, info.sample_rate, info.channels, 128000);
writer_thumb->SetVideoOptions(true, default_vcodec, info.fps, info.width * 0.25, info.height * 0.25, info.pixel_ratio, false, false, info.video_bit_rate * 0.25);
-
// Prepare Streams
writer_final->PrepareStreams();
writer_preview->PrepareStreams();
@@ -97,12 +96,22 @@ void ChunkWriter::WriteFrame(tr1::shared_ptr frame)
writer_preview->WriteFrame(frame);
writer_thumb->WriteFrame(frame);
- // Increment frame counter
- frame_count++;
-
// Write the frames once it reaches the correct chunk size
if (frame_count % chunk_size == 0 && frame_count >= chunk_size)
{
+ cout << "Done with chunk" << endl;
+ cout << "frame_count: " << frame_count << endl;
+ cout << "chunk_size: " << chunk_size << endl;
+
+ // Pad an additional 12 frames
+ for (int z = 0; z<12; z++)
+ {
+ // Repeat frame
+ writer_final->WriteFrame(frame);
+ writer_preview->WriteFrame(frame);
+ writer_thumb->WriteFrame(frame);
+ }
+
// Write Footer
writer_final->WriteTrailer();
writer_preview->WriteTrailer();
@@ -120,6 +129,9 @@ void ChunkWriter::WriteFrame(tr1::shared_ptr frame)
is_writing = false;
}
+ // Increment frame counter
+ frame_count++;
+
// Keep track of the last frame added
last_frame = frame;
}
@@ -156,6 +168,39 @@ void ChunkWriter::WriteFrame(int start, int length)
// Close the writer
void ChunkWriter::Close()
{
+ // Write the frames once it reaches the correct chunk size
+ if (is_writing)
+ {
+ cout << "Final chunk" << endl;
+ cout << "frame_count: " << frame_count << endl;
+ cout << "chunk_size: " << chunk_size << endl;
+
+ // Pad an additional 12 frames
+ for (int z = 0; z<12; z++)
+ {
+ // Repeat frame
+ writer_final->WriteFrame(last_frame);
+ writer_preview->WriteFrame(last_frame);
+ writer_thumb->WriteFrame(last_frame);
+ }
+
+ // Write Footer
+ writer_final->WriteTrailer();
+ writer_preview->WriteTrailer();
+ writer_thumb->WriteTrailer();
+
+ // Close writer & reader
+ writer_final->Close();
+ writer_preview->Close();
+ writer_thumb->Close();
+
+ // Increment chunk count
+ chunk_count++;
+
+ // Stop writing chunk
+ is_writing = false;
+ }
+
// Reset frame counters
chunk_count = 0;
frame_count = 0;
diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp
index 7f3764a2..1dc127e7 100644
--- a/src/FFmpegReader.cpp
+++ b/src/FFmpegReader.cpp
@@ -7,7 +7,7 @@ FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, Inval
audio_pts_offset(99999), video_pts_offset(99999), path(path), is_video_seek(true), check_interlace(false),
check_fps(false), enable_seek(true), rescaler_position(0), num_of_rescalers(32), is_open(false),
seek_audio_frame_found(-1), seek_video_frame_found(-1), resampleCtx(NULL), prev_samples(0), prev_pts(0),
- pts_total(0), pts_counter(0), display_debug(false) {
+ pts_total(0), pts_counter(0), display_debug(false), is_duration_known(false) {
// Init FileInfo struct (clear all values)
InitFileInfo();
@@ -273,12 +273,32 @@ void FFmpegReader::UpdateVideoInfo()
// Set the duration in seconds, and video length (# of frames)
info.duration = pStream->duration * info.video_timebase.ToDouble();
- // Check for valid duration
+ // Check for valid duration (if found)
if (info.duration <= 0.0f && pFormatCtx->duration >= 0)
// Use the format's duration
info.duration = pFormatCtx->duration / AV_TIME_BASE;
- info.video_length = round(info.duration * info.fps.ToDouble());
+ // Calculate duration from filesize and bitrate (if any)
+ if (info.duration <= 0.0f && info.video_bit_rate > 0 && info.file_size > 0)
+ // Estimate from bitrate, total bytes, and framerate
+ info.duration = (info.file_size / info.video_bit_rate);
+
+ // No duration found in stream of file
+ if (info.duration <= 0.0f)
+ {
+ // No duration is found in the video stream
+ info.duration = -1;
+ info.video_length = -1;
+ is_duration_known = false;
+ }
+ else
+ {
+ // Yes, a duration was found
+ is_duration_known = true;
+
+ // Calculate number of frames
+ info.video_length = round(info.duration * info.fps.ToDouble());
+ }
// Override an invalid framerate
if (info.fps.ToFloat() > 120.0f)
@@ -295,7 +315,9 @@ void FFmpegReader::UpdateVideoInfo()
tr1::shared_ptr FFmpegReader::GetFrame(int requested_frame) throw(ReaderClosed, TooManySeeks)
{
- //cout << "GET FRAME " << requested_frame << ", last_frame: " << last_frame << endl;
+ if (display_debug)
+ cout << "GET FRAME " << requested_frame << ", last_frame: " << last_frame << endl;
+
// Check for open reader (or throw exception)
if (!is_open)
throw ReaderClosed("The FFmpegReader is closed. Call Open() before calling this method.", path);
@@ -311,20 +333,20 @@ tr1::shared_ptr FFmpegReader::GetFrame(int requested_frame) throw(ReaderC
// Adjust for a requested frame that is too small or too large
if (requested_frame < 1)
requested_frame = 1;
- if (requested_frame > info.video_length)
+ if (requested_frame > info.video_length && is_duration_known)
requested_frame = info.video_length;
if (info.has_video && info.video_length == 0)
// Invalid duration of video file
throw InvalidFile("Could not detect the duration of the video or audio stream.", path);
+ // Reset seek count
+ seek_count = 0;
+
// Check for first frame (always need to get frame 1 before other frames, to correctly calculate offsets)
if (last_frame == 0 && requested_frame != 1)
// Get first frame
ReadStream(1);
- // Reset seek count
- seek_count = 0;
-
// Are we within 30 frames of the requested frame?
int diff = requested_frame - last_frame;
if (diff >= 1 && diff <= 30)
@@ -366,12 +388,13 @@ tr1::shared_ptr FFmpegReader::ReadStream(int requested_frame)
// Minimum number of packets to process (for performance reasons)
int packets_processed = 0;
- int minimum_packets = omp_get_num_procs();
-
- //omp_set_num_threads(1);
+ int minimum_packets = omp_get_num_procs() / 2; // DEBUG, WORK-AROUND for an ImageMagick bug (preventing use of all 8 cores)
+ omp_set_num_threads(minimum_packets); // DEBUG, WORK-AROUND for an ImageMagick bug (preventing use of all 8 cores)
omp_set_nested(true);
+
#pragma omp parallel
{
+
#pragma omp single
{
// Loop through the stream until the correct frame is found
diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp
index de1ccce9..a5d75518 100644
--- a/src/FFmpegWriter.cpp
+++ b/src/FFmpegWriter.cpp
@@ -329,7 +329,7 @@ void FFmpegWriter::write_queued_frames()
spooled_video_frames.clear();
spooled_audio_frames.clear();
- //omp_set_num_threads(1);
+ omp_set_num_threads(4);
omp_set_nested(true);
#pragma omp parallel
{
diff --git a/src/Main.cpp b/src/Main.cpp
index a93e87d3..ef9b5f7a 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -20,10 +20,26 @@ void FrameReady(int number)
int main(int argc, char* argv[])
{
// Create a chunkwriter
- //FFmpegReader *r3 = new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4");
- //ChunkWriter cw1("/home/jonathan/apps/chunks/chunk1/", r3);
- //cw1.WriteFrame(r3, 1, r3->info.video_length);
- //cw1.Close();
+// FFmpegReader *r3 = new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4");
+// r3->DisplayInfo();
+// ChunkWriter cw1("/home/jonathan/apps/chunks/chunk1/", r3);
+// cw1.WriteFrame(r3, 1, r3->info.video_length);
+// cw1.Close();
+// return 0;
+
+ // FFmpegReader to test 1 part of a chunk
+// FFmpegReader *r10 = new FFmpegReader("/home/jonathan/apps/chunks/chunk1/final/000001.webm");
+// r10->enable_seek = false;
+// r10->Open();
+//// r10->GetFrame(1)->Display();
+//// r10->GetFrame(2)->Display();
+//// r10->GetFrame(3)->Display();
+//// r10->GetFrame(1)->Display();
+// for (int z1 = 20; z1 <= 24*3; z1++)
+// if (z1 >= 65)
+// r10->GetFrame(z1)->DisplayWaveform();
+// return 0;
+
// Create a chunkreader
cout << "Start Chunk Reader" << endl;
@@ -35,11 +51,11 @@ int main(int argc, char* argv[])
//cr1.GetFrame(300)->Display();
/* WRITER ---------------- */
- FFmpegWriter w9("/home/jonathan/fromchunks.webm");
+ FFmpegWriter w9("/home/jonathan/fromchunks.mp3");
// Set options
- w9.SetAudioOptions(true, cr1.info.acodec, cr1.info.sample_rate, cr1.info.channels, cr1.info.audio_bit_rate);
- w9.SetVideoOptions(true, cr1.info.vcodec, cr1.info.fps, cr1.info.width, cr1.info.height, cr1.info.pixel_ratio, false, false, cr1.info.video_bit_rate);
+ w9.SetAudioOptions(true, "libmp3lame", cr1.info.sample_rate, cr1.info.channels, cr1.info.audio_bit_rate);
+ //w9.SetVideoOptions(true, cr1.info.vcodec, cr1.info.fps, cr1.info.width, cr1.info.height, cr1.info.pixel_ratio, false, false, cr1.info.video_bit_rate);
// Prepare Streams
w9.PrepareStreams();
@@ -57,6 +73,7 @@ int main(int argc, char* argv[])
cout << "End Chunk Reader" << endl;
+ return 0;
// Qt Test Code
if (argc == 2)
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 61671967..b4786e3b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -35,6 +35,7 @@ include_directories(${SDL_INCLUDE_DIR})
################# QT4 ###################
# Find QT4 libraries
+SET(QT_QMAKE_EXECUTABLE '/usr/bin/qmake-qt4') # DEBUB, WORK-AROUND: Force the use of QT4 (when multiple versions are installed)
FIND_PACKAGE(Qt4 REQUIRED)
# Include Qt headers (needed for compile)