Merge branch '1.0' of git.carlh.net:git/libdcp into 1.0
[libdcp.git] / src / picture_asset.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
6     libdcp is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     libdcp is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with libdcp.  If not, see <http://www.gnu.org/licenses/>.
18
19     In addition, as a special exception, the copyright holders give
20     permission to link the code of portions of this program with the
21     OpenSSL library under certain conditions as described in each
22     individual source file, and distribute linked combinations
23     including the two.
24
25     You must obey the GNU General Public License in all respects
26     for all of the code used other than OpenSSL.  If you modify
27     file(s) with this exception, you may extend this exception to your
28     version of the file(s), but you are not obligated to do so.  If you
29     do not wish to do so, delete this exception statement from your
30     version.  If you delete this exception statement from all source
31     files in the program, then also delete it here.
32 */
33
34 #include "picture_asset.h"
35 #include "util.h"
36 #include "exceptions.h"
37 #include "openjpeg_image.h"
38 #include "picture_asset_writer.h"
39 #include "dcp_assert.h"
40 #include "compose.hpp"
41 #include "j2k.h"
42 #include <asdcp/AS_DCP.h>
43 #include <asdcp/KM_fileio.h>
44 #include <libxml++/nodes/element.h>
45 #include <boost/filesystem.hpp>
46 #include <list>
47 #include <stdexcept>
48
49 using std::string;
50 using std::list;
51 using std::vector;
52 using std::max;
53 using std::pair;
54 using std::make_pair;
55 using boost::shared_ptr;
56 using namespace dcp;
57
58 PictureAsset::PictureAsset (boost::filesystem::path file)
59         : Asset (file)
60         , _intrinsic_duration (0)
61 {
62
63 }
64
65 PictureAsset::PictureAsset (Fraction edit_rate)
66         : _edit_rate (edit_rate)
67         , _intrinsic_duration (0)
68 {
69
70 }
71
72 void
73 PictureAsset::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc)
74 {
75         _size.width = desc.StoredWidth;
76         _size.height = desc.StoredHeight;
77         _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
78         _intrinsic_duration = desc.ContainerDuration;
79         _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator);
80         _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator);
81 }
82
83 bool
84 PictureAsset::descriptor_equals (
85         ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, NoteHandler note
86         ) const
87 {
88         if (
89                 a.EditRate != b.EditRate ||
90                 a.SampleRate != b.SampleRate ||
91                 a.StoredWidth != b.StoredWidth ||
92                 a.StoredHeight != b.StoredHeight ||
93                 a.AspectRatio != b.AspectRatio ||
94                 a.Rsize != b.Rsize ||
95                 a.Xsize != b.Xsize ||
96                 a.Ysize != b.Ysize ||
97                 a.XOsize != b.XOsize ||
98                 a.YOsize != b.YOsize ||
99                 a.XTsize != b.XTsize ||
100                 a.YTsize != b.YTsize ||
101                 a.XTOsize != b.XTOsize ||
102                 a.YTOsize != b.YTOsize ||
103                 a.Csize != b.Csize
104 //              a.CodingStyleDefault != b.CodingStyleDefault ||
105 //              a.QuantizationDefault != b.QuantizationDefault
106                 ) {
107
108                 note (DCP_ERROR, "video MXF picture descriptors differ");
109                 return false;
110         }
111
112         if (a.ContainerDuration != b.ContainerDuration) {
113                 note (DCP_ERROR, "video container durations differ");
114         }
115
116 //              for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) {
117 //                      if (a.ImageComponents[j] != b.ImageComponents[j]) {
118 //                              notes.pack_start ("video MXF picture descriptors differ");
119 //                      }
120 //              }
121
122         return true;
123 }
124
125 bool
126 PictureAsset::frame_buffer_equals (
127         int frame, EqualityOptions opt, NoteHandler note,
128         uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
129         ) const
130 {
131         if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
132                 note (DCP_NOTE, "J2K identical");
133                 /* Easy result; the J2K data is identical */
134                 return true;
135         }
136
137         /* Decompress the images to bitmaps */
138         shared_ptr<OpenJPEGImage> image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0);
139         shared_ptr<OpenJPEGImage> image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0);
140
141         /* Compare them */
142
143         vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3);
144         int d = 0;
145         int max_diff = 0;
146
147         for (int c = 0; c < 3; ++c) {
148
149                 if (image_A->size() != image_B->size()) {
150                         note (DCP_ERROR, String::compose ("image sizes for frame %1 differ", frame));
151                         return false;
152                 }
153
154                 int const pixels = image_A->size().width * image_A->size().height;
155                 for (int j = 0; j < pixels; ++j) {
156                         int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]);
157                         abs_diffs[d++] = t;
158                         max_diff = max (max_diff, t);
159                 }
160         }
161
162         uint64_t total = 0;
163         for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
164                 total += *j;
165         }
166
167         double const mean = double (total) / abs_diffs.size ();
168
169         uint64_t total_squared_deviation = 0;
170         for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
171                 total_squared_deviation += pow (*j - mean, 2);
172         }
173
174         double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
175
176         note (DCP_NOTE, String::compose ("mean difference %1 deviation %2", mean, std_dev));
177
178         if (mean > opt.max_mean_pixel_error) {
179                 note (
180                         DCP_ERROR,
181                         String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame)
182                         );
183
184                 return false;
185         }
186
187         if (std_dev > opt.max_std_dev_pixel_error) {
188                 note (
189                         DCP_ERROR,
190                         String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame)
191                         );
192
193                 return false;
194         }
195
196         return true;
197 }
198
199 string
200 PictureAsset::pkl_type (Standard standard) const
201 {
202         switch (standard) {
203         case INTEROP:
204                 return "application/x-smpte-mxf;asdcpKind=Picture";
205         case SMPTE:
206                 return "application/mxf";
207         default:
208                 DCP_ASSERT (false);
209         }
210 }