diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index 9a765697..2996eac3 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -69,9 +69,12 @@ namespace openshot int audio_input_frame_size; int initial_audio_input_frame_size; int audio_input_position; - AudioResampler *resampler; + deque queued_frames; + deque processed_frames; + map av_frames; + /// Add an audio output stream AVStream* add_audio_stream(); @@ -99,11 +102,16 @@ namespace openshot /// open video codec void open_video(AVFormatContext *oc, AVStream *st); + /// process video frame + void process_video_packet(Frame* frame); + /// write audio frame void write_audio_packet(Frame* frame); /// write video frame - void write_video_packet(Frame* frame); + void write_video_packet(Frame* frame, AVFrame* frame_final); + + public: @@ -132,11 +140,14 @@ namespace openshot /// Write the file header (after the options are set) void WriteHeader(); - /// Write a single frame - void WriteFrame(Frame* frame); + /// 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(); /// 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(); diff --git a/include/FileWriterBase.h b/include/FileWriterBase.h index ba8fb5a2..a3b583e4 100644 --- a/include/FileWriterBase.h +++ b/include/FileWriterBase.h @@ -62,8 +62,13 @@ namespace openshot /// Information about the current media file WriterInfo info; - /// This method is required for all derived classes of FileWriterBase - virtual void WriteFrame(Frame* frame) = 0; + /// 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 all frames on the + /// stack. + virtual void WriteFrames() = 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. diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 59f539b4..6de23dc4 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -4,7 +4,7 @@ using namespace openshot; FFmpegReader::FFmpegReader(string path) throw(InvalidFile, NoStreamsFound, InvalidCodec) : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), - audio_pts_offset(99999), video_pts_offset(99999), working_cache(10), final_cache(10), path(path), + audio_pts_offset(99999), video_pts_offset(99999), working_cache(12), final_cache(12), path(path), is_video_seek(true), check_interlace(false), check_fps(false), enable_seek(true), rescaler_position(0), num_of_rescalers(32) { diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 6d2b2a4c..8da14a5a 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -281,30 +281,89 @@ void FFmpegWriter::WriteHeader() avformat_write_header(oc, NULL); } -// Write a single frame -void FFmpegWriter::WriteFrame(Frame* frame) +// Add a frame to the queue waiting to be encoded. +void FFmpegWriter::AddFrame(Frame* frame) { - // Encode and add the frame to the output file - if (info.has_video && video_st) - write_video_packet(frame); + // Add frame pointer to "queue", waiting to be processed the next + // time the WriteFrames() method is called. + queued_frames.push_back(frame); +} + +// Write all frames in the queue to the video file. +void FFmpegWriter::WriteFrames() +{ + //omp_set_num_threads(1); + #pragma omp parallel + { + #pragma omp master + { + // Loop through each queued frame + while (!queued_frames.empty()) + { + // Get front frame (from the queue) + Frame *frame = queued_frames.front(); + + // Add to processed queue + processed_frames.push_back(frame); + + // Encode and add the frame to the output file + if (info.has_video && video_st) + process_video_packet(frame); + + if (info.has_audio && audio_st) + write_audio_packet(frame); + + + // Remove front item + queued_frames.pop_front(); + + } // end while + + } // end omp master + + // Be sure all threads are finished + #pragma omp barrier + + } // end omp parallel + + + // Loop back through the frames (in order), and write them to the video file + while (!processed_frames.empty()) + { + // Get front frame (from the queue) + Frame *frame = processed_frames.front(); + + if (info.has_video && video_st) + { + // Get AVFrame + AVFrame *frame_final = av_frames[frame]; + + // Write frame to video file + write_video_packet(frame, frame_final); + + // Remove AVFrame + av_frames.erase(frame); + } + + // Remove front item + processed_frames.pop_front(); + } - if (info.has_audio && audio_st) - write_audio_packet(frame); } // 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() @@ -723,42 +782,73 @@ AVFrame* FFmpegWriter::allocate_avframe(PixelFormat pix_fmt, int width, int heig return new_av_frame; } -// write video frame -void FFmpegWriter::write_video_packet(Frame* frame) +// process video frame +void FFmpegWriter::process_video_packet(Frame* frame) { // Get the codec AVCodecContext *c; c = video_st->codec; - // Allocate an RGB frame & final output frame - int bytes_source = 0; - int bytes_final = 0; - AVFrame *frame_source = allocate_avframe(PIX_FMT_RGB24, frame->GetWidth(), frame->GetHeight(), &bytes_source); - AVFrame *frame_final = allocate_avframe(c->pix_fmt, info.width, info.height, &bytes_final); - - // Get a list of pixels from the frame. - const Magick::PixelPacket *pixel_packets = frame->GetPixels(); - - // Fill the AVFrame with RGB image data - for (int packet = 0, row = 0; row < bytes_source; packet++, row+=3) + // Initialize the software scaler (if needed) + SwsContext *scaler = img_convert_ctx; + #pragma omp critical (image_scaler) { - // Update buffer (which is already linked to the AVFrame: pFrameRGB) - frame_source->data[0][row] = pixel_packets[packet].red; - frame_source->data[0][row+1] = pixel_packets[packet].green; - frame_source->data[0][row+2] = pixel_packets[packet].blue; + if (!scaler) + // 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) + throw OutOfMemory("Could not allocate SwsContext.", path); } + #pragma omp task firstprivate(frame, c, scaler) + { - // Resize image and convet pixel format to correct output format (for example: RGB to YUV420P) - if (!img_convert_ctx) - // Init the software scaler from FFMpeg - 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); + // Allocate an RGB frame & final output frame + int bytes_source = 0; + int bytes_final = 0; + AVFrame *frame_source = allocate_avframe(PIX_FMT_RGB24, frame->GetWidth(), frame->GetHeight(), &bytes_source); + AVFrame *frame_final = allocate_avframe(c->pix_fmt, info.width, info.height, &bytes_final); - // Resize & convert pixel format - sws_scale(img_convert_ctx, frame_source->data, frame_source->linesize, 0, - frame->GetHeight(), frame_final->data, frame_final->linesize); + // Get a list of pixels from the frame. + const Magick::PixelPacket *pixel_packets = frame->GetPixels(); + + // Fill the AVFrame with RGB image data + for (int packet = 0, row = 0; row < bytes_source; packet++, row+=3) + { + // Update buffer (which is already linked to the AVFrame: pFrameRGB) + frame_source->data[0][row] = pixel_packets[packet].red; + frame_source->data[0][row+1] = pixel_packets[packet].green; + frame_source->data[0][row+2] = pixel_packets[packet].blue; + } + +// SwsContext *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) +// throw OutOfMemory("Could not allocate SwsContext.", path); + + // Resize & convert pixel format + #pragma omp critical (image_scaler) + sws_scale(scaler, frame_source->data, frame_source->linesize, 0, + frame->GetHeight(), frame_final->data, frame_final->linesize); + + // Add resized AVFrame to av_frames map + #pragma omp critical (av_frames) + av_frames[frame] = frame_final; + + // Deallocate memory + av_free(frame_source->data[0]); + av_free(frame_source); + + } // end task + + +} + +// write video frame +void FFmpegWriter::write_video_packet(Frame* frame, AVFrame* frame_final) +{ + // Get the codec + AVCodecContext *c; + c = video_st->codec; // Encode Picture and Write Frame @@ -819,11 +909,10 @@ void FFmpegWriter::write_video_packet(Frame* frame) } // Deallocate memory - av_free(frame_source->data[0]); - av_free(frame_source); av_free(frame_final->data[0]); av_free(frame_final); delete[] video_outbuf; + } // Output the ffmpeg info about this format, streams, and codecs (i.e. dump format) diff --git a/src/Main.cpp b/src/Main.cpp index 646217f3..7b1ea125 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -14,25 +14,6 @@ void FrameReady(int number) int main() { -// Cache c; -// // Loop 30 times -// for (int i = 0; i < 30; i++) -// { -// // Add blank frame to the cache -// Frame *f = new Frame(); -// c.Add(i, f); -// -// -// if (i == 27 || i == 28) -// c.Remove(i); -// else -// c.SetCurrentFrame(i); -// -// } -// cout << c.Count() << endl; -// c.Clear(); -// cout << c.Count() << endl; - // openshot::FFmpegReader r("../../src/examples/test.mp4"); // openshot::FFmpegReader r("../../src/examples/test1.mp4"); @@ -84,20 +65,30 @@ int main() // Output stream info w.OutputStreamInfo(); - Frame *f = r.GetFrame(300); + //Frame *f = r.GetFrame(300); - for (int frame = 1; frame <= 1000; frame++) + for (int frame = 1; frame <= 500; frame++) { - //Frame *f = r.GetFrame(frame); + Frame *f = r.GetFrame(frame); // Apply effect //f->AddEffect("flip"); // Write frame - cout << "Write frame " << f->number << endl; - w.WriteFrame(f); + cout << "queue frame " << f->number << endl; + w.AddFrame(f); + + if (frame % 12 == 0) + { + cout << "-- Writing frames --" << endl; + w.WriteFrames(); + } } + // Write remaining frames + w.WriteFrames(); + w.WriteFrames(); + // Write Footer w.WriteTrailer();