You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Fixed many bugs on the multi-threaded encoder, simplified the API, and removed some memory leaks.
This commit is contained in:
@@ -55,6 +55,7 @@ namespace openshot
|
||||
{
|
||||
private:
|
||||
string path;
|
||||
int cache_size;
|
||||
|
||||
AVOutputFormat *fmt;
|
||||
AVFormatContext *oc;
|
||||
@@ -115,7 +116,8 @@ namespace openshot
|
||||
/// write video frame
|
||||
void write_video_packet(Frame* frame, AVFrame* frame_final);
|
||||
|
||||
|
||||
/// write all queued frames
|
||||
void write_queued_frames();
|
||||
|
||||
public:
|
||||
|
||||
@@ -125,12 +127,18 @@ namespace openshot
|
||||
/// Close the writer
|
||||
void Close();
|
||||
|
||||
/// Get the cache size (number of frames to queue before writing)
|
||||
int GetCacheSize() { return cache_size; };
|
||||
|
||||
/// Output the ffmpeg info about this format, streams, and codecs (i.e. dump format)
|
||||
void OutputStreamInfo();
|
||||
|
||||
/// Set audio export options
|
||||
void SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, int bit_rate);
|
||||
|
||||
/// Set the cache size (number of frames to queue before writing)
|
||||
int SetCacheSize(int new_size) { cache_size = new_size; };
|
||||
|
||||
/// Set video export options
|
||||
void SetVideoOptions(bool has_video, string codec, Fraction fps, int width, int height,
|
||||
Fraction pixel_ratio, bool interlaced, bool top_field_first, int bit_rate);
|
||||
@@ -145,13 +153,10 @@ namespace openshot
|
||||
void WriteHeader();
|
||||
|
||||
/// Add a frame to the stack waiting to be encoded.
|
||||
void AddFrame(Frame* frame);
|
||||
|
||||
/// Write all frames on the stack to the video file.
|
||||
void WriteFrames();
|
||||
void WriteFrame(Frame* frame);
|
||||
|
||||
/// Write a block of frames from a reader
|
||||
//void WriteFrame(FileReaderBase* reader, int start, int length);
|
||||
void WriteFrame(FileReaderBase* reader, int start, int length);
|
||||
|
||||
/// Write the file trailer (after all frames are written)
|
||||
void WriteTrailer();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <iomanip>
|
||||
#include "Fraction.h"
|
||||
#include "Frame.h"
|
||||
#include "FileReaderBase.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -62,13 +63,11 @@ namespace openshot
|
||||
/// Information about the current media file
|
||||
WriterInfo info;
|
||||
|
||||
/// This method is required for all derived classes of FileWriterBase. Add a frame to the stack
|
||||
/// waiting to be encoded.
|
||||
virtual void AddFrame(Frame* frame) = 0;
|
||||
/// This method is required for all derived classes of FileWriterBase. Write a Frame to the video file.
|
||||
virtual void WriteFrame(Frame* frame) = 0;
|
||||
|
||||
/// This method is required for all derived classes of FileWriterBase. Write all frames on the
|
||||
/// stack.
|
||||
virtual void WriteFrames() = 0;
|
||||
/// This method is required for all derived classes of FileWriterBase. Write a block of frames from a reader.
|
||||
virtual void WriteFrame(FileReaderBase* reader, int start, int length) = 0;
|
||||
|
||||
/// Initialize the values of the WriterInfo struct. It is important for derived classes to call
|
||||
/// this method, or the WriterInfo struct values will not be initialized.
|
||||
|
||||
@@ -34,7 +34,8 @@ FIND_PACKAGE(OpenMP)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
|
||||
|
||||
############### PROFILING #################
|
||||
#set(PROFILER "/usr/lib/libprofiler.so")
|
||||
set(PROFILER "/usr/lib/libprofiler.so")
|
||||
#set(PROFILER "/usr/lib/libtcmalloc.so")
|
||||
|
||||
############### CREATE LIBRARY #################
|
||||
# Create shared openshot library
|
||||
|
||||
@@ -30,7 +30,7 @@ using namespace openshot;
|
||||
FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, InvalidCodec, InvalidOptions, OutOfMemory) :
|
||||
path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL),
|
||||
audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0),
|
||||
initial_audio_input_frame_size(0), resampler(NULL), img_convert_ctx(NULL)
|
||||
initial_audio_input_frame_size(0), resampler(NULL), img_convert_ctx(NULL), cache_size(12)
|
||||
{
|
||||
|
||||
// Init FileInfo struct (clear all values)
|
||||
@@ -282,7 +282,7 @@ void FFmpegWriter::WriteHeader()
|
||||
}
|
||||
|
||||
// Add a frame to the queue waiting to be encoded.
|
||||
void FFmpegWriter::AddFrame(Frame* frame)
|
||||
void FFmpegWriter::WriteFrame(Frame* frame)
|
||||
{
|
||||
// Add frame pointer to "queue", waiting to be processed the next
|
||||
// time the WriteFrames() method is called.
|
||||
@@ -291,10 +291,15 @@ void FFmpegWriter::AddFrame(Frame* frame)
|
||||
|
||||
if (info.has_audio && audio_st)
|
||||
audio_frames.push_back(frame);
|
||||
|
||||
// Write the frames once it reaches the correct cache size
|
||||
if (queued_frames.size() == cache_size)
|
||||
// Write frames to video file
|
||||
write_queued_frames();
|
||||
}
|
||||
|
||||
// Write all frames in the queue to the video file.
|
||||
void FFmpegWriter::WriteFrames()
|
||||
void FFmpegWriter::write_queued_frames()
|
||||
{
|
||||
//omp_set_num_threads(1);
|
||||
#pragma omp parallel
|
||||
@@ -342,11 +347,15 @@ void FFmpegWriter::WriteFrames()
|
||||
// Add to deallocate queue (so we can remove the AVFrames when we are done)
|
||||
deallocate_frames.push_back(frame);
|
||||
|
||||
// Get AVFrame
|
||||
AVFrame *frame_final = av_frames[frame];
|
||||
// Does this frame's AVFrame still exist
|
||||
if (av_frames.count(frame))
|
||||
{
|
||||
// Get AVFrame
|
||||
AVFrame *frame_final = av_frames[frame];
|
||||
|
||||
// Write frame to video file
|
||||
write_video_packet(frame, frame_final);
|
||||
// Write frame to video file
|
||||
write_video_packet(frame, frame_final);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove front item
|
||||
@@ -379,22 +388,25 @@ void FFmpegWriter::WriteFrames()
|
||||
}
|
||||
|
||||
// Write a block of frames from a reader
|
||||
//void FFmpegWriter::WriteFrame(FileReaderBase* reader, int start, int length)
|
||||
//{
|
||||
// // Loop through each frame (and encoded it)
|
||||
// for (int number = start; number <= length; number++)
|
||||
// {
|
||||
// // Get the frame
|
||||
// Frame *f = reader->GetFrame(number);
|
||||
//
|
||||
// // Encode frame
|
||||
// WriteFrame(f);
|
||||
// }
|
||||
//}
|
||||
void FFmpegWriter::WriteFrame(FileReaderBase* reader, int start, int length)
|
||||
{
|
||||
// Loop through each frame (and encoded it)
|
||||
for (int number = start; number <= length; number++)
|
||||
{
|
||||
// Get the frame
|
||||
Frame *f = reader->GetFrame(number);
|
||||
|
||||
// Encode frame
|
||||
WriteFrame(f);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the file trailer (after all frames are written)
|
||||
void FFmpegWriter::WriteTrailer()
|
||||
{
|
||||
// Write any remaining queued frames to video file
|
||||
write_queued_frames();
|
||||
|
||||
/* write the trailer, if any. the trailer must be written
|
||||
* before you close the CodecContexts open when you wrote the
|
||||
* header; otherwise write_trailer may try to use memory that
|
||||
@@ -459,8 +471,10 @@ void FFmpegWriter::add_avframe(Frame* frame, AVFrame* av_frame)
|
||||
{
|
||||
// Add AVFrame to map (if it does not already exist)
|
||||
if (!av_frames.count(frame))
|
||||
// Add
|
||||
{
|
||||
// Add av_frame
|
||||
av_frames[frame] = av_frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not add, and deallocate this AVFrame
|
||||
@@ -683,9 +697,9 @@ void FFmpegWriter::write_audio_packets()
|
||||
c = audio_st->codec;
|
||||
|
||||
// Create a resampler (only once)
|
||||
if (!resampler)
|
||||
resampler = new AudioResampler();
|
||||
AudioResampler *new_sampler = resampler;
|
||||
if (!new_sampler)
|
||||
new_sampler = new AudioResampler();
|
||||
|
||||
#pragma omp task firstprivate(c, new_sampler)
|
||||
{
|
||||
@@ -715,6 +729,7 @@ void FFmpegWriter::write_audio_packets()
|
||||
channels_in_frame = frame->GetAudioChannelsCount();
|
||||
|
||||
// Get audio sample array
|
||||
//float* frame_samples_float = new float(total_frame_samples);
|
||||
float* frame_samples_float = frame->GetInterleavedAudioSamples(info.sample_rate, new_sampler, &samples_in_frame);
|
||||
|
||||
// Calculate total samples
|
||||
@@ -867,12 +882,12 @@ void FFmpegWriter::process_video_packet(Frame* frame)
|
||||
c = video_st->codec;
|
||||
|
||||
// Initialize the software scaler (if needed)
|
||||
SwsContext *scaler = img_convert_ctx;
|
||||
if (!scaler)
|
||||
if (!img_convert_ctx)
|
||||
// Init the software scaler from FFMpeg
|
||||
scaler = sws_getContext(frame->GetWidth(), frame->GetHeight(), PIX_FMT_RGB24, info.width, info.height, c->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
||||
if (scaler == NULL)
|
||||
img_convert_ctx = sws_getContext(frame->GetWidth(), frame->GetHeight(), PIX_FMT_RGB24, info.width, info.height, c->pix_fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL);
|
||||
if (img_convert_ctx == NULL)
|
||||
throw OutOfMemory("Could not allocate SwsContext.", path);
|
||||
SwsContext *scaler = img_convert_ctx;
|
||||
|
||||
#pragma omp task firstprivate(frame, c, scaler)
|
||||
{
|
||||
|
||||
@@ -294,6 +294,13 @@ float* Frame::GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* re
|
||||
}
|
||||
}
|
||||
|
||||
// Clear resampled buffer (if any)
|
||||
if (resampled_buffer)
|
||||
{
|
||||
resampled_buffer->clear();
|
||||
delete resampled_buffer;
|
||||
}
|
||||
|
||||
// Update sample count (since it might have changed due to resampling)
|
||||
*sample_count = num_of_samples;
|
||||
|
||||
@@ -322,14 +329,14 @@ int Frame::GetAudioSamplesRate()
|
||||
// Get pixel data (as packets)
|
||||
const Magick::PixelPacket* Frame::GetPixels()
|
||||
{
|
||||
// Return arry of pixel packets
|
||||
// Return array of pixel packets
|
||||
return image->getConstPixels(0,0, image->columns(), image->rows());
|
||||
}
|
||||
|
||||
// Get pixel data (for only a single scan-line)
|
||||
const Magick::PixelPacket* Frame::GetPixels(int row)
|
||||
{
|
||||
// Return arry of pixel packets
|
||||
// Return array of pixel packets
|
||||
return image->getConstPixels(0,row, image->columns(), 1);
|
||||
}
|
||||
|
||||
@@ -343,9 +350,9 @@ const Magick::PixelPacket* Frame::GetPixels(unsigned int width, unsigned int hei
|
||||
small_image->colorize(255, 0, 0, Magick::Color(0,0,255));
|
||||
small_image->blur(5.0, 5.0);
|
||||
|
||||
stringstream file;
|
||||
file << "frame" << frame << ".png";
|
||||
small_image->write(file.str());
|
||||
// stringstream file;
|
||||
// file << "frame" << frame << ".png";
|
||||
// small_image->write(file.str());
|
||||
|
||||
// Return arry of pixel packets
|
||||
return small_image->getConstPixels(0,0, small_image->columns(), small_image->rows());
|
||||
|
||||
19
src/Main.cpp
19
src/Main.cpp
@@ -20,9 +20,9 @@ int main()
|
||||
// openshot::FFmpegReader r("../../src/examples/piano.wav");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/big-buck-bunny_trailer.webm");
|
||||
|
||||
openshot::FFmpegReader r("/home/jonathan/Videos/sintel-1024-stereo.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/sintel-1024-stereo.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/00001.mts");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Videos/sintel_trailer-720p.mp4");
|
||||
openshot::FFmpegReader r("/home/jonathan/Videos/sintel_trailer-720p.mp4");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Aptana Studio Workspace/OpenShotLibrary/src/examples/piano.wav");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Music/Army of Lovers/Crucified/Army of Lovers - Crucified [Single Version].mp3");
|
||||
// openshot::FFmpegReader r("/home/jonathan/Documents/OpenShot Art/test.jpeg");
|
||||
@@ -38,7 +38,7 @@ int main()
|
||||
|
||||
// Set options
|
||||
w.SetAudioOptions(true, "libvorbis", 44100, 2, 128000);
|
||||
w.SetVideoOptions(true, "libvpx", Fraction(25, 1), 640, 360, Fraction(1,1), false, false, 2000000);
|
||||
w.SetVideoOptions(true, "libvpx", Fraction(24, 1), 640, 360, Fraction(1,1), false, false, 2000000);
|
||||
|
||||
// Prepare Streams
|
||||
w.PrepareStreams();
|
||||
@@ -67,7 +67,7 @@ int main()
|
||||
|
||||
//Frame *f = r.GetFrame(1);
|
||||
|
||||
for (int frame = 1; frame <= 500; frame++)
|
||||
for (int frame = 1; frame <= 1000; frame++)
|
||||
{
|
||||
Frame *f = r.GetFrame(frame);
|
||||
|
||||
@@ -76,19 +76,10 @@ int main()
|
||||
|
||||
// Write frame
|
||||
cout << "queue frame " << frame << endl;
|
||||
w.AddFrame(f);
|
||||
w.WriteFrame(f);
|
||||
|
||||
if (frame % 12 == 0)
|
||||
{
|
||||
cout << "-- Writing frames --" << endl;
|
||||
w.WriteFrames();
|
||||
}
|
||||
}
|
||||
|
||||
// Write remaining frames
|
||||
w.WriteFrames();
|
||||
w.WriteFrames();
|
||||
|
||||
// Write Footer
|
||||
w.WriteTrailer();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user