diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-07-22 16:23:23 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-07-22 16:23:23 +0100 |
| commit | 8349f0c97d98c0b7550ff4c76ad25f8f06270d6a (patch) | |
| tree | bf441e13142b2810ac4e46cf94d4204ec4e037d5 /src/lib | |
| parent | 75712cfaf2a8ec8904d7d9552c542a2245bbbc17 (diff) | |
Basics of front-end 3D (as far as viewer, at least).
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/dcp_video_frame.cc | 32 | ||||
| -rw-r--r-- | src/lib/dcp_video_frame.h | 11 | ||||
| -rw-r--r-- | src/lib/encoder.cc | 23 | ||||
| -rw-r--r-- | src/lib/encoder.h | 4 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 2 | ||||
| -rw-r--r-- | src/lib/film.cc | 48 | ||||
| -rw-r--r-- | src/lib/film.h | 18 | ||||
| -rw-r--r-- | src/lib/player.cc | 15 | ||||
| -rw-r--r-- | src/lib/player.h | 9 | ||||
| -rw-r--r-- | src/lib/server.cc | 3 | ||||
| -rw-r--r-- | src/lib/still_image_decoder.cc | 2 | ||||
| -rw-r--r-- | src/lib/transcoder.cc | 6 | ||||
| -rw-r--r-- | src/lib/types.h | 9 | ||||
| -rw-r--r-- | src/lib/video_content.cc | 3 | ||||
| -rw-r--r-- | src/lib/video_decoder.cc | 17 | ||||
| -rw-r--r-- | src/lib/video_decoder.h | 10 | ||||
| -rw-r--r-- | src/lib/writer.cc | 103 | ||||
| -rw-r--r-- | src/lib/writer.h | 11 |
18 files changed, 232 insertions, 94 deletions
diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index ef8db7d23..10f1e4ad1 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -77,10 +77,11 @@ using libdcp::Size; * @param l Log to write to. */ DCPVideoFrame::DCPVideoFrame ( - shared_ptr<const Image> image, int f, int dcp_fps, int bw, shared_ptr<Log> l + shared_ptr<const Image> image, int f, Eyes eyes, int dcp_fps, int bw, shared_ptr<Log> l ) : _image (image) , _frame (f) + , _eyes (eyes) , _frames_per_second (dcp_fps) , _j2k_bandwidth (bw) , _log (l) @@ -102,7 +103,11 @@ DCPVideoFrame::encode_locally () ); /* Set the max image and component sizes based on frame_rate */ - int const max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second; + int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second; + if (_eyes == EYES_LEFT || _eyes == EYES_RIGHT) { + /* In 3D we have only half the normal bandwidth per eye */ + max_cs_len /= 2; + } int const max_comp_size = max_cs_len / 1.25; /* get a J2K compressor handle */ @@ -208,12 +213,13 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) socket->connect (*endpoint_iterator); stringstream s; - s << N_("encode please\n") - << N_("width ") << _image->size().width << N_("\n") - << N_("height ") << _image->size().height << N_("\n") - << N_("frame ") << _frame << N_("\n") - << N_("frames_per_second ") << _frames_per_second << N_("\n") - << N_("j2k_bandwidth ") << _j2k_bandwidth << N_("\n"); + s << "encode please\n" + << "width " << _image->size().width << "\n" + << "height " << _image->size().height << "\n" + << "eyes " << static_cast<int> (_eyes) << "\n" + << "frame " << _frame << "\n" + << "frames_per_second " << _frames_per_second << "\n" + << "j2k_bandwidth " << _j2k_bandwidth << "\n"; _log->log (String::compose ( N_("Sending to remote; pixel format %1, components %2, lines (%3,%4,%5), line sizes (%6,%7,%8)"), @@ -272,9 +278,9 @@ EncodedData::~EncodedData () * @param frame DCP frame index. */ void -EncodedData::write (shared_ptr<const Film> film, int frame) const +EncodedData::write (shared_ptr<const Film> film, int frame, Eyes eyes) const { - string const tmp_j2c = film->j2c_path (frame, true); + string const tmp_j2c = film->j2c_path (frame, eyes, true); FILE* f = fopen (tmp_j2c.c_str (), N_("wb")); @@ -285,16 +291,16 @@ EncodedData::write (shared_ptr<const Film> film, int frame) const fwrite (_data, 1, _size, f); fclose (f); - string const real_j2c = film->j2c_path (frame, false); + string const real_j2c = film->j2c_path (frame, eyes, false); /* Rename the file from foo.j2c.tmp to foo.j2c now that it is complete */ boost::filesystem::rename (tmp_j2c, real_j2c); } void -EncodedData::write_info (shared_ptr<const Film> film, int frame, libdcp::FrameInfo fin) const +EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, libdcp::FrameInfo fin) const { - string const info = film->info_path (frame); + string const info = film->info_path (frame, eyes); ofstream h (info.c_str()); fin.write (h); } diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index 4c545b6ef..e786d9b61 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -47,8 +47,8 @@ public: virtual ~EncodedData (); void send (boost::shared_ptr<Socket> socket); - void write (boost::shared_ptr<const Film>, int) const; - void write_info (boost::shared_ptr<const Film>, int, libdcp::FrameInfo) const; + void write (boost::shared_ptr<const Film>, int, Eyes) const; + void write_info (boost::shared_ptr<const Film>, int, Eyes, libdcp::FrameInfo) const; /** @return data */ uint8_t* data () const { @@ -101,11 +101,15 @@ public: class DCPVideoFrame : public boost::noncopyable { public: - DCPVideoFrame (boost::shared_ptr<const Image>, int, int, int, boost::shared_ptr<Log>); + DCPVideoFrame (boost::shared_ptr<const Image>, int, Eyes, int, int, boost::shared_ptr<Log>); boost::shared_ptr<EncodedData> encode_locally (); boost::shared_ptr<EncodedData> encode_remotely (ServerDescription const *); + Eyes eyes () const { + return _eyes; + } + int frame () const { return _frame; } @@ -113,6 +117,7 @@ public: private: boost::shared_ptr<const Image> _image; int _frame; ///< frame index within the DCP's intrinsic duration + Eyes _eyes; int _frames_per_second; ///< Frames per second that we will use for the DCP int _j2k_bandwidth; ///< J2K bandwidth to use diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 718ae55a4..8e61a0d60 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -52,10 +52,11 @@ Encoder::Encoder (shared_ptr<const Film> f, shared_ptr<Job> j) : _film (f) , _job (j) , _video_frames_out (0) - , _have_a_real_frame (false) , _terminate (false) { - + _have_a_real_frame[EYES_BOTH] = false; + _have_a_real_frame[EYES_LEFT] = false; + _have_a_real_frame[EYES_RIGHT] = false; } Encoder::~Encoder () @@ -117,7 +118,7 @@ Encoder::process_end () for (list<shared_ptr<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) { _film->log()->log (String::compose (N_("Encode left-over frame %1"), (*i)->frame ())); try { - _writer->write ((*i)->encode_locally(), (*i)->frame ()); + _writer->write ((*i)->encode_locally(), (*i)->frame (), (*i)->eyes ()); frame_done (); } catch (std::exception& e) { _film->log()->log (String::compose (N_("Local encode failed (%1)"), e.what ())); @@ -170,7 +171,7 @@ Encoder::frame_done () } void -Encoder::process_video (shared_ptr<const Image> image, bool same) +Encoder::process_video (shared_ptr<const Image> image, Eyes eyes, bool same) { boost::mutex::scoped_lock lock (_mutex); @@ -190,25 +191,25 @@ Encoder::process_video (shared_ptr<const Image> image, bool same) } if (_writer->can_fake_write (_video_frames_out)) { - _writer->fake_write (_video_frames_out); - _have_a_real_frame = false; + _writer->fake_write (_video_frames_out, eyes); + _have_a_real_frame[eyes] = false; frame_done (); - } else if (same && _have_a_real_frame) { + } else if (same && _have_a_real_frame[eyes]) { /* Use the last frame that we encoded. */ - _writer->repeat (_video_frames_out); + _writer->repeat (_video_frames_out, eyes); frame_done (); } else { /* Queue this new frame for encoding */ TIMING ("adding to queue of %1", _queue.size ()); _queue.push_back (shared_ptr<DCPVideoFrame> ( new DCPVideoFrame ( - image, _video_frames_out, _film->dcp_video_frame_rate(), + image, _video_frames_out, eyes, _film->dcp_video_frame_rate(), _film->j2k_bandwidth(), _film->log() ) )); _condition.notify_all (); - _have_a_real_frame = true; + _have_a_real_frame[eyes] = true; } ++_video_frames_out; @@ -302,7 +303,7 @@ Encoder::encoder_thread (ServerDescription* server) } if (encoded) { - _writer->write (encoded, vf->frame ()); + _writer->write (encoded, vf->frame (), vf->eyes ()); frame_done (); } else { lock.lock (); diff --git a/src/lib/encoder.h b/src/lib/encoder.h index a6e5932be..a866a77f1 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -66,7 +66,7 @@ public: * @param i Video frame image. * @param same true if i is the same as the last time we were called. */ - void process_video (boost::shared_ptr<const Image> i, bool same); + void process_video (boost::shared_ptr<const Image> i, Eyes eyes, bool same); /** Call with some audio data */ void process_audio (boost::shared_ptr<const AudioBuffers>); @@ -100,7 +100,7 @@ private: /** Number of video frames written for the DCP so far */ int _video_frames_out; - bool _have_a_real_frame; + bool _have_a_real_frame[EYES_COUNT]; bool _terminate; std::list<boost::shared_ptr<DCPVideoFrame> > _queue; std::list<boost::thread *> _threads; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 11cea8fb1..ea32d102d 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -59,7 +59,7 @@ using libdcp::Size; FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio) : Decoder (f) - , VideoDecoder (f) + , VideoDecoder (f, c) , AudioDecoder (f) , SubtitleDecoder (f) , FFmpeg (c) diff --git a/src/lib/film.cc b/src/lib/film.cc index e39e94cfc..40c696256 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -95,6 +95,7 @@ Film::Film (string d) , _dci_metadata (Config::instance()->default_dci_metadata ()) , _dcp_video_frame_rate (24) , _dcp_audio_channels (MAX_AUDIO_CHANNELS) + , _dcp_3d (false) , _sequence_video (true) , _dirty (false) { @@ -141,6 +142,10 @@ Film::video_identifier () const << "_" << scaler()->id() << "_" << j2k_bandwidth(); + if (_dcp_3d) { + s << "_3D"; + } + return s.str (); } @@ -327,6 +332,7 @@ Film::write_metadata () const root->add_child("DCPVideoFrameRate")->add_child_text (lexical_cast<string> (_dcp_video_frame_rate)); root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date)); root->add_child("DCPAudioChannels")->add_child_text (lexical_cast<string> (_dcp_audio_channels)); + root->add_child("DCP3D")->add_child_text (_dcp_3d ? "1" : "0"); root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0"); _playlist->as_xml (root->add_child ("Playlist")); @@ -439,6 +445,14 @@ Film::dci_name (bool if_created_now) const d << "_" << dcp_content_type()->dci_name(); } + if (dcp_3d ()) { + d << "-3D"; + } + + if (dcp_video_frame_rate() != 24) { + d << "-" << dcp_video_frame_rate(); + } + if (container()) { d << "_" << container()->dci_name(); } @@ -636,6 +650,16 @@ Film::set_dcp_audio_channels (int c) } void +Film::set_dcp_3d (bool t) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _dcp_3d = t; + } + signal_changed (DCP_3D); +} + +void Film::signal_changed (Property p) { { @@ -667,15 +691,23 @@ Film::set_dci_date_today () } string -Film::info_path (int f) const +Film::info_path (int f, Eyes e) const { boost::filesystem::path p; p /= info_dir (); stringstream s; s.width (8); - s << setfill('0') << f << ".md5"; + s << setfill('0') << f; + + if (e == EYES_LEFT) { + s << ".L"; + } else if (e == EYES_RIGHT) { + s << ".R"; + } + s << ".md5"; + p /= s.str(); /* info_dir() will already have added any initial bit of the path, @@ -685,7 +717,7 @@ Film::info_path (int f) const } string -Film::j2c_path (int f, bool t) const +Film::j2c_path (int f, Eyes e, bool t) const { boost::filesystem::path p; p /= "j2c"; @@ -693,7 +725,15 @@ Film::j2c_path (int f, bool t) const stringstream s; s.width (8); - s << setfill('0') << f << ".j2c"; + s << setfill('0') << f; + + if (e == EYES_LEFT) { + s << ".L"; + } else if (e == EYES_RIGHT) { + s << ".R"; + } + + s << ".j2c"; if (t) { s << ".tmp"; diff --git a/src/lib/film.h b/src/lib/film.h index 99c214ab7..5aff6f0be 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -55,8 +55,8 @@ public: Film (std::string d); std::string info_dir () const; - std::string j2c_path (int f, bool t) const; - std::string info_path (int f) const; + std::string j2c_path (int, Eyes, bool) const; + std::string info_path (int, Eyes) const; std::string internal_video_mxf_dir () const; std::string internal_video_mxf_filename () const; boost::filesystem::path audio_analysis_path (boost::shared_ptr<const AudioContent>) const; @@ -130,7 +130,9 @@ public: DCI_METADATA, DCP_VIDEO_FRAME_RATE, DCP_AUDIO_CHANNELS, - SEQUENCE_VIDEO + /** The setting of _dcp_3d has been changed */ + DCP_3D, + SEQUENCE_VIDEO, }; @@ -197,6 +199,11 @@ public: return _dcp_audio_channels; } + bool dcp_3d () const { + boost::mutex::scoped_lock lm (_state_mutex); + return _dcp_3d; + } + bool sequence_video () const { boost::mutex::scoped_lock lm (_state_mutex); return _sequence_video; @@ -220,6 +227,7 @@ public: void set_dci_metadata (DCIMetadata); void set_dcp_video_frame_rate (int); void set_dcp_audio_channels (int); + void set_dcp_3d (bool); void set_dci_date_today (); void set_sequence_video (bool); @@ -275,6 +283,10 @@ private: /** The date that we should use in a DCI name */ boost::gregorian::date _dci_date; int _dcp_audio_channels; + /** If true, the DCP will be written in 3D mode; otherwise in 2D. + This will be regardless of what content is on the playlist. + */ + bool _dcp_3d; bool _sequence_video; /** true if our state has changed since we last saved it */ diff --git a/src/lib/player.cc b/src/lib/player.cc index 98a31ae74..20beda945 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -202,7 +202,7 @@ Player::pass () } void -Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, bool same, VideoContent::Frame frame) +Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image, Eyes eyes, bool same, VideoContent::Frame frame) { shared_ptr<Piece> piece = weak_piece.lock (); if (!piece) { @@ -242,11 +242,11 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image _last_video = piece->content; #endif - Video (work_image, same, time); + Video (work_image, eyes, same, time); time += TIME_HZ / _film->dcp_video_frame_rate(); if (frc.repeat) { - Video (work_image, true, time); + Video (work_image, eyes, true, time); time += TIME_HZ / _film->dcp_video_frame_rate(); } @@ -414,7 +414,7 @@ Player::setup_pieces () if (fc) { shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio)); - fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3)); + fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4)); fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2)); fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4)); @@ -435,7 +435,7 @@ Player::setup_pieces () if (!id) { id.reset (new StillImageDecoder (_film, ic)); - id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3)); + id->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3, _4)); } piece->decoder = id; @@ -479,6 +479,9 @@ Player::content_changed (weak_ptr<Content> w, int property, bool frequent) } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET || property == SubtitleContentProperty::SUBTITLE_SCALE) { update_subtitle (); Changed (frequent); + } else if (property == VideoContentProperty::VIDEO_FRAME_TYPE) { + cout << "vft change.\n"; + Changed (frequent); } } @@ -518,7 +521,7 @@ Player::emit_black () #endif /* XXX: use same here */ - Video (_black_frame, false, _video_position); + Video (_black_frame, EYES_BOTH, false, _video_position); _video_position += _film->video_frames_to_time (1); } diff --git a/src/lib/player.h b/src/lib/player.h index 568c7a7a1..8b28f010d 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -60,10 +60,11 @@ public: /** Emitted when a video frame is ready. * First parameter is the video image. - * Second parameter is true if the image is the same as the last one that was emitted. - * Third parameter is the time. + * Second parameter is the eye(s) that should see this image. + * Third parameter is true if the image is the same as the last one that was emitted. + * Fourth parameter is the time. */ - boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, Time)> Video; + boost::signals2::signal<void (boost::shared_ptr<const Image>, Eyes, bool, Time)> Video; /** Emitted when some audio data is ready */ boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio; @@ -79,7 +80,7 @@ public: private: friend class PlayerWrapper; - void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, bool, VideoContent::Frame); + void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame); void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame); void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time); void setup_pieces (); diff --git a/src/lib/server.cc b/src/lib/server.cc index 9af0883a7..37a076a54 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -105,13 +105,14 @@ Server::process (shared_ptr<Socket> socket) int frame = get_required_int (kv, "frame"); int frames_per_second = get_required_int (kv, "frames_per_second"); int j2k_bandwidth = get_required_int (kv, "j2k_bandwidth"); + Eyes eyes = static_cast<Eyes> (get_required_int (kv, "eyes")); shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, true)); image->read_from_socket (socket); DCPVideoFrame dcp_video_frame ( - image, frame, frames_per_second, j2k_bandwidth, _log + image, frame, eyes, frames_per_second, j2k_bandwidth, _log ); shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally (); diff --git a/src/lib/still_image_decoder.cc b/src/lib/still_image_decoder.cc index 21cc83f54..10e34427b 100644 --- a/src/lib/still_image_decoder.cc +++ b/src/lib/still_image_decoder.cc @@ -34,7 +34,7 @@ using libdcp::Size; StillImageDecoder::StillImageDecoder (shared_ptr<const Film> f, shared_ptr<const StillImageContent> c) : Decoder (f) - , VideoDecoder (f) + , VideoDecoder (f, c) , StillImage (c) { diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 7022965bd..3002ef61c 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -40,11 +40,11 @@ using boost::weak_ptr; using boost::dynamic_pointer_cast; static void -video_proxy (weak_ptr<Encoder> encoder, shared_ptr<const Image> image, bool same) +video_proxy (weak_ptr<Encoder> encoder, shared_ptr<const Image> image, Eyes eyes, bool same) { shared_ptr<Encoder> e = encoder.lock (); if (e) { - e->process_video (image, same); + e->process_video (image, eyes, same); } } @@ -67,7 +67,7 @@ Transcoder::Transcoder (shared_ptr<const Film> f, shared_ptr<Job> j) , _player (f->make_player ()) , _encoder (new Encoder (f, j)) { - _player->Video.connect (bind (video_proxy, _encoder, _1, _2)); + _player->Video.connect (bind (video_proxy, _encoder, _1, _2, _3)); _player->Audio.connect (bind (audio_proxy, _encoder, _1)); } diff --git a/src/lib/types.h b/src/lib/types.h index b1b359810..d6136fc3e 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -40,12 +40,21 @@ enum VideoFrameType VIDEO_FRAME_TYPE_3D_LEFT_RIGHT }; +enum Eyes +{ + EYES_BOTH, + EYES_LEFT, + EYES_RIGHT, + EYES_COUNT +}; + /** @struct Crop * @brief A description of the crop of an image or video. */ struct Crop { Crop () : left (0), right (0), top (0), bottom (0) {} + Crop (int l, int r, int t, int b) : left (l), right (r), top (t), bottom (b) {} /** Number of pixels to remove from the left-hand side */ int left; diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 7eca53c3d..076cd6ec6 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -35,6 +35,7 @@ int const VideoContentProperty::VIDEO_RATIO = 4; using std::string; using std::stringstream; using std::setprecision; +using std::cout; using boost::shared_ptr; using boost::lexical_cast; using boost::optional; @@ -231,7 +232,7 @@ VideoContent::set_video_frame_type (VideoFrameType t) { { boost::mutex::scoped_lock lm (_mutex); - _video_frame_rate = t; + _video_frame_type = t; } signal_changed (VideoContentProperty::VIDEO_FRAME_TYPE); diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 38d5dfcb8..eaa4534e4 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -25,8 +25,9 @@ using std::cout; using boost::shared_ptr; -VideoDecoder::VideoDecoder (shared_ptr<const Film> f) +VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c) : Decoder (f) + , _video_content (c) , _video_position (0) { @@ -35,7 +36,19 @@ VideoDecoder::VideoDecoder (shared_ptr<const Film> f) void VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame) { - Video (image, same, frame); + switch (_video_content->video_frame_type ()) { + case VIDEO_FRAME_TYPE_2D: + Video (image, EYES_BOTH, same, frame); + break; + case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT: + { + int const half = image->size().width / 2; + Video (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame); + Video (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame); + break; + } + } + _video_position = frame + 1; } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 26a11c805..142320a04 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -32,7 +32,7 @@ class Image; class VideoDecoder : public virtual Decoder { public: - VideoDecoder (boost::shared_ptr<const Film>); + VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>); /** Seek so that the next pass() will yield (approximately) the requested frame. * Pass accurate = true to try harder to get close to the request. @@ -41,14 +41,16 @@ public: /** Emitted when a video frame is ready. * First parameter is the video image. - * Second parameter is true if the image is the same as the last one that was emitted. - * Third parameter is the frame within our source. + * Second parameter is the eye(s) which should see this image. + * Third parameter is true if the image is the same as the last one that was emitted for this Eyes value. + * Fourth parameter is the frame within our source. */ - boost::signals2::signal<void (boost::shared_ptr<const Image>, bool, VideoContent::Frame)> Video; + boost::signals2::signal<void (boost::shared_ptr<const Image>, Eyes, bool, VideoContent::Frame)> Video; protected: void video (boost::shared_ptr<const Image>, bool, VideoContent::Frame); + boost::shared_ptr<const VideoContent> _video_content; VideoContent::Frame _video_position; }; diff --git a/src/lib/writer.cc b/src/lib/writer.cc index c5360a122..122dcd716 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -99,7 +99,7 @@ Writer::Writer (shared_ptr<const Film> f, shared_ptr<Job> j) } void -Writer::write (shared_ptr<const EncodedData> encoded, int frame) +Writer::write (shared_ptr<const EncodedData> encoded, int frame, Eyes eyes) { boost::mutex::scoped_lock lock (_mutex); @@ -107,6 +107,7 @@ Writer::write (shared_ptr<const EncodedData> encoded, int frame) qi.type = QueueItem::FULL; qi.encoded = encoded; qi.frame = frame; + qi.eyes = eyes; _queue.push_back (qi); ++_queued_full_in_memory; @@ -114,17 +115,18 @@ Writer::write (shared_ptr<const EncodedData> encoded, int frame) } void -Writer::fake_write (int frame) +Writer::fake_write (int frame, Eyes eyes) { boost::mutex::scoped_lock lock (_mutex); - ifstream ifi (_film->info_path (frame).c_str()); + ifstream ifi (_film->info_path (frame, eyes).c_str()); libdcp::FrameInfo info (ifi); QueueItem qi; qi.type = QueueItem::FAKE; qi.size = info.size; qi.frame = frame; + qi.eyes = eyes; _queue.push_back (qi); _condition.notify_all (); @@ -137,6 +139,23 @@ Writer::write (shared_ptr<const AudioBuffers> audio) _sound_asset_writer->write (audio->data(), audio->frames()); } +/** This must be called from Writer::thread() with an appropriate lock held */ +bool +Writer::have_sequenced_image_at_queue_head () const +{ + if (_queue.empty ()) { + return false; + } + + /* We assume that we will get either all 2D frames or all 3D frames, not a mixture */ + + bool const eyes_ok = (_queue.front().eyes == EYES_BOTH) || + (_queue.front().eyes == EYES_LEFT && _last_written_eyes == EYES_RIGHT) || + (_queue.front().eyes == EYES_RIGHT && _last_written_eyes == EYES_LEFT); + + return _queue.front().frame == (_last_written_frame + 1) && eyes_ok; +} + void Writer::thread () try @@ -149,10 +168,7 @@ try _queue.sort (); - if (_finish || - _queued_full_in_memory > _maximum_frames_in_memory || - (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1))) { - + if (_finish || _queued_full_in_memory > _maximum_frames_in_memory || have_sequenced_image_at_queue_head ()) { break; } @@ -166,7 +182,7 @@ try } /* Write any frames that we can write; i.e. those that are in sequence */ - while (!_queue.empty() && _queue.front().frame == (_last_written_frame + 1)) { + while (have_sequenced_image_at_queue_head ()) { QueueItem qi = _queue.front (); _queue.pop_front (); if (qi.type == QueueItem::FULL && qi.encoded) { @@ -179,10 +195,10 @@ try { _film->log()->log (String::compose (N_("Writer FULL-writes %1 to MXF"), qi.frame)); if (!qi.encoded) { - qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, false))); + qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false))); } libdcp::FrameInfo const fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size()); - qi.encoded->write_info (_film, qi.frame, fin); + qi.encoded->write_info (_film, qi.frame, qi.eyes, fin); _last_written = qi.encoded; ++_full_written; break; @@ -197,7 +213,7 @@ try { _film->log()->log (String::compose (N_("Writer REPEAT-writes %1 to MXF"), qi.frame)); libdcp::FrameInfo const fin = _picture_asset_writer->write (_last_written->data(), _last_written->size()); - _last_written->write_info (_film, qi.frame, fin); + _last_written->write_info (_film, qi.frame, qi.eyes, fin); ++_repeat_written; break; } @@ -231,7 +247,7 @@ try lock.unlock (); _film->log()->log (String::compose (N_("Writer full (awaiting %1); pushes %2 to disk"), _last_written_frame + 1, qi.frame)); - qi.encoded->write (_film, qi.frame); + qi.encoded->write (_film, qi.frame, qi.eyes); lock.lock (); qi.encoded.reset (); --_queued_full_in_memory; @@ -324,19 +340,44 @@ Writer::finish () /** Tell the writer that frame `f' should be a repeat of the frame before it */ void -Writer::repeat (int f) +Writer::repeat (int f, Eyes e) { boost::mutex::scoped_lock lock (_mutex); QueueItem qi; qi.type = QueueItem::REPEAT; qi.frame = f; + qi.eyes = e; _queue.push_back (qi); _condition.notify_all (); } +bool +Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes) +{ + /* Read the frame info as written */ + ifstream ifi (_film->info_path (f, eyes).c_str()); + libdcp::FrameInfo info (ifi); + + /* Read the data from the MXF and hash it */ + fseek (mxf, info.offset, SEEK_SET); + EncodedData data (info.size); + size_t const read = fread (data.data(), 1, data.size(), mxf); + if (read != static_cast<size_t> (data.size ())) { + _film->log()->log (String::compose ("Existing frame %1 is incomplete", f)); + return false; + } + + string const existing_hash = md5_digest (data.data(), data.size()); + if (existing_hash != info.hash) { + _film->log()->log (String::compose ("Existing frame %1 failed hash check", f)); + return false; + } + + return true; +} void Writer::check_existing_picture_mxf () @@ -353,23 +394,17 @@ Writer::check_existing_picture_mxf () while (1) { - /* Read the frame info as written */ - ifstream ifi (_film->info_path (_first_nonexistant_frame).c_str()); - libdcp::FrameInfo info (ifi); - - /* Read the data from the MXF and hash it */ - fseek (mxf, info.offset, SEEK_SET); - EncodedData data (info.size); - size_t const read = fread (data.data(), 1, data.size(), mxf); - if (read != static_cast<size_t> (data.size ())) { - _film->log()->log (String::compose ("Existing frame %1 is incomplete", _first_nonexistant_frame)); - break; - } - - string const existing_hash = md5_digest (data.data(), data.size()); - if (existing_hash != info.hash) { - _film->log()->log (String::compose ("Existing frame %1 failed hash check", _first_nonexistant_frame)); - break; + if (_film->dcp_3d ()) { + if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_LEFT)) { + break; + } + if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_RIGHT)) { + break; + } + } else { + if (!check_existing_picture_mxf_frame (mxf, _first_nonexistant_frame, EYES_BOTH)) { + break; + } } _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame)); @@ -394,11 +429,15 @@ Writer::can_fake_write (int frame) const bool operator< (QueueItem const & a, QueueItem const & b) { - return a.frame < b.frame; + if (a.frame != b.frame) { + return a.frame < b.frame; + } + + return a.eyes == EYES_LEFT && b.eyes == EYES_RIGHT; } bool operator== (QueueItem const & a, QueueItem const & b) { - return a.frame == b.frame; + return a.frame == b.frame && a.eyes == b.eyes; } diff --git a/src/lib/writer.h b/src/lib/writer.h index 1e5d86489..023107d97 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -22,6 +22,7 @@ #include <boost/thread.hpp> #include <boost/thread/condition.hpp> #include "exceptions.h" +#include "types.h" class Film; class EncodedData; @@ -56,6 +57,7 @@ public: int size; /** frame index */ int frame; + Eyes eyes; }; bool operator< (QueueItem const & a, QueueItem const & b); @@ -68,16 +70,18 @@ public: bool can_fake_write (int) const; - void write (boost::shared_ptr<const EncodedData>, int); - void fake_write (int); + void write (boost::shared_ptr<const EncodedData>, int, Eyes); + void fake_write (int, Eyes); void write (boost::shared_ptr<const AudioBuffers>); - void repeat (int f); + void repeat (int f, Eyes); void finish (); private: void thread (); void check_existing_picture_mxf (); + bool check_existing_picture_mxf_frame (FILE *, int, Eyes); + bool have_sequenced_image_at_queue_head () const; /** our Film */ boost::shared_ptr<const Film> _film; @@ -101,6 +105,7 @@ private: boost::shared_ptr<const EncodedData> _last_written; /** the index of the last written frame */ int _last_written_frame; + Eyes _last_written_eyes; /** maximum number of frames to hold in memory, for when we are managing ordering */ |
