Tolerate VI-N as a MCA channel ID - apparently written by CineAsset.
[libdcp.git] / src / j2k_transcode.cc
1 /*
2     Copyright (C) 2012-2021 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
35 /** @file  src/j2k_transcode.cc
36  *  @brief Methods to encode and decode JPEG2000
37  */
38
39
40 #include "array_data.h"
41 #include "j2k_transcode.h"
42 #include "exceptions.h"
43 #include "openjpeg_image.h"
44 #include "dcp_assert.h"
45 #include "compose.hpp"
46 #include <openjpeg.h>
47 #include <cmath>
48 #include <iostream>
49
50
51 using std::min;
52 using std::pow;
53 using std::string;
54 using std::shared_ptr;
55 using boost::shared_array;
56 using namespace dcp;
57
58
59 shared_ptr<dcp::OpenJPEGImage>
60 dcp::decompress_j2k (Data const& data, int reduce)
61 {
62         return dcp::decompress_j2k (data.data(), data.size(), reduce);
63 }
64
65
66 shared_ptr<dcp::OpenJPEGImage>
67 dcp::decompress_j2k (shared_ptr<const Data> data, int reduce)
68 {
69         return dcp::decompress_j2k (data->data(), data->size(), reduce);
70 }
71
72
73 class ReadBuffer
74 {
75 public:
76         ReadBuffer (uint8_t const * data, int64_t size)
77                 : _data (data)
78                 , _size (size)
79                 , _offset (0)
80         {}
81
82         OPJ_SIZE_T read (void* buffer, OPJ_SIZE_T nb_bytes)
83         {
84                 int64_t N = min (nb_bytes, _size - _offset);
85                 memcpy (buffer, _data + _offset, N);
86                 _offset += N;
87                 return N;
88         }
89
90 private:
91         uint8_t const * _data;
92         OPJ_SIZE_T _size;
93         OPJ_SIZE_T _offset;
94 };
95
96
97 static OPJ_SIZE_T
98 read_function (void* buffer, OPJ_SIZE_T nb_bytes, void* data)
99 {
100         return reinterpret_cast<ReadBuffer*>(data)->read (buffer, nb_bytes);
101 }
102
103
104 static void
105 read_free_function (void* data)
106 {
107         delete reinterpret_cast<ReadBuffer*>(data);
108 }
109
110
111 static void
112 decompress_error_callback (char const * msg, void *)
113 {
114         throw J2KDecompressionError (msg);
115 }
116
117
118 static void
119 compress_error_callback (char const * msg, void *)
120 {
121         throw MiscError (msg);
122 }
123
124
125 shared_ptr<dcp::OpenJPEGImage>
126 dcp::decompress_j2k (uint8_t const * data, int64_t size, int reduce)
127 {
128         DCP_ASSERT (reduce >= 0);
129
130         uint8_t const jp2_magic[] = {
131                 0x00,
132                 0x00,
133                 0x00,
134                 0x0c,
135                 'j',
136                 'P',
137                 0x20,
138                 0x20
139         };
140
141         auto format = OPJ_CODEC_J2K;
142         if (size >= int (sizeof (jp2_magic)) && memcmp (data, jp2_magic, sizeof (jp2_magic)) == 0) {
143                 format = OPJ_CODEC_JP2;
144         }
145
146         auto decoder = opj_create_decompress (format);
147         if (!decoder) {
148                 boost::throw_exception(ReadError("could not create JPEG2000 decompressor"));
149         }
150         opj_dparameters_t parameters;
151         opj_set_default_decoder_parameters (&parameters);
152         parameters.cp_reduce = reduce;
153         opj_setup_decoder (decoder, &parameters);
154
155         auto stream = opj_stream_default_create (OPJ_TRUE);
156         if (!stream) {
157                 throw MiscError ("could not create JPEG2000 stream");
158         }
159
160         opj_set_error_handler(decoder, decompress_error_callback, 00);
161
162         opj_stream_set_read_function (stream, read_function);
163         auto buffer = new ReadBuffer (data, size);
164         opj_stream_set_user_data (stream, buffer, read_free_function);
165         opj_stream_set_user_data_length (stream, size);
166
167         opj_image_t* image = 0;
168         opj_read_header (stream, decoder, &image);
169         if (opj_decode (decoder, stream, image) == OPJ_FALSE) {
170                 opj_destroy_codec (decoder);
171                 opj_stream_destroy (stream);
172                 if (format == OPJ_CODEC_J2K) {
173                         boost::throw_exception (ReadError (String::compose ("could not decode JPEG2000 codestream of %1 bytes.", size)));
174                 } else {
175                         boost::throw_exception (ReadError (String::compose ("could not decode JP2 file of %1 bytes.", size)));
176                 }
177         }
178
179         opj_destroy_codec (decoder);
180         opj_stream_destroy (stream);
181
182         image->x1 = rint (float(image->x1) / pow (2.0f, reduce));
183         image->y1 = rint (float(image->y1) / pow (2.0f, reduce));
184         return std::make_shared<OpenJPEGImage>(image);
185 }
186
187
188 class WriteBuffer
189 {
190 public:
191         OPJ_SIZE_T write (void* buffer, OPJ_SIZE_T nb_bytes)
192         {
193                 auto const new_offset = _offset + nb_bytes;
194                 if (new_offset > OPJ_SIZE_T(_data.size())) {
195                         _data.set_size(new_offset);
196                 }
197                 memcpy(_data.data() + _offset, buffer, nb_bytes);
198                 _offset = new_offset;
199                 return nb_bytes;
200         }
201
202         OPJ_BOOL seek (OPJ_SIZE_T nb_bytes)
203         {
204                 _offset = nb_bytes;
205                 return OPJ_TRUE;
206         }
207
208         ArrayData data () const
209         {
210                 return _data;
211         }
212
213 private:
214         ArrayData _data;
215         OPJ_SIZE_T _offset = 0;
216 };
217
218
219 static OPJ_SIZE_T
220 write_function (void* buffer, OPJ_SIZE_T nb_bytes, void* data)
221 {
222         return reinterpret_cast<WriteBuffer*>(data)->write(buffer, nb_bytes);
223 }
224
225
226 static void
227 write_free_function (void* data)
228 {
229         delete reinterpret_cast<WriteBuffer*>(data);
230 }
231
232
233 static OPJ_BOOL
234 seek_function (OPJ_OFF_T nb_bytes, void* data)
235 {
236         return reinterpret_cast<WriteBuffer*>(data)->seek(nb_bytes);
237
238 }
239
240
241 ArrayData
242 dcp::compress_j2k (shared_ptr<const OpenJPEGImage> xyz, int bandwidth, int frames_per_second, bool threed, bool fourk, string comment)
243 {
244         /* get a J2K compressor handle */
245         auto encoder = opj_create_compress (OPJ_CODEC_J2K);
246         if (encoder == nullptr) {
247                 throw MiscError ("could not create JPEG2000 encoder");
248         }
249
250         if (comment.empty()) {
251                 /* asdcplib complains with "Illegal data size" when reading frames encoded with an empty comment */
252                 throw MiscError("compress_j2k comment can not be an empty string");
253         }
254
255         opj_set_error_handler (encoder, compress_error_callback, 0);
256
257         /* Set encoding parameters to default values */
258         opj_cparameters_t parameters;
259         opj_set_default_encoder_parameters (&parameters);
260         if (fourk) {
261                 parameters.numresolution = 7;
262         }
263         parameters.rsiz = fourk ? OPJ_PROFILE_CINEMA_4K : OPJ_PROFILE_CINEMA_2K;
264         parameters.cp_comment = strdup (comment.c_str());
265
266         /* set max image */
267         parameters.max_cs_size = (bandwidth / 8) / frames_per_second;
268         if (threed) {
269                 /* In 3D we have only half the normal bandwidth per eye */
270                 parameters.max_cs_size /= 2;
271         }
272         parameters.max_comp_size = parameters.max_cs_size / 1.25;
273         parameters.tcp_numlayers = 1;
274         parameters.tcp_mct = 1;
275 #ifdef LIBDCP_HAVE_NUMGBITS
276         parameters.numgbits = fourk ? 2 : 1;
277 #endif
278
279         /* Setup the encoder parameters using the current image and user parameters */
280         opj_setup_encoder (encoder, &parameters, xyz->opj_image());
281
282 #ifndef LIBDCP_HAVE_NUMGBITS
283         string numgbits = String::compose("GUARD_BITS=%1", fourk ? 2 : 1);
284         char const* extra_options[] = { numgbits.c_str(), nullptr };
285         opj_encoder_set_extra_options(encoder, extra_options);
286 #endif
287
288         auto stream = opj_stream_default_create (OPJ_FALSE);
289         if (!stream) {
290                 opj_destroy_codec (encoder);
291                 free (parameters.cp_comment);
292                 throw MiscError ("could not create JPEG2000 stream");
293         }
294
295         opj_stream_set_write_function (stream, write_function);
296         opj_stream_set_seek_function (stream, seek_function);
297         WriteBuffer* buffer = new WriteBuffer ();
298         opj_stream_set_user_data (stream, buffer, write_free_function);
299
300         if (!opj_start_compress (encoder, xyz->opj_image(), stream)) {
301                 opj_stream_destroy (stream);
302                 opj_destroy_codec (encoder);
303                 free (parameters.cp_comment);
304                 if ((errno & 0x61500) == 0x61500) {
305                         /* We've had one of the magic error codes from our patched openjpeg */
306                         boost::throw_exception (StartCompressionError (errno & 0xff));
307                 } else {
308                         boost::throw_exception (StartCompressionError ());
309                 }
310         }
311
312         if (!opj_encode (encoder, stream)) {
313                 opj_stream_destroy (stream);
314                 opj_destroy_codec (encoder);
315                 free (parameters.cp_comment);
316                 throw MiscError ("JPEG2000 encoding failed");
317         }
318
319         if (!opj_end_compress (encoder, stream)) {
320                 opj_stream_destroy (stream);
321                 opj_destroy_codec (encoder);
322                 free (parameters.cp_comment);
323                 throw MiscError ("could not end JPEG2000 encoding");
324         }
325
326         ArrayData enc (buffer->data ());
327
328         opj_stream_destroy (stream);
329         opj_destroy_codec (encoder);
330         free (parameters.cp_comment);
331
332         return enc;
333 }
334