You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
initial chunck readers/writers
This commit is contained in:
58
include/ChunkReader.h
Normal file
58
include/ChunkReader.h
Normal file
@@ -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 <cmath>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <omp.h>
|
||||
#include <stdio.h>
|
||||
#include <tr1/memory>
|
||||
#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<Frame> GetFrame(int requested_frame) throw(ReaderClosed);
|
||||
|
||||
/// Open File - which is called by the constructor automatically
|
||||
void Open() throw(InvalidFile);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
79
include/ChunkWriter.h
Normal file
79
include/ChunkWriter.h
Normal file
@@ -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 <cmath>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <omp.h>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#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<Frame> last_frame;
|
||||
deque<tr1::shared_ptr<Frame> > spooled_frames;
|
||||
deque<tr1::shared_ptr<Frame> > queued_frames;
|
||||
deque<tr1::shared_ptr<Frame> > processed_frames;
|
||||
|
||||
/// process video frame
|
||||
void process_frame(tr1::shared_ptr<Frame> 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> frame);
|
||||
|
||||
/// Write a block of frames from a reader
|
||||
void WriteFrame(FileReaderBase* reader, int start, int length);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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> frame) = 0;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,6 +59,7 @@ SET ( OPENSHOT_SOURCE_FILES
|
||||
AudioBufferSource.cpp
|
||||
AudioResampler.cpp
|
||||
Cache.cpp
|
||||
ChunkWriter.cpp
|
||||
Clip.cpp
|
||||
Coordinate.cpp
|
||||
DummyReader.cpp
|
||||
|
||||
99
src/ChunkReader.cpp
Normal file
99
src/ChunkReader.cpp
Normal file
@@ -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<Magick::Image>(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<Frame> ChunkReader::GetFrame(int requested_frame) throw(ReaderClosed)
|
||||
{
|
||||
if (image)
|
||||
{
|
||||
// Create or get frame object
|
||||
tr1::shared_ptr<Frame> 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<Magick::Image> 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);
|
||||
}
|
||||
|
||||
|
||||
152
src/ChunkWriter.cpp
Normal file
152
src/ChunkWriter.cpp
Normal file
@@ -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> 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> 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<Frame> 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> 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
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
10
src/Main.cpp
10
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<Frame> f = r.GetFrame(1);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user