diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-01-18 00:40:49 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-01-18 00:40:49 +0000 |
| commit | 479b67ec506289a4763b2dd4f07e72d1dd676483 (patch) | |
| tree | 8a4570b83af694189f8035c77cfdbcb07f40dc5b /src/lib/writer.cc | |
| parent | d4024b9f794823a2808724ae9fae74195f1a0824 (diff) | |
Add writer class to pull some stuff out of Encoder.
Diffstat (limited to 'src/lib/writer.cc')
| -rw-r--r-- | src/lib/writer.cc | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/src/lib/writer.cc b/src/lib/writer.cc new file mode 100644 index 000000000..0381ee378 --- /dev/null +++ b/src/lib/writer.cc @@ -0,0 +1,180 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <libdcp/picture_asset.h> +#include "writer.h" +#include "compose.hpp" +#include "film.h" +#include "format.h" +#include "log.h" +#include "dcp_video_frame.h" + +using std::make_pair; +using std::pair; +using boost::shared_ptr; + +unsigned int const Writer::_maximum_frames_in_memory = 8; + +Writer::Writer (shared_ptr<const Film> f) + : _film (f) + , _thread (0) + , _finish (false) + , _last_written_frame (-1) +{ + _picture_asset.reset ( + new libdcp::MonoPictureAsset ( + _film->dir (_film->dcp_name()), + String::compose ("video_%1.mxf", 0), + DCPFrameRate (_film->frames_per_second()).frames_per_second, + _film->format()->dcp_size() + ) + ); + + _picture_asset_writer = _picture_asset->start_write (); + + _thread = new boost::thread (boost::bind (&Writer::thread, this)); +} + +void +Writer::write (shared_ptr<EncodedData> encoded, int frame) +{ + boost::mutex::scoped_lock lock (_mutex); + _queue.push_back (make_pair (encoded, frame)); + _condition.notify_all (); +} + +struct QueueSorter +{ + bool operator() (pair<shared_ptr<EncodedData>, int> const & a, pair<shared_ptr<EncodedData>, int> const & b) { + return a.second < b.second; + } +}; + +void +Writer::thread () +{ + while (1) + { + boost::mutex::scoped_lock lock (_mutex); + + while (1) { + if (_finish || + _queue.size() > _maximum_frames_in_memory || + (!_queue.empty() && _queue.front().second == (_last_written_frame + 1))) { + + break; + } + + TIMING ("writer sleeps with a queue of %1; %2 pending", _queue.size(), _pending.size()); + _condition.wait (lock); + TIMING ("writer wakes with a queue of %1", _queue.size()); + + _queue.sort (QueueSorter ()); + } + + if (_finish && _queue.empty() && _pending.empty()) { + return; + } + + /* Write any frames that we can write; i.e. those that are in sequence */ + while (!_queue.empty() && _queue.front().second == (_last_written_frame + 1)) { + pair<boost::shared_ptr<EncodedData>, int> encoded = _queue.front (); + _queue.pop_front (); + + lock.unlock (); + _film->log()->log (String::compose ("Writer writes %1 to MXF", encoded.second)); + if (encoded.first) { + _picture_asset_writer->write (encoded.first->data(), encoded.first->size()); + _last_written = encoded.first; + } else { + _picture_asset_writer->write (_last_written->data(), _last_written->size()); + } + lock.lock (); + + ++_last_written_frame; + } + + while (_queue.size() > _maximum_frames_in_memory) { + /* Too many frames in memory which can't yet be written to the stream. + Put some to disk. + */ + + pair<boost::shared_ptr<EncodedData>, int> encoded = _queue.back (); + _queue.pop_back (); + if (!encoded.first) { + /* This is a `repeat-last' frame, so no need to write it to disk */ + continue; + } + + lock.unlock (); + _film->log()->log (String::compose ("Writer full (awaiting %1); pushes %2 to disk", _last_written_frame + 1, encoded.second)); + encoded.first->write (_film, encoded.second); + lock.lock (); + + _pending.push_back (encoded.second); + } + + while (_queue.size() < _maximum_frames_in_memory && !_pending.empty()) { + /* We have some space in memory. Fetch some frames back off disk. */ + + _pending.sort (); + int const fetch = _pending.front (); + + lock.unlock (); + _film->log()->log (String::compose ("Writer pulls %1 back from disk", fetch)); + shared_ptr<EncodedData> encoded; + if (boost::filesystem::exists (_film->frame_out_path (fetch, false))) { + /* It's an actual frame (not a repeat-last); load it in */ + encoded.reset (new EncodedData (_film->frame_out_path (fetch, false))); + } + lock.lock (); + + _queue.push_back (make_pair (encoded, fetch)); + _pending.remove (fetch); + } + } + +} + +void +Writer::finish () +{ + if (!_thread) { + return; + } + + boost::mutex::scoped_lock lock (_mutex); + _finish = true; + _condition.notify_all (); + lock.unlock (); + + _thread->join (); + delete _thread; + _thread = 0; + + _picture_asset_writer->finalize (); +} + +/** Tell the writer that frame `f' should be a repeat of the frame before it */ +void +Writer::repeat (int f) +{ + boost::mutex::scoped_lock lock (_mutex); + _queue.push_back (make_pair (shared_ptr<EncodedData> (), f)); +} |
