diff --git a/include/ChunkReader.h b/include/ChunkReader.h index 14d97d6c..d60810c9 100644 --- a/include/ChunkReader.h +++ b/include/ChunkReader.h @@ -8,6 +8,7 @@ */ #include "FileReaderBase.h" +#include "FFmpegReader.h" #include #include @@ -28,6 +29,22 @@ using namespace std; namespace openshot { + /// This struct holds the location of a frame within a chunk. Chunks are small video files, which + /// typically contain a few seconds of video each. Locating a frame among these small video files is + /// accomplished by using this struct. + struct chunk_location + { + int number; + int frame; + }; + + enum ChunkVersion + { + THUMBNAIL, + PREVIEW, + FINAL + }; + /** * \brief This class reads a special chunk-formatted file, which can be easily * shared in a distributed environment, and can return openshot::Frame objects. @@ -37,10 +54,20 @@ namespace openshot private: string path; bool is_open; + int chunk_size; + FFmpegReader *local_reader; + chunk_location previous_location; + ChunkVersion version; /// Check if folder path existing bool does_folder_exist(string path); + /// Find the location of a frame in a chunk + chunk_location find_chunk_frame(int requested_frame); + + /// get a formatted path of a specific chunk + string get_chunk_path(int chunk_number, string folder, string extension); + /// Load JSON meta data about this chunk folder void load_json(); @@ -48,16 +75,22 @@ namespace openshot /// Constructor for ChunkReader. This automatically opens the chunk file or folder and loads /// frame 1, or it throws one of the following exceptions. - ChunkReader(string path) throw(InvalidFile, InvalidJSON); + ChunkReader(string path, ChunkVersion chunk_version) throw(InvalidFile, InvalidJSON); /// Close Reader void Close(); + /// Get the chunk size (number of frames to write in each chunk) + int GetChunkSize() { return chunk_size; }; + + /// Set the chunk size (number of frames to write in each chunk) + int SetChunkSize(int new_size) { chunk_size = new_size; }; + /// Get an openshot::Frame object for a specific frame number of this reader. /// /// @returns The requested frame (containing the image) /// @param[requested_frame] number The frame number that is requested. - tr1::shared_ptr GetFrame(int requested_frame) throw(ReaderClosed); + tr1::shared_ptr GetFrame(int requested_frame) throw(ReaderClosed, ChunkNotFound); /// Open File - which is called by the constructor automatically void Open() throw(InvalidFile); diff --git a/include/Exceptions.h b/include/Exceptions.h index d79924d3..716af922 100644 --- a/include/Exceptions.h +++ b/include/Exceptions.h @@ -32,6 +32,20 @@ namespace openshot { } }; + /// Exception when a required chunk is missing + class ChunkNotFound : public BaseException + { + public: + string file_path; + int frame_number; + int chunk_number; + int chunk_frame; + ChunkNotFound(string message, int frame_number, int chunk_number, int chunk_frame) + : BaseException(message), frame_number(frame_number), chunk_number(chunk_number), chunk_frame(chunk_frame) { } + virtual ~ChunkNotFound() throw () {} + }; + + /// Exception when accessing a blackmagic decklink card class DecklinkError : public BaseException { diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp index e612cfd7..272eae61 100644 --- a/src/ChunkReader.cpp +++ b/src/ChunkReader.cpp @@ -2,8 +2,8 @@ using namespace openshot; -ChunkReader::ChunkReader(string path) throw(InvalidFile, InvalidJSON) - : path(path), is_open(false) +ChunkReader::ChunkReader(string path, ChunkVersion chunk_version) throw(InvalidFile, InvalidJSON) + : path(path), chunk_size(24 * 3), is_open(false), version(chunk_version), local_reader(NULL) { // Init FileInfo struct (clear all values) InitFileInfo(); @@ -13,6 +13,10 @@ ChunkReader::ChunkReader(string path) throw(InvalidFile, InvalidJSON) // Raise exception throw InvalidFile("Chunk folder could not be opened.", path); + // Init previous location + previous_location.number = 0; + previous_location.frame = 0; + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) Open(); Close(); @@ -94,6 +98,22 @@ void ChunkReader::load_json() } } +// Find the location of a frame in a chunk +chunk_location ChunkReader::find_chunk_frame(int requested_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; + + // Prepare chunk location struct + chunk_location location = {chunk_number, chunk_frame_number}; + + return location; +} + // Open chunk folder or file void ChunkReader::Open() throw(InvalidFile) { @@ -119,18 +139,83 @@ void ChunkReader::Close() } } -// Get an openshot::Frame object for a specific frame number of this reader. -tr1::shared_ptr ChunkReader::GetFrame(int requested_frame) throw(ReaderClosed) +// get a formatted path of a specific chunk +string ChunkReader::get_chunk_path(int chunk_number, string folder, string extension) { - if (1==1) + // Create path of new chunk video + stringstream chunk_count_string; + chunk_count_string << chunk_number; + QString padded_count = "%1"; //chunk_count_string.str().c_str(); + padded_count = padded_count.arg(chunk_count_string.str().c_str(), 6, '0'); + if (folder.length() != 0 && extension.length() != 0) + // Return path with FOLDER and EXTENSION name + return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str() + QDir::separator() + padded_count + extension.c_str()).toStdString(); + + else if (folder.length() == 0 && extension.length() != 0) + // Return path with NO FOLDER and EXTENSION name + return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + padded_count + extension.c_str()).toStdString(); + + else if (folder.length() != 0 && extension.length() == 0) + // Return path with FOLDER and NO EXTENSION + return QDir::cleanPath(QString(path.c_str()) + QDir::separator() + folder.c_str()).toStdString(); +} + +// Get an openshot::Frame object for a specific frame number of this reader. +tr1::shared_ptr ChunkReader::GetFrame(int requested_frame) throw(ReaderClosed, ChunkNotFound) +{ + // Determine what chunk contains this frame + chunk_location location = find_chunk_frame(requested_frame); + + // New Chunk (Close the old reader, and open the new one) + if (previous_location.number != location.number) { + // Determine version of chunk + string folder_name = ""; + switch (version) + { + case THUMBNAIL: + folder_name = "thumb"; + break; + case PREVIEW: + folder_name = "preview"; + break; + case FINAL: + folder_name = "final"; + break; + } + // Load path of chunk video + string chunk_video_path = get_chunk_path(location.number, folder_name, ".webm"); + // Close existing reader (if needed) + if (local_reader) + { + cout << "Close READER" << endl; + // Close and delete old reader + local_reader->Close(); + delete local_reader; + } + try + { + cout << "Load READER: " << chunk_video_path << endl; + // Load new FFmpegReader + local_reader = new FFmpegReader(chunk_video_path); + local_reader->enable_seek = false; // disable seeking + local_reader->Open(); // open reader + + } catch (InvalidFile) + { + // Invalid Chunk (possibly it is not found) + throw ChunkNotFound(path, requested_frame, location.number, location.frame); + } + + // Set the new location + previous_location = location; } - else - // no frame loaded - throw InvalidFile("No frame could be created from this type of file.", path); + + // Return the frame (from the current reader) + return local_reader->GetFrame(location.frame); } diff --git a/src/Main.cpp b/src/Main.cpp index 3b03585e..e1a9095d 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -27,8 +27,13 @@ int main(int argc, char* argv[]) // Create a chunkreader cout << "Start Chunk Reader" << endl; - ChunkReader cr1("/home/jonathan/apps/chunks/chunk1/"); + ChunkReader cr1("/home/jonathan/apps/chunks/chunk1/", FINAL); cr1.DisplayInfo(); + cr1.Open(); + //for (int z1 = 70; z1 < 85; z1++) + // cr1.GetFrame(z1)->Display(); + cr1.GetFrame(300)->Display(); + cout << "End Chunk Reader" << endl; // Qt Test Code