wip: hacks which at least get GPU-decoded image on screen
[dcpomatic.git] / src / lib / j2k_image_proxy.cc
1 /*
2     Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic 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     DCP-o-matic 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 DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "j2k_image_proxy.h"
22 #include "dcpomatic_socket.h"
23 #include "image.h"
24 #include "dcpomatic_assert.h"
25 #include "warnings.h"
26 #include <dcp/raw_convert.h>
27 #include <dcp/openjpeg_image.h>
28 #include <dcp/mono_picture_frame.h>
29 #include <dcp/stereo_picture_frame.h>
30 #include <dcp/colour_conversion.h>
31 #include <dcp/rgb_xyz.h>
32 #include <dcp/j2k.h>
33 #include <libcxml/cxml.h>
34 DCPOMATIC_DISABLE_WARNINGS
35 #include <libxml++/libxml++.h>
36 DCPOMATIC_ENABLE_WARNINGS
37 #include <iostream>
38
39 #include "i18n.h"
40
41 using std::string;
42 using std::cout;
43 using std::max;
44 using std::pair;
45 using std::make_pair;
46 using boost::shared_ptr;
47 using boost::optional;
48 using boost::dynamic_pointer_cast;
49 using dcp::Data;
50 using dcp::raw_convert;
51 using namespace dcpomatic;
52
53
54 /** Construct a J2KImageProxy from a JPEG2000 file */
55 J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPixelFormat pixel_format)
56         : _data (path)
57         , _size (size)
58         , _pixel_format (pixel_format)
59         , _error (false)
60 {
61         /* ::image assumes 16bpp */
62         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
63 }
64
65 J2KImageProxy::J2KImageProxy (
66         shared_ptr<const dcp::MonoPictureFrame> frame,
67         dcp::Size size,
68         AVPixelFormat pixel_format,
69         optional<int> forced_reduction
70         )
71         : _data (frame->j2k_size ())
72         , _size (size)
73         , _pixel_format (pixel_format)
74         , _forced_reduction (forced_reduction)
75         , _error (false)
76 {
77         /* ::image assumes 16bpp */
78         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
79         memcpy (_data.data().get(), frame->j2k_data(), _data.size ());
80 }
81
82 J2KImageProxy::J2KImageProxy (
83         shared_ptr<const dcp::StereoPictureFrame> frame,
84         dcp::Size size,
85         dcp::Eye eye,
86         AVPixelFormat pixel_format,
87         optional<int> forced_reduction
88         )
89         : _size (size)
90         , _eye (eye)
91         , _pixel_format (pixel_format)
92         , _forced_reduction (forced_reduction)
93         , _error (false)
94 {
95         /* ::image assumes 16bpp */
96         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
97         switch (eye) {
98         case dcp::EYE_LEFT:
99                 _data = Data (frame->left_j2k_size ());
100                 memcpy (_data.data().get(), frame->left_j2k_data(), _data.size ());
101                 break;
102         case dcp::EYE_RIGHT:
103                 _data = Data (frame->right_j2k_size ());
104                 memcpy (_data.data().get(), frame->right_j2k_data(), _data.size ());
105                 break;
106         }
107 }
108
109 J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
110         : _error (false)
111 {
112         _size = dcp::Size (xml->number_child<int> ("Width"), xml->number_child<int> ("Height"));
113         if (xml->optional_number_child<int> ("Eye")) {
114                 _eye = static_cast<dcp::Eye> (xml->number_child<int> ("Eye"));
115         }
116         _data = Data (xml->number_child<int> ("Size"));
117         /* This only matters when we are using J2KImageProxy for the preview, which
118            will never use this constructor (which is only used for passing data to
119            encode servers).  So we can put anything in here.  It's a bit of a hack.
120         */
121         _pixel_format = AV_PIX_FMT_XYZ12LE;
122         socket->read (_data.data().get (), _data.size ());
123 }
124
125 int
126 J2KImageProxy::prepare (optional<dcp::Size> target_size) const
127 {
128         boost::mutex::scoped_lock lm (_mutex);
129
130         if (target_size) {
131                 std::cout << "target_size is " << target_size->width << " " << target_size->height << "\n";
132         } else {
133                 std::cout << "no target size.\n";
134         }
135
136         if (_target_size) {
137                 std::cout << "_target_size is " << _target_size->width << " " << _target_size->height << "\n";
138         } else {
139                 std::cout << "no _target size.\n";
140         }
141
142         if (_image) {// && target_size == _target_size) {
143                 std::cout << "hit.\n";
144                 return 0;
145                 //DCPOMATIC_ASSERT (_reduce);
146                 //return *_reduce;
147         }
148
149         int reduce = 0;
150
151         if (_forced_reduction) {
152                 reduce = *_forced_reduction;
153         } else {
154                 while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
155                         ++reduce;
156                 }
157
158                 --reduce;
159                 reduce = max (0, reduce);
160         }
161
162         try {
163                 std::cout << "cpu decompress.\n";
164                 shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (_data, reduce);
165                 _image.reset (new Image (_pixel_format, decompressed->size(), true));
166
167                 int const shift = 16 - decompressed->precision (0);
168
169                 /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
170                    the data is 12-bit either way.
171                    */
172
173                 int const width = decompressed->size().width;
174
175                 int p = 0;
176                 int* decomp_0 = decompressed->data (0);
177                 int* decomp_1 = decompressed->data (1);
178                 int* decomp_2 = decompressed->data (2);
179                 for (int y = 0; y < decompressed->size().height; ++y) {
180                         uint16_t* q = (uint16_t *) (_image->data()[0] + y * _image->stride()[0]);
181                         for (int x = 0; x < width; ++x) {
182                                 *q++ = decomp_0[p] << shift;
183                                 *q++ = decomp_1[p] << shift;
184                                 *q++ = decomp_2[p] << shift;
185                                 ++p;
186                         }
187                 }
188         } catch (dcp::J2KDecompressionError& e) {
189                 _image.reset (new Image (_pixel_format, _size, true));
190                 _image->make_black ();
191                 _error = true;
192         }
193
194         _target_size = target_size;
195         _reduce = reduce;
196
197         return reduce;
198 }
199
200
201 ImageProxy::Result
202 J2KImageProxy::image (optional<dcp::Size> target_size) const
203 {
204         int const r = prepare (target_size);
205
206         /* I think this is safe without a lock on mutex.  _image is guaranteed to be
207            set up when prepare() has happened.
208         */
209         return Result (_image, r, _error);
210 }
211
212
213 void
214 J2KImageProxy::add_metadata (xmlpp::Node* node) const
215 {
216         node->add_child("Type")->add_child_text (N_("J2K"));
217         node->add_child("Width")->add_child_text (raw_convert<string> (_size.width));
218         node->add_child("Height")->add_child_text (raw_convert<string> (_size.height));
219         if (_eye) {
220                 node->add_child("Eye")->add_child_text (raw_convert<string> (static_cast<int> (_eye.get ())));
221         }
222         node->add_child("Size")->add_child_text (raw_convert<string> (_data.size ()));
223 }
224
225 void
226 J2KImageProxy::write_to_socket (shared_ptr<Socket> socket) const
227 {
228         socket->write (_data.data().get(), _data.size());
229 }
230
231 bool
232 J2KImageProxy::same (shared_ptr<const ImageProxy> other) const
233 {
234         shared_ptr<const J2KImageProxy> jp = dynamic_pointer_cast<const J2KImageProxy> (other);
235         if (!jp) {
236                 return false;
237         }
238
239         if (_data.size() != jp->_data.size()) {
240                 return false;
241         }
242
243         return memcmp (_data.data().get(), jp->_data.data().get(), _data.size()) == 0;
244 }
245
246 J2KImageProxy::J2KImageProxy (Data data, dcp::Size size, AVPixelFormat pixel_format)
247         : _data (data)
248         , _size (size)
249         , _pixel_format (pixel_format)
250 {
251         /* ::image assumes 16bpp */
252         DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
253 }
254
255 size_t
256 J2KImageProxy::memory_used () const
257 {
258         size_t m = _data.size();
259         if (_image) {
260                 /* 3 components, 16-bits per pixel */
261                 m += 3 * 2 * _image->size().width * _image->size().height;
262         }
263         return m;
264 }
265
266
267 void
268 J2KImageProxy::set_image (shared_ptr<dcpomatic::Image> image)
269 {
270         // XXX need _target_size to be set up
271         boost::mutex::scoped_lock lm (_mutex);
272         _image = image;
273 }