diff --git a/include/ChunkWriter.h b/include/ChunkWriter.h
index e04540cc..836d5f7d 100644
--- a/include/ChunkWriter.h
+++ b/include/ChunkWriter.h
@@ -88,6 +88,7 @@ namespace openshot
FFmpegWriter *writer_preview;
FFmpegWriter *writer_final;
tr1::shared_ptr last_frame;
+ bool last_frame_needed;
string default_extension;
string default_vcodec;
string default_acodec;
diff --git a/include/Frame.h b/include/Frame.h
index 3a4e2161..cb27cc8c 100644
--- a/include/Frame.h
+++ b/include/Frame.h
@@ -65,7 +65,55 @@ namespace openshot
* @brief This class represents a single frame of video (i.e. image & audio data)
*
* FileReaders (such as FFmpegReader) use instances of this class to store the individual frames of video,
- * which include both the image data (i.e. pixels) and audio samples.
+ * which include both the image data (i.e. pixels) and audio samples. An openshot::Frame also has many debug
+ * methods, such as the ability to display the image (using X11), play the audio samples (using JUCE), or
+ * display the audio waveform as an image.
+ *
+ * FileWriters (such as FFmpegWriter) use instances of this class to create new video files, image files, or
+ * video streams. So, think of these openshot::Frame instances as the smallest unit of work in a video
+ * editor.
+ *
+ * There are many ways to create an instance of an openshot::Frame:
+ * @code
+ *
+ * // Most basic: a blank frame (300x200 blank image, 48kHz audio silence)
+ * Frame();
+ *
+ * // Image only settings (48kHz audio silence)
+ * Frame(1, // Frame number
+ * 720, // Width of image
+ * 480, // Height of image
+ * "#000000" // HTML color code of background color
+ * );
+ *
+ * // Image only from pixel array (48kHz audio silence)
+ * Frame(number, // Frame number
+ * 720, // Width of image
+ * 480, // Height of image
+ * "RGBA", // Color format / map
+ * Magick::CharPixel, // Storage format / data type
+ * buffer // Array of image data (pixels)
+ * );
+ *
+ * // Audio only (300x200 blank image)
+ * Frame(number, // Frame number
+ * 44100, // Sample rate of audio stream
+ * 2 // Number of audio channels
+ * );
+ *
+ * // Image and Audio settings (user defines all key settings)
+ * Frame(number, // Frame number
+ * 720, // Width of image
+ * 480, // Height of image
+ * "#000000" // HTML color code of background color
+ * 44100, // Sample rate of audio stream
+ * 2 // Number of audio channels
+ * );
+ *
+ * // Some methods require a shared pointer to an openshot::Frame object.
+ * tr1::shared_ptr f(new Frame(1, 720, 480, "#000000", 44100, 2));
+ *
+ * @endcode
*/
class Frame
{
diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp
index 89b3fda7..154948b4 100644
--- a/src/ChunkReader.cpp
+++ b/src/ChunkReader.cpp
@@ -128,12 +128,12 @@ void ChunkReader::load_json()
// Find the location of a frame in a chunk
ChunkLocation ChunkReader::find_chunk_frame(int requested_frame)
{
- // Determine which chunk contains this frame
+ // Determine which chunk contains this frame.
int chunk_number = (requested_frame / chunk_size) + 1;
// Determine which frame in this chunk
int start_frame_of_chunk = (chunk_number - 1) * chunk_size;
- int chunk_frame_number = requested_frame - start_frame_of_chunk;
+ int chunk_frame_number = (requested_frame - start_frame_of_chunk) + 1; // Add 1 to adjust for the 1st frame of every chunk is just there to "stoke" the audio samples from the previous chunk.
// Prepare chunk location struct
ChunkLocation location = {chunk_number, chunk_frame_number};
diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp
index d60f5db6..760c2b4b 100644
--- a/src/ChunkWriter.cpp
+++ b/src/ChunkWriter.cpp
@@ -31,7 +31,7 @@ using namespace openshot;
ChunkWriter::ChunkWriter(string path, ReaderBase *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),
- default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis")
+ default_extension(".webm"), default_vcodec("libvpx"), default_acodec("libvorbis"), last_frame_needed(false)
{
// Init FileInfo struct (clear all values)
InitFileInfo();
@@ -107,14 +107,42 @@ void ChunkWriter::WriteFrame(tr1::shared_ptr frame)
writer_preview->WriteHeader();
writer_thumb->WriteHeader();
- // Keep track that a chunk is being writen
+ // Keep track that a chunk is being written
is_writing = true;
+ last_frame_needed = true;
}
- // Write a frame to the current chunk
+ // If this is not the 1st chunk, always start frame 1 with the last frame from the previous
+ // chunk. This helps to prevent audio resampling issues (because it "stokes" the sample array)
+ if (last_frame_needed)
+ {
+ if (last_frame)
+ {
+ // Write the previous chunks LAST FRAME to the current chunk
+ writer_final->WriteFrame(last_frame);
+ writer_preview->WriteFrame(last_frame);
+ writer_thumb->WriteFrame(last_frame);
+ } else {
+ // Write the 1st frame (of the 1st chunk)... since no previous chunk is available
+ tr1::shared_ptr blank_frame(new Frame(1, info.width, info.height, "#000000", info.sample_rate, info.channels));
+ blank_frame->AddColor(info.width, info.height, "#000000");
+ writer_final->WriteFrame(blank_frame);
+ writer_preview->WriteFrame(blank_frame);
+ writer_thumb->WriteFrame(blank_frame);
+ }
+
+ // disable last frame
+ last_frame_needed = false;
+ }
+
+
+ //////////////////////////////////////////////////
+ // WRITE THE CURRENT FRAME TO THE CURRENT CHUNK
writer_final->WriteFrame(frame);
writer_preview->WriteFrame(frame);
writer_thumb->WriteFrame(frame);
+ //////////////////////////////////////////////////
+
// Write the frames once it reaches the correct chunk size
if (frame_count % chunk_size == 0 && frame_count >= chunk_size)
diff --git a/src/Main.cpp b/src/Main.cpp
index 9aa27ddd..ab453aa6 100644
--- a/src/Main.cpp
+++ b/src/Main.cpp
@@ -49,7 +49,7 @@ int main(int argc, char* argv[])
// 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.WriteFrame(r3, 1, r3->info.video_length - 45);
// cw1.Close();
// return 0;
@@ -77,11 +77,11 @@ int main(int argc, char* argv[])
//cr1.GetFrame(300)->Display();
/* WRITER ---------------- */
- FFmpegWriter w9("/home/jonathan/fromchunks.mp3");
+ FFmpegWriter w9("/home/jonathan/fromchunks.webm");
// Set options
- 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);
+ w9.SetAudioOptions(true, "libvorbis", 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();