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