Merge branch 'master' into interop
[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 };
78
79 struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase
80 {
81         ASDCP::JP2K::MXFWriter mxf_writer;
82 };
83
84 struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase
85 {
86         ASDCP::JP2K::MXFSWriter mxf_writer;
87 };
88
89 /** @param a Asset to write to.  `a' must not be deleted while
90  *  this writer class still exists, or bad things will happen.
91  */
92 MonoPictureAssetWriter::MonoPictureAssetWriter (PictureAsset* asset, bool overwrite, bool interop, MXFMetadata const & metadata)
93         : PictureAssetWriter (asset, overwrite, interop, metadata)
94         , _state (new MonoPictureAssetWriter::ASDCPState)
95 {
96
97 }
98
99 StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* asset, bool overwrite, bool interop, MXFMetadata const & metadata)
100         : PictureAssetWriter (asset, overwrite, interop, metadata)
101         , _state (new StereoPictureAssetWriter::ASDCPState)
102         , _next_eye (EYE_LEFT)
103 {
104
105 }
106
107 template <class P, class Q>
108 void libdcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t* data, int size)
109 {
110         if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) {
111                 boost::throw_exception (MiscError ("could not parse J2K frame"));
112         }
113
114         state->j2k_parser.FillPictureDescriptor (state->picture_descriptor);
115         state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1);
116         
117         asset->fill_writer_info (&state->writer_info, asset->uuid(), writer->_interop, writer->_metadata);
118         
119         if (ASDCP_FAILURE (state->mxf_writer.OpenWrite (
120                                    asset->path().string().c_str(),
121                                    state->writer_info,
122                                    state->picture_descriptor,
123                                    16384,
124                                    writer->_overwrite)
125                     )) {
126                 
127                 boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string()));
128         }
129
130         writer->_started = true;
131 }
132
133 void
134 MonoPictureAssetWriter::start (uint8_t* data, int size)
135 {
136         libdcp::start (this, _state, _asset, data, size);
137 }
138
139 void
140 StereoPictureAssetWriter::start (uint8_t* data, int size)
141 {
142         libdcp::start (this, _state, _asset, data, size);
143 }
144
145 FrameInfo
146 MonoPictureAssetWriter::write (uint8_t* data, int size)
147 {
148         assert (!_finalized);
149
150         if (!_started) {
151                 start (data, size);
152         }
153
154         if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
155                 boost::throw_exception (MiscError ("could not parse J2K frame"));
156         }
157
158         uint64_t const before_offset = _state->mxf_writer.Tell ();
159
160         string hash;
161         if (ASDCP_FAILURE (_state->mxf_writer.WriteFrame (_state->frame_buffer, 0, 0, &hash))) {
162                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
163         }
164
165         ++_frames_written;
166         return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
167 }
168
169 /** Write a frame for one eye.  Frames must be written left, then right, then left etc.
170  *  @param data JPEG2000 data.
171  *  @param size Size of data.
172  */
173 FrameInfo
174 StereoPictureAssetWriter::write (uint8_t* data, int size)
175 {
176         assert (!_finalized);
177
178         if (!_started) {
179                 start (data, size);
180         }
181
182         if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
183                 boost::throw_exception (MiscError ("could not parse J2K frame"));
184         }
185
186         uint64_t const before_offset = _state->mxf_writer.Tell ();
187
188         string hash;
189         if (ASDCP_FAILURE (
190                     _state->mxf_writer.WriteFrame (
191                             _state->frame_buffer,
192                             _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT,
193                             0,
194                             0,
195                             &hash)
196                     )) {
197                 
198                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
199         }
200
201         _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
202
203         return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
204 }
205
206 void
207 MonoPictureAssetWriter::fake_write (int size)
208 {
209         assert (_started);
210         assert (!_finalized);
211
212         if (ASDCP_FAILURE (_state->mxf_writer.FakeWriteFrame (size))) {
213                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
214         }
215
216         ++_frames_written;
217 }
218
219 void
220 StereoPictureAssetWriter::fake_write (int size)
221 {
222         assert (_started);
223         assert (!_finalized);
224
225         if (ASDCP_FAILURE (_state->mxf_writer.FakeWriteFrame (size))) {
226                 boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string()));
227         }
228
229         _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
230         ++_frames_written;
231 }
232
233 void
234 MonoPictureAssetWriter::finalize ()
235 {
236         assert (!_finalized);
237         
238         if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
239                 boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string()));
240         }
241
242         _finalized = true;
243         _asset->set_intrinsic_duration (_frames_written);
244         _asset->set_duration (_frames_written);
245 }
246
247 void
248 StereoPictureAssetWriter::finalize ()
249 {
250         assert (!_finalized);
251         
252         if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
253                 boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string()));
254         }
255
256         _finalized = true;
257         _asset->set_intrinsic_duration (_frames_written / 2);
258         _asset->set_duration (_frames_written / 2);
259 }