+ int png_color_type = 0;
+ int bits_per_pixel = 0;
+ switch (image->pixel_format()) {
+ case AV_PIX_FMT_RGB24:
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ bits_per_pixel = 8;
+ break;
+ case AV_PIX_FMT_XYZ12LE:
+ png_color_type = PNG_COLOR_TYPE_RGB;
+ bits_per_pixel = 16;
+ break;
+ default:
+ BOOST_REQUIRE_MESSAGE (false, "unexpected pixel format " << image->pixel_format());
+ }
+
+ /* error handling? */
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, reinterpret_cast<void*>(const_cast<Image*>(image.get())), png_error_fn, 0);
+ BOOST_REQUIRE (png_ptr);
+
+ Memory state;
+
+ png_set_write_fn (png_ptr, &state, png_write_data, png_flush);
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ BOOST_REQUIRE (info_ptr);
+
+ png_set_IHDR (png_ptr, info_ptr, image->size().width, image->size().height, bits_per_pixel, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ png_byte ** row_pointers = reinterpret_cast<png_byte **>(png_malloc(png_ptr, image->size().height * sizeof(png_byte *)));
+ for (int i = 0; i < image->size().height; ++i) {
+ row_pointers[i] = (png_byte *) (image->data()[0] + i * image->stride()[0]);
+ }
+
+ png_write_info (png_ptr, info_ptr);
+ png_write_image (png_ptr, row_pointers);
+ png_write_end (png_ptr, info_ptr);
+
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+ png_free (png_ptr, row_pointers);
+
+ dcp::ArrayData(state.data, state.size).write(file);
+}
+
+
+void
+check_ffmpeg (boost::filesystem::path ref, boost::filesystem::path check, int audio_tolerance)
+{
+ int const r = system (String::compose("ffcmp -t %1 %2 %3", audio_tolerance, ref.string(), check.string()).c_str());
+ BOOST_REQUIRE_EQUAL (WEXITSTATUS(r), 0);
+}
+
+void
+check_one_frame (boost::filesystem::path dcp_dir, int64_t index, boost::filesystem::path ref)
+{
+ dcp::DCP dcp (dcp_dir);
+ dcp.read ();
+ auto asset = dynamic_pointer_cast<dcp::MonoPictureAsset> (dcp.cpls().front()->reels().front()->main_picture()->asset());
+ BOOST_REQUIRE (asset);
+ auto frame = asset->start_read()->get_frame(index);
+ auto ref_frame (new dcp::MonoPictureFrame (ref));
+
+ auto image = frame->xyz_image ();
+ auto ref_image = ref_frame->xyz_image ();
+
+ BOOST_REQUIRE (image->size() == ref_image->size());
+
+ int off = 0;
+ for (int y = 0; y < ref_image->size().height; ++y) {
+ for (int x = 0; x < ref_image->size().width; ++x) {
+ BOOST_REQUIRE_EQUAL (ref_image->data(0)[off], image->data(0)[off]);
+ BOOST_REQUIRE_EQUAL (ref_image->data(1)[off], image->data(1)[off]);
+ BOOST_REQUIRE_EQUAL (ref_image->data(2)[off], image->data(2)[off]);
+ ++off;
+ }
+ }
+}
+
+boost::filesystem::path
+dcp_file (shared_ptr<const Film> film, string prefix)
+{
+ auto i = boost::filesystem::directory_iterator (film->dir(film->dcp_name()));
+ while (i != boost::filesystem::directory_iterator() && !boost::algorithm::starts_with (i->path().leaf().string(), prefix)) {
+ ++i;
+ }
+
+ BOOST_REQUIRE (i != boost::filesystem::directory_iterator());
+ return i->path();
+}
+
+boost::filesystem::path
+subtitle_file (shared_ptr<Film> film)
+{
+ for (auto i: boost::filesystem::directory_iterator(film->directory().get() / film->dcp_name (false))) {
+ if (boost::filesystem::is_directory(i.path())) {
+ for (auto j: boost::filesystem::directory_iterator(i.path())) {
+ if (boost::algorithm::starts_with(j.path().leaf().string(), "sub_")) {
+ return j.path();
+ }
+ }
+ }
+ }
+
+ BOOST_REQUIRE (false);
+ /* Remove warning */
+ return boost::filesystem::path("/");
+}
+
+void
+make_random_file (boost::filesystem::path path, size_t size)
+{
+ auto t = fopen_boost(path, "wb");
+ BOOST_REQUIRE (t);
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t r = rand() & 0xff;
+ fwrite (&r, 1, 1, t);
+ }
+ fclose (t);
+}
+
+
+LogSwitcher::LogSwitcher (shared_ptr<Log> log)
+ : _old (dcpomatic_log)
+{
+ dcpomatic_log = log;
+}
+
+
+LogSwitcher::~LogSwitcher ()
+{
+ dcpomatic_log = _old;
+}
+
+std::ostream&
+dcp::operator<< (std::ostream& s, dcp::Size i)
+{
+ s << i.width << "x" << i.height;
+ return s;
+}
+
+std::ostream&
+dcp::operator<< (std::ostream& s, dcp::Standard t)
+{
+ switch (t) {
+ case Standard::INTEROP:
+ s << "interop";
+ break;
+ case Standard::SMPTE:
+ s << "smpte";
+ break;
+ }
+ return s;
+}
+
+std::ostream&
+operator<< (std::ostream&s, VideoFrameType f)
+{
+ s << video_frame_type_to_string(f);
+ return s;
+}
+
+
+void
+Cleanup::add (boost::filesystem::path path)
+{
+ _paths.push_back (path);
+}