From bcfbe60a4a882c155da6447d99a5affadc45890d Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 31 Jul 2013 12:45:47 -0500 Subject: [PATCH] initial chunck readers/writers --- include/ChunkReader.h | 58 +++++++++++++++ include/ChunkWriter.h | 79 ++++++++++++++++++++ include/FileWriterBase.h | 3 + include/OpenShot.h | 1 + src/CMakeLists.txt | 1 + src/ChunkReader.cpp | 99 +++++++++++++++++++++++++ src/ChunkWriter.cpp | 152 +++++++++++++++++++++++++++++++++++++++ src/FileWriterBase.cpp | 33 +++++++++ src/Main.cpp | 10 +++ src/openshot.i | 2 + 10 files changed, 438 insertions(+) create mode 100644 include/ChunkReader.h create mode 100644 include/ChunkWriter.h create mode 100644 src/ChunkReader.cpp create mode 100644 src/ChunkWriter.cpp diff --git a/include/ChunkReader.h b/include/ChunkReader.h new file mode 100644 index 00000000..27b43329 --- /dev/null +++ b/include/ChunkReader.h @@ -0,0 +1,58 @@ +#ifndef OPENSHOT_CHUNK_READER_H +#define OPENSHOT_CHUNK_READER_H + +/** + * \file + * \brief Header file for ChunkReader class + * \author Copyright (c) 2013 Jonathan Thomas + */ + +#include "FileReaderBase.h" + +#include +#include +#include +#include +#include +#include +#include "Magick++.h" +#include "Cache.h" +#include "Exceptions.h" + +using namespace std; + +namespace openshot +{ + + /** + * \brief This class reads a special chunk-formatted file, which can be easily + * shared in a distributed environment, and can return openshot::Frame objects. + */ + class ChunkReader : public FileReaderBase + { + private: + string path; + bool is_open; + + public: + + /// 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); + + /// Close Reader + void Close(); + + /// 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); + + /// Open File - which is called by the constructor automatically + void Open() throw(InvalidFile); + }; + +} + +#endif diff --git a/include/ChunkWriter.h b/include/ChunkWriter.h new file mode 100644 index 00000000..0bdaac76 --- /dev/null +++ b/include/ChunkWriter.h @@ -0,0 +1,79 @@ +#ifndef OPENSHOT_CHUNK_WRITER_H +#define OPENSHOT_CHUNK_WRITER_H + +/** + * \file + * \brief Header file for ChunkWriter class + * \author Copyright (c) 2013 Jonathan Thomas + */ + +#include "FileReaderBase.h" +#include "FileWriterBase.h" + +#include +#include +#include +#include +#include +#include +#include +#include "Magick++.h" +#include "Cache.h" +#include "Exceptions.h" +#include "Sleep.h" + + +using namespace std; + +namespace openshot +{ + /** + * \brief This class takes any reader and generates a special type of video file, built with + * chunks of small video and audio data. These chunks can easily be passed around in a distributed + * computing environment, without needing to share the entire video file. + */ + class ChunkWriter : public FileWriterBase + { + private: + string path; + int cache_size; + bool is_writing; + int64 write_video_count; + int64 write_audio_count; + + tr1::shared_ptr last_frame; + deque > spooled_frames; + deque > queued_frames; + deque > processed_frames; + + /// process video frame + void process_frame(tr1::shared_ptr frame); + + /// write all queued frames + void write_queued_frames(); + + public: + + /// Constructor for ChunkWriter. Throws one of the following exceptions. + ChunkWriter(FileReaderBase *reader, string path) throw(InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory); + + /// Close the writer + void Close(); + + /// Get the cache size (number of frames to queue before writing) + int GetCacheSize() { return cache_size; }; + + /// Set the cache size (number of frames to queue before writing) + int SetCacheSize(int new_size) { cache_size = new_size; }; + + /// Add a frame to the stack waiting to be encoded. + void WriteFrame(tr1::shared_ptr frame); + + /// Write a block of frames from a reader + void WriteFrame(FileReaderBase* reader, int start, int length); + + }; + +} + +#endif diff --git a/include/FileWriterBase.h b/include/FileWriterBase.h index d1ddb4d8..7d4ecb98 100644 --- a/include/FileWriterBase.h +++ b/include/FileWriterBase.h @@ -63,6 +63,9 @@ namespace openshot /// Information about the current media file WriterInfo info; + /// This method copy's the info struct of a reader, and sets the writer with the same info + void CopyReaderInfo(FileReaderBase* reader); + /// This method is required for all derived classes of FileWriterBase. Write a Frame to the video file. virtual void WriteFrame(tr1::shared_ptr frame) = 0; diff --git a/include/OpenShot.h b/include/OpenShot.h index f3a49679..644bd6ea 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -28,6 +28,7 @@ #include "AudioBufferSource.h" #include "AudioResampler.h" #include "Cache.h" +#include "ChunkWriter.h" #include "Clip.h" #include "Coordinate.h" #ifdef USE_BLACKMAGIC diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40914dfe..7cc3e6c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ SET ( OPENSHOT_SOURCE_FILES AudioBufferSource.cpp AudioResampler.cpp Cache.cpp + ChunkWriter.cpp Clip.cpp Coordinate.cpp DummyReader.cpp diff --git a/src/ChunkReader.cpp b/src/ChunkReader.cpp new file mode 100644 index 00000000..67c512e7 --- /dev/null +++ b/src/ChunkReader.cpp @@ -0,0 +1,99 @@ +#include "../include/ChunkReader.h" + +using namespace openshot; + +ChunkReader::ChunkReader(string path) throw(InvalidFile) : path(path), is_open(false) +{ + // Init FileInfo struct (clear all values) + InitFileInfo(); + + // Open and Close the reader, to populate it's attributes (such as height, width, etc...) + Open(); + Close(); +} + +// Open chunk folder or file +void ChunkReader::Open() throw(InvalidFile) +{ + // Open reader if not already open + if (!is_open) + { + // Attempt to open file + try + { + // load chunk + image = tr1::shared_ptr(new Magick::Image(path)); + + // Give image a transparent background color + image->backgroundColor(Magick::Color("none")); + } + catch (Magick::Exception e) { + // raise exception + throw InvalidFile("File could not be opened.", path); + } + + // Update image properties + info.has_audio = false; + info.has_video = true; + info.file_size = image->fileSize(); + info.vcodec = image->format(); + info.width = image->size().width(); + info.height = image->size().height(); + info.pixel_ratio.num = 1; + info.pixel_ratio.den = 1; + info.duration = 60 * 60 * 24; // 24 hour duration + info.fps.num = 30; + info.fps.den = 1; + info.video_timebase.num = 1; + info.video_timebase.den = 30; + info.video_length = round(info.duration * info.fps.ToDouble()); + + // Calculate the DAR (display aspect ratio) + Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den); + + // Reduce size fraction + size.Reduce(); + + // Set the ratio based on the reduced fraction + info.display_ratio.num = size.num; + info.display_ratio.den = size.den; + + // Mark as "open" + is_open = true; + } +} + +// Close image file +void ChunkReader::Close() +{ + // Close all objects, if reader is 'open' + if (is_open) + { + // Mark as "closed" + is_open = false; + } +} + +// Get an openshot::Frame object for a specific frame number of this reader. +tr1::shared_ptr ChunkReader::GetFrame(int requested_frame) throw(ReaderClosed) +{ + if (image) + { + // Create or get frame object + tr1::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2)); + image_frame->SetSampleRate(44100); + + // Add Image data to frame + tr1::shared_ptr copy_image(new Magick::Image(*image.get())); + copy_image->modifyImage(); // actually copy the image data to this object + image_frame->AddImage(copy_image); + + // return frame object + return image_frame; + } + else + // no frame loaded + throw InvalidFile("No frame could be created from this type of file.", path); +} + + diff --git a/src/ChunkWriter.cpp b/src/ChunkWriter.cpp new file mode 100644 index 00000000..0c5eae13 --- /dev/null +++ b/src/ChunkWriter.cpp @@ -0,0 +1,152 @@ +/** + * \file + * \brief Source code for the ChunkWriter class + * \author Copyright (c) 2013 Jonathan Thomas + */ + + +#include "../include/ChunkWriter.h" + +using namespace openshot; + +ChunkWriter::ChunkWriter(FileReaderBase *reader, string path) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory) : + path(path), cache_size(8), is_writing(false) +{ + // Init FileInfo struct (clear all values) + InitFileInfo(); + + // Copy info struct from the source reader + CopyReaderInfo(reader); +} + +// Add a frame to the queue waiting to be encoded. +void ChunkWriter::WriteFrame(tr1::shared_ptr frame) +{ + // Add frame pointer to "queue", waiting to be processed the next + // time the WriteFrames() method is called. + spooled_frames.push_back(frame); + + // Write the frames once it reaches the correct cache size + if (spooled_frames.size() == cache_size) + { + // Is writer currently writing? + if (!is_writing) + // Write frames to video file + write_queued_frames(); + + else + { + // YES, WRITING... so wait until it finishes, before writing again + while (is_writing) + Sleep(1); // sleep for 250 milliseconds + + // Write frames to video file + write_queued_frames(); + } + } + + // Keep track of the last frame added + last_frame = frame; +} + +// Write all frames in the queue to the video file. +void ChunkWriter::write_queued_frames() +{ + // Flip writing flag + is_writing = true; + + // Transfer spool to queue + queued_frames = spooled_frames; + + // Empty spool + spooled_frames.clear(); + + //omp_set_num_threads(1); + omp_set_nested(true); + #pragma omp parallel + { + #pragma omp single + { + // Loop through each queued image frame + while (!queued_frames.empty()) + { + // Get front frame (from the queue) + tr1::shared_ptr frame = queued_frames.front(); + + // Add to processed queue + processed_frames.push_back(frame); + + // Encode and add the frame to the output file + process_frame(frame); + + // Remove front item + queued_frames.pop_front(); + + } // end while + + // Done writing + is_writing = false; + + } // end omp single + } // end omp parallel + +} + +// Write a block of frames from a reader +void ChunkWriter::WriteFrame(FileReaderBase* reader, int start, int length) +{ + // Loop through each frame (and encoded it) + for (int number = start; number <= length; number++) + { + // Get the frame + tr1::shared_ptr f = reader->GetFrame(number); + + // Encode frame + WriteFrame(f); + } +} + +// Close the writer +void ChunkWriter::Close() +{ + // Reset frame counters + write_video_count = 0; + write_audio_count = 0; +} + +// process frame +void ChunkWriter::process_frame(tr1::shared_ptr frame) +{ + #pragma omp task firstprivate(frame) + { + // Determine the height & width of the source image + int source_image_width = frame->GetWidth(); + int source_image_height = frame->GetHeight(); + + // Generate frame image name + stringstream thumb_name; + stringstream preview_name; + stringstream final_name; + thumb_name << frame->number << "_t.JPG"; + preview_name << frame->number << "_p.JPG"; + final_name << frame->number << "_f.JPG"; + + #pragma omp critical (chunk_output) + cout << "Writing " << thumb_name.str() << endl; + + // Do nothing if size is 1x1 (i.e. no image in this frame) + if (source_image_height > 1 && source_image_width > 1) + { + // Write image of frame to chunk + frame->Save(thumb_name.str(), 0.25); + frame->Save(preview_name.str(), 0.5); + frame->Save(final_name.str(), 1.0); + } + + + + } // end task + +} + + diff --git a/src/FileWriterBase.cpp b/src/FileWriterBase.cpp index 2088d38c..005b46f9 100644 --- a/src/FileWriterBase.cpp +++ b/src/FileWriterBase.cpp @@ -30,6 +30,39 @@ void FileWriterBase::InitFileInfo() info.audio_timebase = Fraction(); } +// This method copy's the info struct of a reader, and sets the writer with the same info +void FileWriterBase::CopyReaderInfo(FileReaderBase* reader) +{ + info.has_video = reader->info.has_video; + info.has_audio = reader->info.has_audio; + info.duration = reader->info.duration; + info.file_size = reader->info.file_size; + info.height = reader->info.height; + info.width = reader->info.width; + info.pixel_format = reader->info.pixel_format; + info.fps.num = reader->info.fps.num; + info.fps.den = reader->info.fps.den; + info.video_bit_rate = reader->info.video_bit_rate; + info.pixel_ratio.num = reader->info.pixel_ratio.num; + info.pixel_ratio.den = reader->info.pixel_ratio.den; + info.display_ratio.num = reader->info.display_ratio.num; + info.display_ratio.den = reader->info.display_ratio.den; + info.vcodec = reader->info.vcodec; + info.video_length = reader->info.video_length; + info.video_stream_index = reader->info.video_stream_index; + info.video_timebase.num = reader->info.video_timebase.num; + info.video_timebase.den = reader->info.video_timebase.den; + info.interlaced_frame = reader->info.interlaced_frame; + info.top_field_first = reader->info.top_field_first; + info.acodec = reader->info.acodec; + info.audio_bit_rate = reader->info.audio_bit_rate; + info.sample_rate = reader->info.sample_rate; + info.channels = reader->info.channels; + info.audio_stream_index = reader->info.audio_stream_index; + info.audio_timebase.num = reader->info.audio_timebase.num; + info.audio_timebase.den = reader->info.audio_timebase.den; +} + // Display file information void FileWriterBase::DisplayInfo() { cout << fixed << setprecision(2) << boolalpha; diff --git a/src/Main.cpp b/src/Main.cpp index adbbb30e..b00d748e 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -18,6 +18,16 @@ void FrameReady(int number) int main() { + // Chunk writer example + FFmpegReader *r1 = new FFmpegReader("/home/jonathan/Videos/sintel_trailer-720p.mp4"); + r1->Open(); + ChunkWriter cw(r1, ""); + cw.WriteFrame(r1, 1, 600); + + return 0; + + + TextReader r(720, 480, 10, 10, GRAVITY_TOP_RIGHT, "What's Up!", "Courier", 30, "Blue", "Black"); r.Open(); tr1::shared_ptr f = r.GetFrame(1); diff --git a/src/openshot.i b/src/openshot.i index e5737185..deb83b3c 100644 --- a/src/openshot.i +++ b/src/openshot.i @@ -21,6 +21,7 @@ #include "../include/FileWriterBase.h" #include "../include/Cache.h" #include "../include/Clip.h" +#include "../include/ChunkWriter.h" #include "../include/Coordinate.h" #include "../include/DummyReader.h" #include "../include/Exceptions.h" @@ -48,6 +49,7 @@ %include "../include/FileReaderBase.h" %include "../include/FileWriterBase.h" %include "../include/Cache.h" +%include "../include/ChunkWriter.h" %include "../include/Clip.h" %include "../include/Coordinate.h" #ifdef USE_BLACKMAGIC