libdcp
j2k_transcode.cc
Go to the documentation of this file.
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 
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 
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 
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 
ArrayData class.
Class to hold an arbitrary block of data.
Definition: array_data.h:55
Definition: data.h:52
An error that occurs during decompression of JPEG2000 data.
Definition: exceptions.h:135
A miscellaneous exception.
Definition: exceptions.h:94
Any error that occurs when reading data from a DCP.
Definition: exceptions.h:106
DCP_ASSERT macro.
Exceptions thrown by libdcp.
Methods to encode and decode JPEG2000.
Namespace for everything in libdcp.
Definition: array_data.h:50
std::shared_ptr< OpenJPEGImage > decompress_j2k(uint8_t const *data, int64_t size, int reduce)
ArrayData compress_j2k(std::shared_ptr< const OpenJPEGImage >, int bandwidth, int frames_per_second, bool threed, bool fourk, std::string comment="libdcp")
OpenJPEGImage class.