More encryption fixes.
[libdcp.git] / src / picture_asset_writer.cc
1 /*
2     Copyright (C) 2012-2013 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 #include "AS_DCP.h"
21 #include "KM_fileio.h"
22 #include "picture_asset_writer.h"
23 #include "exceptions.h"
24 #include "picture_asset.h"
25
26 using std::istream;
27 using std::ostream;
28 using std::string;
29 using boost::shared_ptr;
30 using namespace libdcp;
31
32 FrameInfo::FrameInfo (istream& s)
33         : offset (0)
34         , size (0)
35 {
36         s >> offset >> size;
37
38         if (!s.good ()) {
39                 /* Make sure we zero these if something bad happened, otherwise
40                    the caller might try to alloc lots of RAM.
41                 */
42                 offset = size = 0;
43         }
44
45         s >> hash;
46 }
47
48 void
49 FrameInfo::write (ostream& s)
50 {
51         s << offset << " " << size << " " << hash;
52 }
53
54
55 PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, bool overwrite, bool interop, MXFMetadata const & metadata)
56         : _asset (asset)
57         , _frames_written (0)
58         , _started (false)
59         , _finalized (false)
60         , _overwrite (overwrite)
61         , _interop (interop)
62         , _metadata (metadata)
63 {
64
65 }
66
67 struct ASDCPStateBase
68 {
69         ASDCPStateBase ()
70                 : frame_buffer (4 * Kumu::Megabyte)
71         {}
72         
73         ASDCP::JP2K::CodestreamParser j2k_parser;
74         ASDCP::JP2K::FrameBuffer frame_buffer;
75         ASDCP::WriterInfo writer_info;
76         ASDCP::JP2K::PictureDescriptor picture_descriptor;
77         ASDCP::AESEncContext* encryption_context;
78 };
79
80 struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase
81 {
82         ASDCP::JP2K::MXFWriter mxf_writer;
83 };
84
85 struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase
86 {
87         ASDCP::JP2K::MXFSWriter mxf_writer;
88 };
89
90 /** @param a Asset to write to.  `a' must not be deleted while
91  *  this writer class still exists, or bad things will happen.
92  */
93 MonoPictureAssetWriter::MonoPictureAssetWriter (PictureAsset* asset, bool overwrite, bool interop, MXFMetadata const & metadata)
94         : PictureAssetWriter (asset, overwrite, interop, metadata)
95         , _state (new MonoPictureAssetWriter::ASDCPState)
96 {
97         _state->encryption_context = asset->encryption_context ();
98 }
99
100 StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* asset, bool overwrite, bool interop, MXFMetadata const & metadata)
101         : PictureAssetWriter (asset, overwrite, interop, metadata)
102         , _state (new StereoPictureAssetWriter::ASDCPState)
103         , _next_eye (EYE_LEFT)
104 {
105         _state->encryption_context = asset->encryption_context ();
106 }
107
108 template <class P, class Q>
109 void libdcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t* data, int size)
110 {
111         if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) {
112                 boost::throw_exception (MiscError ("could not parse J2K frame"));
113         }
114
115         state->j2k_parser.FillPictureDescriptor (state->picture_descriptor);
116         state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1);
117         
118         asset->fill_writer_info (&state->writer_info, asset->uuid(), writer->_interop, writer->_metadata);
119         
120         if (ASDCP_FAILURE (state->mxf_writer.OpenWrite (
121                                    asset->path().string().c_str(),
122                                    state->writer_info,
123                                    state->picture_descriptor,
124                                    16384,
125                                    writer->_overwrite)
126                     )) {
127                 
128                 boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string()));
129         }
130
131         writer->_started = true;
132 }
133
134 void
135 MonoPictureAssetWriter::start (uint8_t* data, int size)
136 {
137         libdcp::start (this, _state, _asset, data, size);
138 }
139
140 void
141 StereoPictureAssetWriter::start (uint8_t* data, int size)
142 {
143         libdcp::start (this, _state, _asset, data, size);
144 }
145
146 FrameInfo
147 MonoPictureAssetWriter::write (uint8_t* data, int size)
148 {
149         assert (!_finalized);
150
151         if (!_started) {
152                 start (data, size);
153         }
154
155         if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
156                 boost::throw_exception (MiscError ("could not parse J2K frame"));
157         }
158
159         uint64_t const before_offset = _state->mxf_writer.Tell ();
160
161         string hash;
162         if (ASDCP_FAILURE (_state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash))) {
163                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
164         }
165
166         ++_frames_written;
167         return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
168 }
169
170 /** Write a frame for one eye.  Frames must be written left, then right, then left etc.
171  *  @param data JPEG2000 data.
172  *  @param size Size of data.
173  */
174 FrameInfo
175 StereoPictureAssetWriter::write (uint8_t* data, int size)
176 {
177         assert (!_finalized);
178
179         if (!_started) {
180                 start (data, size);
181         }
182
183         if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
184                 boost::throw_exception (MiscError ("could not parse J2K frame"));
185         }
186
187         uint64_t const before_offset = _state->mxf_writer.Tell ();
188
189         string hash;
190         if (ASDCP_FAILURE (
191                     _state->mxf_writer.WriteFrame (
192                             _state->frame_buffer,
193                             _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT,
194                             _state->encryption_context,
195                             0,
196                             &hash)
197                     )) {
198                 
199                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
200         }
201
202         _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
203
204         return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
205 }
206
207 void
208 MonoPictureAssetWriter::fake_write (int size)
209 {
210         assert (_started);
211         assert (!_finalized);
212
213         if (ASDCP_FAILURE (_state->mxf_writer.FakeWriteFrame (size))) {
214                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
215         }
216
217         ++_frames_written;
218 }
219
220 void
221 StereoPictureAssetWriter::fake_write (int size)
222 {
223         assert (_started);
224         assert (!_finalized);
225
226         if (ASDCP_FAILURE (_state->mxf_writer.FakeWriteFrame (size))) {
227                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
228         }
229
230         _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
231         ++_frames_written;
232 }
233
234 void
235 MonoPictureAssetWriter::finalize ()
236 {
237         assert (!_finalized);
238         
239         if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
240                 boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string()));
241         }
242
243         _finalized = true;
244         _asset->set_intrinsic_duration (_frames_written);
245         _asset->set_duration (_frames_written);
246 }
247
248 void
249 StereoPictureAssetWriter::finalize ()
250 {
251         assert (!_finalized);
252         
253         if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
254                 boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string()));
255         }
256
257         _finalized = true;
258         _asset->set_intrinsic_duration (_frames_written / 2);
259         _asset->set_duration (_frames_written / 2);
260 }