78ef7676979c6e227ac01fdc5835f94fc8d47390
[libdcp.git] / src / util.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 /** @file  src/util.cc
21  *  @brief Utility methods.
22  */
23
24 #include <stdexcept>
25 #include <sstream>
26 #include <iostream>
27 #include <iomanip>
28 #include <boost/filesystem.hpp>
29 #include <openssl/sha.h>
30 #include "KM_util.h"
31 #include "KM_fileio.h"
32 #include "AS_DCP.h"
33 #include "util.h"
34 #include "exceptions.h"
35 #include "types.h"
36 #include "argb_frame.h"
37 #include "lut.h"
38
39 using std::string;
40 using std::stringstream;
41 using std::min;
42 using std::max;
43 using boost::shared_ptr;
44 using namespace libdcp;
45
46 /** Create a UUID.
47  *  @return UUID.
48  */
49 string
50 libdcp::make_uuid ()
51 {
52         char buffer[64];
53         Kumu::UUID id;
54         Kumu::GenRandomValue (id);
55         id.EncodeHex (buffer, 64);
56         return string (buffer);
57 }
58
59
60 /** Create a digest for a file.
61  *  @param filename File name.
62  *  @param progress If non-0, a signal which will be emitted periodically to update
63  *  progress; the parameter will start at 0.5 and proceed to 1.
64  *  @return Digest.
65  */
66 string
67 libdcp::make_digest (string filename, boost::signals2::signal<void (float)>* progress)
68 {
69         int const file_size = boost::filesystem::file_size (filename);
70         
71         Kumu::FileReader reader;
72         if (ASDCP_FAILURE (reader.OpenRead (filename.c_str ()))) {
73                 throw FileError ("could not open file to compute digest", filename);
74         }
75         
76         SHA_CTX sha;
77         SHA1_Init (&sha);
78         
79         Kumu::ByteString read_buffer (65536);
80         int done = 0;
81         while (1) {
82                 ui32_t read = 0;
83                 Kumu::Result_t r = reader.Read (read_buffer.Data(), read_buffer.Capacity(), &read);
84                 
85                 if (r == Kumu::RESULT_ENDOFFILE) {
86                         break;
87                 } else if (ASDCP_FAILURE (r)) {
88                         throw FileError ("could not read file to compute digest", filename);
89                 }
90                 
91                 SHA1_Update (&sha, read_buffer.Data(), read);
92                 done += read;
93
94                 if (progress) {
95                         (*progress) (0.5 + (0.5 * done / file_size));
96                 }
97         }
98
99         byte_t byte_buffer[20];
100         SHA1_Final (byte_buffer, &sha);
101
102         stringstream s;
103         char digest[64];
104         return Kumu::base64encode (byte_buffer, 20, digest, 64);
105 }
106
107 /** Convert a content kind to a string which is representative,
108  *  but not necessarily human-readable.
109  *  @param kind ContentKind.
110  *  @return string.
111  */
112 string
113 libdcp::content_kind_to_string (ContentKind kind)
114 {
115         switch (kind) {
116         case FEATURE:
117                 return "feature";
118         case SHORT:
119                 return "short";
120         case TRAILER:
121                 return "trailer";
122         case TEST:
123                 return "test";
124         case TRANSITIONAL:
125                 return "transitional";
126         case RATING:
127                 return "rating";
128         case TEASER:
129                 return "teaser";
130         case POLICY:
131                 return "policy";
132         case PUBLIC_SERVICE_ANNOUNCEMENT:
133                 return "psa";
134         case ADVERTISEMENT:
135                 return "advertisement";
136         }
137
138         assert (false);
139 }
140
141 libdcp::ContentKind
142 libdcp::content_kind_from_string (string type)
143 {
144         if (type == "feature") {
145                 return FEATURE;
146         } else if (type == "short") {
147                 return SHORT;
148         } else if (type == "trailer" || type == "Trailer") {
149                 return TRAILER;
150         } else if (type == "test") {
151                 return TEST;
152         } else if (type == "transitional") {
153                 return TRANSITIONAL;
154         } else if (type == "rating") {
155                 return RATING;
156         } else if (type == "teaser" || type == "Teaser") {
157                 return TEASER;
158         } else if (type == "policy") {
159                 return POLICY;
160         } else if (type == "psa") {
161                 return PUBLIC_SERVICE_ANNOUNCEMENT;
162         } else if (type == "advertisement") {
163                 return ADVERTISEMENT;
164         }
165
166         assert (false);
167 }
168                 
169 bool
170 libdcp::starts_with (string big, string little)
171 {
172         if (little.size() > big.size()) {
173                 return false;
174         }
175
176         return big.substr (0, little.length()) == little;
177 }
178
179 bool
180 libdcp::ends_with (string big, string little)
181 {
182         if (little.size() > big.size()) {
183                 return false;
184         }
185
186         return big.compare (big.length() - little.length(), little.length(), little) == 0;
187 }
188
189 opj_image_t *
190 libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
191 {
192         opj_dinfo_t* decoder = opj_create_decompress (CODEC_J2K);
193         opj_dparameters_t parameters;
194         opj_set_default_decoder_parameters (&parameters);
195         parameters.cp_reduce = reduce;
196         opj_setup_decoder (decoder, &parameters);
197         opj_cio_t* cio = opj_cio_open ((opj_common_ptr) decoder, data, size);
198         opj_image_t* image = opj_decode (decoder, cio);
199         if (!image) {
200                 opj_destroy_decompress (decoder);
201                 opj_cio_close (cio);
202                 throw DCPReadError ("could not decode JPEG2000 codestream");
203         }
204
205         opj_cio_close (cio);
206
207         image->x1 = rint (float(image->x1) / pow (2, reduce));
208         image->y1 = rint (float(image->y1) / pow (2, reduce));
209         return image;
210 }
211
212 shared_ptr<ARGBFrame>
213 libdcp::xyz_to_rgb (opj_image_t* xyz_frame)
214 {
215         struct {
216                 double x, y, z;
217         } s;
218         
219         struct {
220                 double r, g, b;
221         } d;
222         
223         int* xyz_x = xyz_frame->comps[0].data;
224         int* xyz_y = xyz_frame->comps[1].data;
225         int* xyz_z = xyz_frame->comps[2].data;
226
227         shared_ptr<ARGBFrame> argb_frame (new ARGBFrame (xyz_frame->x1, xyz_frame->y1));
228
229         uint8_t* argb = argb_frame->data ();
230         
231         for (int y = 0; y < xyz_frame->y1; ++y) {
232                 uint8_t* argb_line = argb;
233                 for (int x = 0; x < xyz_frame->x1; ++x) {
234
235                         assert (*xyz_x >= 0 && *xyz_y >= 0 && *xyz_z >= 0 && *xyz_x < 4096 && *xyz_x < 4096 && *xyz_z < 4096);
236                         
237                         /* In gamma LUT */
238                         s.x = lut_in[*xyz_x++];
239                         s.y = lut_in[*xyz_y++];
240                         s.z = lut_in[*xyz_z++];
241                         
242                         /* DCI companding */
243                         s.x /= DCI_COEFFICIENT;
244                         s.y /= DCI_COEFFICIENT;
245                         s.z /= DCI_COEFFICIENT;
246                         
247                         /* XYZ to RGB */
248                         d.r = ((s.x * color_matrix[0][0]) + (s.y * color_matrix[0][1]) + (s.z * color_matrix[0][2]));
249                         d.g = ((s.x * color_matrix[1][0]) + (s.y * color_matrix[1][1]) + (s.z * color_matrix[1][2]));
250                         d.b = ((s.x * color_matrix[2][0]) + (s.y * color_matrix[2][1]) + (s.z * color_matrix[2][2]));
251                         
252                         d.r = min (d.r, 1.0);
253                         d.r = max (d.r, 0.0);
254                         
255                         d.g = min (d.g, 1.0);
256                         d.g = max (d.g, 0.0);
257                         
258                         d.b = min (d.b, 1.0);
259                         d.b = max (d.b, 0.0);
260                         
261                         /* Out gamma LUT */
262                         *argb_line++ = lut_out[(int) (d.b * COLOR_DEPTH)];
263                         *argb_line++ = lut_out[(int) (d.g * COLOR_DEPTH)];
264                         *argb_line++ = lut_out[(int) (d.r * COLOR_DEPTH)];
265                         *argb_line++ = 0xff;
266                 }
267                 
268                 argb += argb_frame->stride ();
269         }
270
271         return argb_frame;
272 }
273
274 bool
275 libdcp::empty_or_white_space (string s)
276 {
277         for (size_t i = 0; i < s.length(); ++i) {
278                 if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
279                         return false;
280                 }
281         }
282
283         return true;
284 }