Merge remote-tracking branch 'origin/main' into v2.17.x
authorCarl Hetherington <cth@carlh.net>
Fri, 29 Mar 2024 22:23:10 +0000 (23:23 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 29 Mar 2024 22:23:10 +0000 (23:23 +0100)
1  2 
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_examiner.cc
src/lib/video_content.cc
src/lib/video_content.h
wscript

index 8baa40c1b63dfcf6252348900f67bb3a3d90e42a,4a7c87b341e1046e0efbc4cabe965626d68ce01e..c80bdec69fef072cc2aa07f48e88a48aec73004b
@@@ -197,61 -197,61 +197,61 @@@ FFmpegContent::FFmpegContent (vector<sh
  
  
  void
 -FFmpegContent::as_xml (xmlpp::Node* node, bool with_paths) const
 +FFmpegContent::as_xml(xmlpp::Element* element, bool with_paths) const
  {
 -      node->add_child("Type")->add_child_text("FFmpeg");
 -      Content::as_xml (node, with_paths);
 +      cxml::add_text_child(element, "Type", "FFmpeg");
 +      Content::as_xml(element, with_paths);
  
        if (video) {
 -              video->as_xml (node);
 +              video->as_xml(element);
        }
  
        if (audio) {
 -              audio->as_xml (node);
 +              audio->as_xml(element);
  
                for (auto i: audio->streams()) {
                        auto f = dynamic_pointer_cast<FFmpegAudioStream> (i);
                        DCPOMATIC_ASSERT (f);
 -                      f->as_xml (node->add_child("AudioStream"));
 +                      f->as_xml(cxml::add_child(element, "AudioStream"));
                }
        }
  
        if (only_text()) {
 -              only_text()->as_xml (node);
 +              only_text()->as_xml(element);
        }
  
        boost::mutex::scoped_lock lm (_mutex);
  
        for (auto i: _subtitle_streams) {
 -              auto t = node->add_child("SubtitleStream");
 +              auto t = cxml::add_child(element, "SubtitleStream");
                if (_subtitle_stream && i == _subtitle_stream) {
 -                      t->add_child("Selected")->add_child_text("1");
 +                      cxml::add_text_child(t, "Selected", "1");
                }
                i->as_xml (t);
        }
  
        for (auto i: _filters) {
 -              node->add_child("Filter")->add_child_text(i.id());
 +              cxml::add_text_child(element, "Filter", i.id());
        }
  
        if (_first_video) {
 -              node->add_child("FirstVideo")->add_child_text(raw_convert<string>(_first_video.get().get()));
 +              cxml::add_text_child(element, "FirstVideo", raw_convert<string>(_first_video.get().get()));
        }
  
        if (_color_range) {
 -              node->add_child("ColorRange")->add_child_text(raw_convert<string>(static_cast<int>(*_color_range)));
 +              cxml::add_text_child(element, "ColorRange", raw_convert<string>(static_cast<int>(*_color_range)));
        }
        if (_color_primaries) {
 -              node->add_child("ColorPrimaries")->add_child_text(raw_convert<string>(static_cast<int>(*_color_primaries)));
 +              cxml::add_text_child(element, "ColorPrimaries", raw_convert<string>(static_cast<int>(*_color_primaries)));
        }
        if (_color_trc) {
 -              node->add_child("ColorTransferCharacteristic")->add_child_text(raw_convert<string>(static_cast<int>(*_color_trc)));
 +              cxml::add_text_child(element, "ColorTransferCharacteristic", raw_convert<string>(static_cast<int>(*_color_trc)));
        }
        if (_colorspace) {
 -              node->add_child("Colorspace")->add_child_text(raw_convert<string>(static_cast<int>(*_colorspace)));
 +              cxml::add_text_child(element, "Colorspace", raw_convert<string>(static_cast<int>(*_colorspace)));
        }
        if (_bits_per_pixel) {
 -              node->add_child("BitsPerPixel")->add_child_text(raw_convert<string>(*_bits_per_pixel));
 +              cxml::add_text_child(element, "BitsPerPixel", raw_convert<string>(*_bits_per_pixel));
        }
  }
  
@@@ -295,8 -295,10 +295,10 @@@ FFmpegContent::examine (shared_ptr<cons
                                        _filters.push_back(*Filter::from_id("hflip"));
                                } else if (fabs (rot - 90) < 1.0) {
                                        _filters.push_back(*Filter::from_id("90clock"));
+                                       video->rotate_size();
                                } else if (fabs (rot - 270) < 1.0) {
                                        _filters.push_back(*Filter::from_id("90anticlock"));
+                                       video->rotate_size();
                                }
                        }
                        if (examiner->has_alpha()) {
index 583ea129725eadee1e09e162bcacb3d00ffaf401,787198e4a477417570880bfdfe11bc8322ccf017..f7d3f9df334b8d92f00c93adc456c08b587cec30
@@@ -73,6 -73,14 +73,6 @@@ FFmpegExaminer::FFmpegExaminer (shared_
                auto codec = _codec_context[i] ? _codec_context[i]->codec : nullptr;
                if (s->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && codec) {
  
 -                      /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up,
 -                         so bodge it here.  No idea why we should have to do this.
 -                      */
 -
 -                      if (s->codecpar->channel_layout == 0) {
 -                              s->codecpar->channel_layout = av_get_default_channel_layout (s->codecpar->channels);
 -                      }
 -
                        DCPOMATIC_ASSERT (_format_context->duration != AV_NOPTS_VALUE);
                        DCPOMATIC_ASSERT (codec->name);
  
@@@ -83,7 -91,7 +83,7 @@@
                                        s->id,
                                        s->codecpar->sample_rate,
                                        llrint ((double(_format_context->duration) / AV_TIME_BASE) * s->codecpar->sample_rate),
 -                                      s->codecpar->channels,
 +                                      s->codecpar->ch_layout.nb_channels,
                                        s->codecpar->bits_per_raw_sample ? s->codecpar->bits_per_raw_sample : s->codecpar->bits_per_coded_sample
                                        )
                                );
  
                av_packet_free (&packet);
  
 -              if (_first_video && got_all_audio && temporal_reference.size() >= (PULLDOWN_CHECK_FRAMES * 2)) {
 +              if (got_all_audio && (!_video_stream || (_first_video && temporal_reference.size() >= (PULLDOWN_CHECK_FRAMES * 2)))) {
                        /* All done */
                        break;
                }
                /* This code taken from get_rotation() in ffmpeg:cmdutils.c */
                auto stream = _format_context->streams[*_video_stream];
                auto rotate_tag = av_dict_get (stream->metadata, "rotate", 0, 0);
-               _rotation = 0;
 -              uint8_t* displaymatrix = av_stream_get_side_data (stream, AV_PKT_DATA_DISPLAYMATRIX, 0);
--
                if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) {
                        char *tail;
                        _rotation = av_strtod (rotate_tag->value, &tail);
                        }
                }
  
 -              if (displaymatrix && !_rotation) {
 -                      _rotation = - av_display_rotation_get ((int32_t*) displaymatrix);
 +              auto side_data = av_packet_side_data_get(stream->codecpar->coded_side_data, stream->codecpar->nb_coded_side_data, AV_PKT_DATA_DISPLAYMATRIX);
 +              if (side_data && !_rotation) {
 +                      _rotation = - av_display_rotation_get(reinterpret_cast<int32_t*>(side_data->data));
                }
  
                _rotation = *_rotation - 360 * floor (*_rotation / 360 + 0.9 / 360);
@@@ -244,7 -251,7 +242,7 @@@ FFmpegExaminer::video_packet (AVCodecCo
                        ).get_value_or (ContentTime ()).frames_round (video_frame_rate().get ());
        }
        if (temporal_reference.size() < (PULLDOWN_CHECK_FRAMES * 2)) {
 -              temporal_reference += (_video_frame->top_field_first ? "T" : "B");
 +              temporal_reference += ((_video_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? "T" : "B");
                temporal_reference += (_video_frame->repeat_pict ? "3" : "2");
        }
  
diff --combined src/lib/video_content.cc
index e84462ed28baa06177e2ef2778c887fa48d60c96,6c027ff114ceaae500adbb3d2c340f94753a2d37..10dd5ff1e27e1a5782d44f7540d5395e18795034
@@@ -273,37 -273,37 +273,37 @@@ VideoContent::VideoContent (Content* pa
  
  
  void
 -VideoContent::as_xml (xmlpp::Node* node) const
 +VideoContent::as_xml(xmlpp::Element* element) const
  {
        boost::mutex::scoped_lock lm (_mutex);
 -      node->add_child("Use")->add_child_text (_use ? "1" : "0");
 -      node->add_child("VideoLength")->add_child_text (raw_convert<string> (_length));
 +      cxml::add_text_child(element, "Use", _use ? "1" : "0");
 +      cxml::add_text_child(element, "VideoLength", raw_convert<string>(_length));
        if (_size) {
 -              node->add_child("VideoWidth")->add_child_text(raw_convert<string>(_size->width));
 -              node->add_child("VideoHeight")->add_child_text(raw_convert<string>(_size->height));
 +              cxml::add_text_child(element, "VideoWidth", raw_convert<string>(_size->width));
 +              cxml::add_text_child(element, "VideoHeight", raw_convert<string>(_size->height));
        }
 -      node->add_child("VideoFrameType")->add_child_text (video_frame_type_to_string (_frame_type));
 +      cxml::add_text_child(element, "VideoFrameType", video_frame_type_to_string(_frame_type));
        if (_sample_aspect_ratio) {
 -              node->add_child("SampleAspectRatio")->add_child_text (raw_convert<string> (_sample_aspect_ratio.get ()));
 +              cxml::add_text_child(element, "SampleAspectRatio", raw_convert<string> (_sample_aspect_ratio.get ()));
        }
 -      _crop.as_xml (node);
 +      _crop.as_xml(element);
        if (_custom_ratio) {
 -              node->add_child("CustomRatio")->add_child_text(raw_convert<string>(*_custom_ratio));
 +              cxml::add_text_child(element, "CustomRatio", raw_convert<string>(*_custom_ratio));
        }
        if (_custom_size) {
 -              node->add_child("CustomWidth")->add_child_text(raw_convert<string>(_custom_size->width));
 -              node->add_child("CustomHeight")->add_child_text(raw_convert<string>(_custom_size->height));
 +              cxml::add_text_child(element, "CustomWidth", raw_convert<string>(_custom_size->width));
 +              cxml::add_text_child(element, "CustomHeight", raw_convert<string>(_custom_size->height));
        }
        if (_colour_conversion) {
 -              _colour_conversion.get().as_xml (node->add_child("ColourConversion"));
 +              _colour_conversion.get().as_xml(cxml::add_child(element, "ColourConversion"));
        }
 -      node->add_child("YUV")->add_child_text (_yuv ? "1" : "0");
 -      node->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in));
 -      node->add_child("FadeOut")->add_child_text (raw_convert<string> (_fade_out));
 -      node->add_child("Range")->add_child_text(_range == VideoRange::FULL ? "full" : "video");
 -      _pixel_quanta.as_xml(node->add_child("PixelQuanta"));
 +      cxml::add_text_child(element, "YUV", _yuv ? "1" : "0");
 +      cxml::add_text_child(element, "FadeIn", raw_convert<string>(_fade_in));
 +      cxml::add_text_child(element, "FadeOut", raw_convert<string>(_fade_out));
 +      cxml::add_text_child(element, "Range", _range == VideoRange::FULL ? "full" : "video");
 +      _pixel_quanta.as_xml(cxml::add_child(element, "PixelQuanta"));
        if (_burnt_subtitle_language) {
 -              node->add_child("BurntSubtitleLanguage")->add_child_text(_burnt_subtitle_language->to_string());
 +              cxml::add_text_child(element, "BurntSubtitleLanguage", _burnt_subtitle_language->to_string());
        }
  }
  
@@@ -424,29 -424,27 +424,29 @@@ VideoContent::size_after_crop () cons
  }
  
  
 -/** @param f Frame index within the whole (untrimmed) content.
 +/** @param time Time within the whole (untrimmed) content.
   *  @return Fade factor (between 0 and 1) or unset if there is no fade.
   */
  optional<double>
 -VideoContent::fade (shared_ptr<const Film> film, Frame f) const
 +VideoContent::fade(shared_ptr<const Film> film, ContentTime time) const
  {
 -      DCPOMATIC_ASSERT (f >= 0);
 +      DCPOMATIC_ASSERT(time.get() >= 0);
  
        double const vfr = _parent->active_video_frame_rate(film);
  
 -      auto const ts = _parent->trim_start().frames_round(vfr);
 -      if ((f - ts) < fade_in()) {
 -              return double (f - ts) / fade_in();
 +      auto const ts = _parent->trim_start();
 +      auto const fade_in_time = ContentTime::from_frames(fade_in(), vfr);
 +      if ((time - ts) < fade_in_time) {
 +              return double(ContentTime(time - ts).get()) / fade_in_time.get();
        }
  
 -      auto fade_out_start = length() - _parent->trim_end().frames_round(vfr) - fade_out();
 -      if (f >= fade_out_start) {
 -              return 1 - double (f - fade_out_start) / fade_out();
 +      auto const fade_out_time = ContentTime::from_frames(fade_out(), vfr);
 +      auto fade_out_start = ContentTime::from_frames(length(), vfr) - _parent->trim_end() - fade_out_time;
 +      if (time >= fade_out_start) {
 +              return 1 - double(ContentTime(time - fade_out_start).get()) / fade_out_time.get();
        }
  
 -      return optional<double> ();
 +      return {};
  }
  
  string
@@@ -724,3 -722,12 +724,12 @@@ VideoContent::actual_crop () cons
        );
  }
  
+ void
+ VideoContent::rotate_size()
+ {
+       if (_size) {
+               std::swap(_size->width, _size->height);
+       }
+ }
diff --combined src/lib/video_content.h
index a7b630b14a0c86df93936a96e60c50c4a9838770,495d000e160401a24059a4f355a92df3acc90f04..eb106cc75877cb7186da74f545dc210e265c8afb
@@@ -66,7 -66,7 +66,7 @@@ public
        VideoContent (Content* parent, cxml::ConstNodePtr node, int version, VideoRange video_range_hint);
        VideoContent (Content* parent, std::vector<std::shared_ptr<Content>>);
  
 -      void as_xml (xmlpp::Node *) const;
 +      void as_xml(xmlpp::Element*) const;
        std::string technical_summary () const;
        std::string identifier () const;
        void take_settings_from (std::shared_ptr<const VideoContent> c);
        boost::optional<dcp::Size> size_after_crop() const;
        boost::optional<dcp::Size> scaled_size(dcp::Size container_size);
  
 -      boost::optional<double> fade (std::shared_ptr<const Film> film, Frame) const;
 +      boost::optional<double> fade(std::shared_ptr<const Film> film, dcpomatic::ContentTime time) const;
  
        std::string processing_description (std::shared_ptr<const Film> film);
  
        void modify_position (std::shared_ptr<const Film> film, dcpomatic::DCPTime& pos) const;
        void modify_trim_start (dcpomatic::ContentTime& pos) const;
  
+       void rotate_size();
        static std::shared_ptr<VideoContent> from_xml (Content* parent, cxml::ConstNodePtr node, int version, VideoRange video_range_hint);
  
  private:
diff --combined wscript
index fb2598dc9ae2acc9ab2baace71f6a1dcd216f402,f6f73a52a132701aa667dfe169270935e817681e..19fd4260c2c6c9a6a7598d8ff2c54e64500c82e6
+++ b/wscript
@@@ -61,7 -61,6 +61,6 @@@ def options(opt)
      opt.add_option('--disable-tests',     action='store_true', default=False, help='disable building of tests')
      opt.add_option('--target-windows-64', action='store_true', default=False, help='set up to do a cross-compile for Windows 64-bit')
      opt.add_option('--target-windows-32', action='store_true', default=False, help='set up to do a cross-compile for Windows 32-bit')
-     opt.add_option('--target-macos-arm64', action='store_true', default=False, help='set up to do a cross-compile for macOS arm64')
      opt.add_option('--static-dcpomatic',  action='store_true', default=False, help='link to components of DCP-o-matic statically')
      opt.add_option('--static-boost',      action='store_true', default=False, help='link statically to Boost')
      opt.add_option('--static-wxwidgets',  action='store_true', default=False, help='link statically to wxWidgets')
      opt.add_option('--workaround-gssapi', action='store_true', default=False, help='link to gssapi_krb5')
      opt.add_option('--use-lld',           action='store_true', default=False, help='use lld linker')
      opt.add_option('--enable-disk',       action='store_true', default=False, help='build dcpomatic2_disk tool; requires Boost process, lwext4 and nanomsg libraries')
 +    opt.add_option('--enable-grok',       action='store_true', default=False, help='build with support for grok J2K encoder')
      opt.add_option('--warnings-are-errors', action='store_true', default=False, help='build with -Werror')
      opt.add_option('--wx-config',         help='path to wx-config')
      opt.add_option('--enable-asan',       action='store_true', help='build with asan')
      opt.add_option('--disable-more-warnings', action='store_true', default=False, help='disable some warnings raised by Xcode 15 with the 2.16 branch')
 +    opt.add_option('--c++17', action='store_true', default=False, help='build with C++17 and libxml++-4.0')
  
  def configure(conf):
      conf.load('compiler_cxx')
      if conf.options.target_windows_64 or conf.options.target_windows_32:
          conf.load('winres')
  
 +    if vars(conf.options)['c++17']:
 +        cpp_std = '17'
 +        conf.env.XMLPP_API = '4.0'
 +        conf.env.PANGOMM_API = '2.48'
 +        conf.env.CAIROMM_API = '1.16'
 +    else:
 +        cpp_std = '11'
 +        conf.env.XMLPP_API = '2.6'
 +        conf.env.PANGOMM_API = '1.4'
 +        conf.env.CAIROMM_API = '1.0'
 +
      # Save conf.options that we need elsewhere in conf.env
      conf.env.DISABLE_GUI = conf.options.disable_gui
      conf.env.DISABLE_TESTS = conf.options.disable_tests
      conf.env.DEBUG = conf.options.enable_debug
      conf.env.STATIC_DCPOMATIC = conf.options.static_dcpomatic
      conf.env.ENABLE_DISK = conf.options.enable_disk
 +    conf.env.ENABLE_GROK = conf.options.enable_grok
      if conf.options.destdir == '':
          conf.env.INSTALL_PREFIX = conf.options.prefix
      else:
          conf.env.INSTALL_PREFIX = conf.options.destdir
  
+     conf.check_cxx(cxxflags=['-msse', '-mfpmath=sse'], msg='Checking for SSE support', mandatory=False, define_name='SSE')
      # Common CXXFLAGS
      conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS',
                                         '-D__STDC_LIMIT_MACROS',
                                         '-Wall',
                                         '-Wextra',
                                         '-Wwrite-strings',
 +                                       # getMessengerLogger() in the grok code triggers these warnings
 +                                       '-Wno-nonnull',
                                         '-Wno-error=deprecated',
                                         # I tried and failed to ignore these with _Pragma
                                         '-Wno-ignored-qualifiers',
                                         '-D_FILE_OFFSET_BITS=64',
 -                                       '-std=c++11'])
 +                                       '-std=c++' + cpp_std])
  
      if conf.options.disable_more_warnings:
          # These are for Xcode 15.0.1 with the v2.16.x-era
      if conf.options.warnings_are_errors:
          conf.env.append_value('CXXFLAGS', '-Werror')
  
-     if not conf.options.target_macos_arm64:
-         conf.env.append_value('CXXFLAGS', '-msse')
+     if conf.env.SSE:
+         conf.env.append_value('CXXFLAGS', ['-msse', '-mfpmath=sse'])
  
      if conf.options.enable_asan:
          conf.env.append_value('CXXFLAGS', '-fsanitize=address')
              conf.env.append_value('CXXFLAGS', ['-Wno-cast-function-type'])
          # Most gccs still give these warnings from boost::optional
          conf.env.append_value('CXXFLAGS', ['-Wno-maybe-uninitialized'])
 -        if int(gcc[0]) > 4:
 +        if int(gcc[0]) > 8:
              # gcc 4.8.5 on Centos 7 does not have this warning
 +            # gcc 7.5.0 on Ubuntu 18.04 and gcc 8.3.0 on Debian 10 do, but
 +            # I didn't manage to turn it back off again with a pragma
              conf.env.append_value('CXXFLAGS', ['-Wsuggest-override'])
  
      if conf.options.enable_debug:
      if conf.options.enable_disk:
          conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_DISK')
  
 +    if conf.options.enable_grok:
 +        conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GROK')
 +
      if conf.options.use_lld:
          try:
              conf.find_program('ld.lld')
          conf.env.append_value('CXXFLAGS', '-DWIN32_LEAN_AND_MEAN')
          conf.env.append_value('CXXFLAGS', '-DBOOST_USE_WINDOWS_H')
          conf.env.append_value('CXXFLAGS', '-DBOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN')
-         conf.env.append_value('CXXFLAGS', '-mfpmath=sse')
          conf.env.append_value('CXXFLAGS', '-Wcast-align')
          wxrc = os.popen('wx-config --rescomp').read().split()[1:]
          conf.env.append_value('WINRCFLAGS', wxrc)
  
      # Linux
      if conf.env.TARGET_LINUX:
-         conf.env.append_value('CXXFLAGS', '-mfpmath=sse')
          conf.env.append_value('CXXFLAGS', '-DLINUX_LOCALE_PREFIX="%s/share/locale"' % conf.env['INSTALL_PREFIX'])
          conf.env.append_value('CXXFLAGS', '-DLINUX_SHARE_PREFIX="%s/share"' % conf.env['INSTALL_PREFIX'])
          conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_LINUX')
      conf.check_cfg(package='fontconfig', args='--cflags --libs', uselib_store='FONTCONFIG', mandatory=True)
  
      # pangomm
 -    conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
 +    conf.check_cfg(package='pangomm-' + conf.env.PANGOMM_API, args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
  
      # cairomm
 -    conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
 +    conf.check_cfg(package='cairomm-' + conf.env.CAIROMM_API, args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
  
      # leqm_nrt
      conf.check_cfg(package='leqm_nrt', args='--cflags --libs', uselib_store='LEQM_NRT', mandatory=True)
      if conf.options.static_dcp:
          conf.check_cfg(package='libdcp-1.0', args='libdcp-1.0 >= %s --cflags' % libdcp_version, uselib_store='DCP', mandatory=True)
          conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
 -        conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-carl', 'kumu-carl', 'openjp2']
 +        conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-dcpomatic', 'kumu-dcpomatic', 'openjp2']
          conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt', 'xerces-c']
      else:
          conf.check_cfg(package='libdcp-1.0', args='libdcp-1.0 >= %s --cflags --libs' % libdcp_version, uselib_store='DCP', mandatory=True)
  
      # libxml++
      if conf.options.static_xmlpp:
 -        conf.env.STLIB_XMLPP = ['xml++-2.6']
 +        conf.env.STLIB_XMLPP = ['xml++-' + conf.env.XMLPP_API]
          conf.env.LIB_XMLPP = ['xml2']
      else:
 -        conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XMLPP', mandatory=True)
 +        conf.check_cfg(package='libxml++-' + conf.env.XMLPP_API, args='--cflags --libs', uselib_store='XMLPP', mandatory=True)
  
      # libxmlsec
      if conf.options.static_xmlsec:
                              int main () { av_ebur128_get_true_peaks (0); }\n
                              """,
                     msg='Checking for EBUR128-patched FFmpeg',
 -                   uselib='AVCODEC AVFILTER',
 +                   uselib='AVCODEC AVFILTER AVUTIL SWRESAMPLE',
                     define_name='DCPOMATIC_HAVE_EBUR128_PATCHED_FFMPEG',
                     mandatory=False)
  
  def build(bld):
      create_version_cc(VERSION, bld.env.CXXFLAGS)
  
 +    # waf can't find these dependencies by itself because they are only included if DCPOMATIC_GROK is defined,
 +    # and I can't find a way to pass that to waf's dependency scanner
 +    if bld.env.ENABLE_GROK:
 +        for dep in (
 +                'src/lib/j2k_encoder.cc',
 +                'src/tools/dcpomatic.cc',
 +                'src/tools/dcpomatic_server.cc',
 +                'src/tools/dcpomatic_server_cli.cc',
 +                'src/tools/dcpomatic_batch.cc'
 +        ):
 +            bld.add_manual_dependency(bld.path.find_node(dep), bld.path.find_node('src/lib/grok/context.h'))
 +            bld.add_manual_dependency(bld.path.find_node(dep), bld.path.find_node('src/lib/grok/messenger.h'))
 +
 +        bld.add_manual_dependency(bld.path.find_node('src/wx/full_config_dialog.cc'), bld.path.find_node('src/wx/grok/gpu_config_panel.h'))
 +
      bld.recurse('src')
      bld.recurse('graphics')