Merge master.
[dcpomatic.git] / src / lib / player.cc
index 8902929cc5f33616d15da6a34ff20434633ae75e..ab0d8f3566ecb484ec491ba6314ff9cbbdd5f24b 100644 (file)
 #include "playlist.h"
 #include "job.h"
 #include "image.h"
+#include "image_proxy.h"
 #include "ratio.h"
 #include "log.h"
 #include "scaler.h"
 #include "render_subtitles.h"
-#include "dcp_video.h"
 #include "config.h"
 #include "content_video.h"
+#include "player_video_frame.h"
 
 using std::list;
 using std::cout;
@@ -218,7 +219,7 @@ Player::film_changed (Film::Property p)
 }
 
 list<PositionImage>
-Player::process_content_image_subtitles (shared_ptr<SubtitleContent> content, list<shared_ptr<ContentImageSubtitle> > subs)
+Player::process_content_image_subtitles (shared_ptr<SubtitleContent> content, list<shared_ptr<ContentImageSubtitle> > subs) const
 {
        list<PositionImage> all;
        
@@ -269,7 +270,7 @@ Player::process_content_image_subtitles (shared_ptr<SubtitleContent> content, li
 }
 
 list<PositionImage>
-Player::process_content_text_subtitles (list<shared_ptr<ContentTextSubtitle> > sub)
+Player::process_content_text_subtitles (list<shared_ptr<ContentTextSubtitle> > sub) const
 {
        list<PositionImage> all;
        for (list<shared_ptr<ContentTextSubtitle> >::const_iterator i = sub.begin(); i != sub.end(); ++i) {
@@ -287,65 +288,50 @@ Player::set_approximate_size ()
        _approximate_size = true;
 }
 
-shared_ptr<DCPVideo>
-Player::get_video (DCPTime time, bool accurate)
+shared_ptr<PlayerVideoFrame>
+Player::black_player_video_frame () const
 {
-       if (!_have_valid_pieces) {
-               setup_pieces ();
-       }
-       
-       list<shared_ptr<Piece> > ov = overlaps<VideoContent> (time);
-       if (ov.empty ()) {
-               /* No video content at this time: return a black frame */
-               return shared_ptr<DCPVideo> (
-                       new DCPVideo (
-                               _black_image,
-                               EYES_BOTH,
-                               Crop (),
-                               _video_container_size,
-                               _video_container_size,
-                               Scaler::from_id ("bicubic"),
-                               Config::instance()->colour_conversions().front().conversion,
-                               time
-                               )
-                       );
-       }
-
-       /* Create a DCPVideo from the content's video at this time */
-
-       shared_ptr<Piece> piece = ov.back ();
-       shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
-       assert (decoder);
-       shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
-       assert (content);
-
-       shared_ptr<ContentVideo> dec = decoder->get_video (dcp_to_content_video (piece, time), accurate);
-
-       dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
-       if (_approximate_size) {
-               image_size.width &= ~3;
-               image_size.height &= ~3;
-       }
+       return shared_ptr<PlayerVideoFrame> (
+               new PlayerVideoFrame (
+                       shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
+                       Crop (),
+                       _video_container_size,
+                       _video_container_size,
+                       Scaler::from_id ("bicubic"),
+                       EYES_BOTH,
+                       PART_WHOLE,
+                       Config::instance()->colour_conversions().front().conversion
+               )
+       );
+}
 
-       shared_ptr<DCPVideo> dcp_video (
-               new DCPVideo (
-                       dec->image,
-                       dec->eyes,
+shared_ptr<PlayerVideoFrame>
+Player::content_to_player_video_frame (
+       shared_ptr<VideoContent> content,
+       ContentVideo content_video,
+       list<shared_ptr<Piece> > subs,
+       DCPTime time,
+       dcp::Size image_size) const
+{
+       shared_ptr<PlayerVideoFrame> pvf (
+               new PlayerVideoFrame (
+                       content_video.image,
                        content->crop (),
                        image_size,
                        _video_container_size,
                        _film->scaler(),
-                       content->colour_conversion (),
-                       time
+                       content_video.eyes,
+                       content_video.part,
+                       content->colour_conversion ()
                        )
                );
-
+       
+       
        /* Add subtitles */
-
-       ov = overlaps<SubtitleContent> (time);
+       
        list<PositionImage> sub_images;
        
-       for (list<shared_ptr<Piece> >::const_iterator i = ov.begin(); i != ov.end(); ++i) {
+       for (list<shared_ptr<Piece> >::const_iterator i = subs.begin(); i != subs.end(); ++i) {
                shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*i)->decoder);
                shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*i)->content);
                ContentTime const from = dcp_to_content_subtitle (*i, time);
@@ -357,10 +343,10 @@ Player::get_video (DCPTime time, bool accurate)
                                subtitle_content,
                                image_subtitles
                                );
-
+                       
                        copy (im.begin(), im.end(), back_inserter (sub_images));
                }
-
+               
                if (_burn_subtitles) {
                        list<shared_ptr<ContentTextSubtitle> > text_subtitles = subtitle_decoder->get_text_subtitles (from, to);
                        if (!text_subtitles.empty ()) {
@@ -369,12 +355,65 @@ Player::get_video (DCPTime time, bool accurate)
                        }
                }
        }
-
+       
        if (!sub_images.empty ()) {
-               dcp_video->set_subtitle (merge (sub_images));
+               pvf->set_subtitle (merge (sub_images));
+       }
+
+       return pvf;
+}
+
+/** @return All PlayerVideoFrames at the given time (there may be two frames for 3D) */
+list<shared_ptr<PlayerVideoFrame> >
+Player::get_video (DCPTime time, bool accurate)
+{
+       if (!_have_valid_pieces) {
+               setup_pieces ();
+       }
+       
+       list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
+               time,
+               time + DCPTime::from_frames (1, _film->video_frame_rate ())
+               );
+
+       list<shared_ptr<PlayerVideoFrame> > pvf;
+               
+       if (ov.empty ()) {
+               /* No video content at this time */
+               pvf.push_back (black_player_video_frame ());
+               return pvf;
        }
 
-       return dcp_video;
+       /* Create a PlayerVideoFrame from the content's video at this time */
+
+       shared_ptr<Piece> piece = ov.back ();
+       shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
+       assert (decoder);
+       shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
+       assert (content);
+
+       list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
+       if (content_video.empty ()) {
+               pvf.push_back (black_player_video_frame ());
+               return pvf;
+       }
+
+       dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
+       if (_approximate_size) {
+               image_size.width &= ~3;
+               image_size.height &= ~3;
+       }
+
+       for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
+               list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (
+                       time,
+                       time + DCPTime::from_frames (1, _film->video_frame_rate ())
+                       );
+               
+               pvf.push_back (content_to_player_video_frame (content, *i, subs, time, image_size));
+       }
+               
+       return pvf;
 }
 
 shared_ptr<AudioBuffers>
@@ -389,7 +428,7 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate)
        shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
        audio->make_silent ();
        
-       list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time);
+       list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
        if (ov.empty ()) {
                return audio;
        }
@@ -401,10 +440,28 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate)
                shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
                assert (decoder);
 
-               AudioFrame const content_time = dcp_to_content_audio (*i, time);
+               if (content->audio_frame_rate() == 0) {
+                       /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
+                        * audio stream).
+                        */
+                       continue;
+               }
+
+               /* The time that we should request from the content */
+               DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
+               DCPTime offset;
+               if (request < DCPTime ()) {
+                       /* We went off the start of the content, so we will need to offset
+                          the stuff we get back.
+                       */
+                       offset = -request;
+                       request = DCPTime ();
+               }
 
-               /* Audio from this piece's decoder (which might be more than what we asked for) */
-               shared_ptr<ContentAudio> all = decoder->get_audio (content_time, length_frames, accurate);
+               AudioFrame const content_frame = dcp_to_content_audio (*i, request);
+
+               /* Audio from this piece's decoder (which might be more or less than what we asked for) */
+               shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
 
                /* Gain */
                if (content->audio_gain() != 0) {
@@ -431,25 +488,13 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate)
                }
                
                all->audio = dcp_mapped;
-               
-               /* Delay */
-               /* XXX
-               audio->dcp_time += content->audio_delay() * TIME_HZ / 1000;
-               if (audio->dcp_time < 0) {
-                       int const frames = - audio->dcp_time * _film->audio_frame_rate() / TIME_HZ;
-                       if (frames >= audio->audio->frames ()) {
-                               return;
-                       }
-                       
-                       shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->audio->channels(), audio->audio->frames() - frames));
-                       trimmed->copy_from (audio->audio.get(), audio->audio->frames() - frames, frames, 0);
-                       
-                       audio->audio = trimmed;
-                       audio->dcp_time = 0;
-               }
-               */
 
-               audio->accumulate_frames (all->audio.get(), all->frame - content_time, 0, min (AudioFrame (all->audio->frames()), length_frames));
+               audio->accumulate_frames (
+                       all->audio.get(),
+                       content_frame - all->frame,
+                       offset.frames (_film->audio_frame_rate()),
+                       min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
+                       );
        }
 
        return audio;