Add a nice note for general MXF errors.
[libdcp.git] / src / sound_asset_writer.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/sound_asset_writer.cc
36  *  @brief SoundAssetWriter class
37  */
38
39
40 #include "bitstream.h"
41 #include "compose.hpp"
42 #include "crypto_context.h"
43 #include "dcp_assert.h"
44 #include "exceptions.h"
45 #include "sound_asset.h"
46 #include "sound_asset_writer.h"
47 #include "warnings.h"
48 LIBDCP_DISABLE_WARNINGS
49 #include <asdcp/AS_DCP.h>
50 #include <asdcp/Metadata.h>
51 LIBDCP_ENABLE_WARNINGS
52 #include <iostream>
53
54
55 using std::min;
56 using std::max;
57 using std::cout;
58 using std::string;
59 using std::vector;
60 using namespace dcp;
61
62
63 struct SoundAssetWriter::ASDCPState
64 {
65         ASDCP::PCM::MXFWriter mxf_writer;
66         ASDCP::PCM::FrameBuffer frame_buffer;
67         ASDCP::WriterInfo writer_info;
68         ASDCP::PCM::AudioDescriptor desc;
69 };
70
71
72 SoundAssetWriter::SoundAssetWriter (SoundAsset* asset, boost::filesystem::path file, bool sync)
73         : AssetWriter (asset, file)
74         , _state (new SoundAssetWriter::ASDCPState)
75         , _asset (asset)
76         , _sync (sync)
77 {
78         DCP_ASSERT (!_sync || _asset->channels() >= 14);
79         DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
80
81         /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
82         _state->desc.EditRate = ASDCP::Rational (_asset->edit_rate().numerator, _asset->edit_rate().denominator);
83         _state->desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
84         _state->desc.Locked = 0;
85         _state->desc.ChannelCount = _asset->channels();
86         _state->desc.QuantizationBits = 24;
87         _state->desc.BlockAlign = 3 * _asset->channels();
88         _state->desc.AvgBps = _asset->sampling_rate() * _state->desc.BlockAlign;
89         _state->desc.LinkedTrackID = 0;
90         if (asset->standard() == Standard::INTEROP) {
91                 _state->desc.ChannelFormat = ASDCP::PCM::CF_NONE;
92         } else {
93                 /* As required by Bv2.1 */
94                 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
95         }
96
97         /* I'm fairly sure this is not necessary, as ContainerDuration is written
98            in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
99         */
100         _state->desc.ContainerDuration = 0;
101
102         _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
103         _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->desc));
104         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
105
106         _asset->fill_writer_info (&_state->writer_info, _asset->id());
107
108         if (_sync) {
109                 _fsk.set_data (create_sync_packets());
110         }
111 }
112
113
114 SoundAssetWriter::~SoundAssetWriter()
115 {
116         try {
117                 /* Last-resort finalization to close the file, at least */
118                 if (!_finalized) {
119                         _state->mxf_writer.Finalize();
120                 }
121         } catch (...) {}
122 }
123
124
125 void
126 SoundAssetWriter::start ()
127 {
128         auto r = _state->mxf_writer.OpenWrite (_file.string().c_str(), _state->writer_info, _state->desc);
129         if (ASDCP_FAILURE(r)) {
130                 boost::throw_exception (FileError("could not open audio MXF for writing", _file.string(), r));
131         }
132
133         if (_asset->standard() == Standard::SMPTE) {
134
135                 ASDCP::MXF::WaveAudioDescriptor* essence_descriptor = nullptr;
136                 _state->mxf_writer.OP1aHeader().GetMDObjectByType(
137                         asdcp_smpte_dict->ul(ASDCP::MDD_WaveAudioDescriptor), reinterpret_cast<ASDCP::MXF::InterchangeObject**>(&essence_descriptor)
138                         );
139                 DCP_ASSERT (essence_descriptor);
140                 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
141
142                 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
143                 GenRandomValue (soundfield->MCALinkID);
144                 if (auto lang = _asset->language()) {
145                         soundfield->RFC5646SpokenLanguage = *lang;
146                 }
147
148                 const MCASoundField field = _asset->channels() > 10 ? MCASoundField::SEVEN_POINT_ONE : MCASoundField::FIVE_POINT_ONE;
149
150                 if (field == MCASoundField::SEVEN_POINT_ONE) {
151                         soundfield->MCATagSymbol = "sg71";
152                         soundfield->MCATagName = "7.1DS";
153 LIBDCP_DISABLE_WARNINGS
154                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_71);
155 LIBDCP_ENABLE_WARNINGS
156                 } else {
157                         soundfield->MCATagSymbol = "sg51";
158                         soundfield->MCATagName = "5.1";
159 LIBDCP_DISABLE_WARNINGS
160                         soundfield->MCALabelDictionaryID = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioSoundfield_51);
161 LIBDCP_ENABLE_WARNINGS
162                 }
163
164                 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
165                 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
166
167                 /* We must describe at least the number of channels in `field', even if they aren't
168                  * in the asset (I think)
169                  */
170                 int descriptors = max(_asset->channels(), field == MCASoundField::FIVE_POINT_ONE ? 6 : 8);
171
172                 auto const used = used_audio_channels();
173
174                 for (auto i = 0; i < descriptors; ++i) {
175                         auto dcp_channel = static_cast<dcp::Channel>(i);
176                         if (find(used.begin(), used.end(), dcp_channel) == used.end()) {
177                                 continue;
178                         }
179                         auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
180                         GenRandomValue (channel->MCALinkID);
181                         channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
182                         channel->MCAChannelID = i + 1;
183                         channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
184                         channel->MCATagName = channel_to_mca_name(dcp_channel, field);
185                         if (auto lang = _asset->language()) {
186                                 channel->RFC5646SpokenLanguage = *lang;
187                         }
188 LIBDCP_DISABLE_WARNINGS
189                         channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
190 LIBDCP_ENABLE_WARNINGS
191                         _state->mxf_writer.OP1aHeader().AddChildObject(channel);
192                         essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
193                 }
194         }
195
196         _asset->set_file (_file);
197         _started = true;
198 }
199
200
201 void
202 SoundAssetWriter::write (float const * const * data, int frames)
203 {
204         DCP_ASSERT (!_finalized);
205         DCP_ASSERT (frames > 0);
206
207         static float const clip = 1.0f - (1.0f / pow (2, 23));
208
209         if (!_started) {
210                 start ();
211         }
212
213         int const ch = _asset->channels ();
214
215         for (int i = 0; i < frames; ++i) {
216
217                 byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
218
219                 /* Write one sample per channel */
220                 for (int j = 0; j < ch; ++j) {
221                         int32_t s = 0;
222                         if (j == 13 && _sync) {
223                                 s = _fsk.get();
224                         } else {
225                                 /* Convert sample to 24-bit int, clipping if necessary. */
226                                 float x = data[j][i];
227                                 if (x > clip) {
228                                         x = clip;
229                                 } else if (x < -clip) {
230                                         x = -clip;
231                                 }
232                                 s = x * (1 << 23);
233                         }
234                         *out++ = (s & 0xff);
235                         *out++ = (s & 0xff00) >> 8;
236                         *out++ = (s & 0xff0000) >> 16;
237                 }
238                 _frame_buffer_offset += 3 * ch;
239
240                 DCP_ASSERT (_frame_buffer_offset <= int(_state->frame_buffer.Capacity()));
241
242                 /* Finish the MXF frame if required */
243                 if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
244                         write_current_frame ();
245                         _frame_buffer_offset = 0;
246                         memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
247                 }
248         }
249 }
250
251 void
252 SoundAssetWriter::write_current_frame ()
253 {
254         auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
255         if (ASDCP_FAILURE(r)) {
256                 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
257         }
258
259         ++_frames_written;
260
261         if (_sync) {
262                 /* We need a new set of sync packets for this frame */
263                 _fsk.set_data (create_sync_packets());
264         }
265 }
266
267 bool
268 SoundAssetWriter::finalize ()
269 {
270         if (_frame_buffer_offset > 0) {
271                 write_current_frame ();
272         }
273
274         if (_started) {
275                 auto const r = _state->mxf_writer.Finalize();
276                 if (ASDCP_FAILURE(r)) {
277                         boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
278                 }
279         }
280
281         _asset->_intrinsic_duration = _frames_written;
282         return AssetWriter::finalize ();
283 }
284
285
286 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
287 vector<bool>
288 SoundAssetWriter::create_sync_packets ()
289 {
290         /* Parts of this code assumes 48kHz */
291         DCP_ASSERT (_asset->sampling_rate() == 48000);
292
293         /* Encoding of edit rate */
294         int edit_rate_code = 0;
295         /* How many 0 bits are used to pad the end of the packet */
296         int remaining_bits = 0;
297         /* How many packets in this edit unit (i.e. "frame") */
298         int packets = 0;
299         auto const edit_rate = _asset->edit_rate ();
300         if (edit_rate == Fraction(24, 1)) {
301                 edit_rate_code = 0;
302                 remaining_bits = 25;
303                 packets = 4;
304         } else if (edit_rate == Fraction(25, 1)) {
305                 edit_rate_code = 1;
306                 remaining_bits = 20;
307                 packets = 4;
308         } else if (edit_rate == Fraction(30, 1)) {
309                 edit_rate_code = 2;
310                 remaining_bits = 0;
311                 packets = 4;
312         } else if (edit_rate == Fraction(48, 1)) {
313                 edit_rate_code = 3;
314                 remaining_bits = 25;
315                 packets = 2;
316         } else if (edit_rate == Fraction(50, 1)) {
317                 edit_rate_code = 4;
318                 remaining_bits = 20;
319                 packets = 2;
320         } else if (edit_rate == Fraction(60, 1)) {
321                 edit_rate_code = 5;
322                 remaining_bits = 0;
323                 packets = 2;
324         } else if (edit_rate == Fraction(96, 1)) {
325                 edit_rate_code = 6;
326                 remaining_bits = 25;
327                 packets = 1;
328         } else if (edit_rate == Fraction(100, 1)) {
329                 edit_rate_code = 7;
330                 remaining_bits = 20;
331                 packets = 1;
332         } else if (edit_rate == Fraction(120, 1)) {
333                 edit_rate_code = 8;
334                 remaining_bits = 0;
335                 packets = 1;
336         }
337
338         Bitstream bs;
339
340         Kumu::UUID id;
341         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
342
343         for (int i = 0; i < packets; ++i) {
344                 bs.write_from_byte (0x4d);
345                 bs.write_from_byte (0x56);
346                 bs.start_crc (0x1021);
347                 bs.write_from_byte (edit_rate_code, 4);
348                 bs.write_from_byte (0, 2);
349                 bs.write_from_byte (_sync_packet, 2);
350                 bs.write_from_byte (id.Value()[i * 4 + 0]);
351                 bs.write_from_byte (id.Value()[i * 4 + 1]);
352                 bs.write_from_byte (id.Value()[i * 4 + 2]);
353                 bs.write_from_byte (id.Value()[i * 4 + 3]);
354                 bs.write_from_word (_frames_written, 24);
355                 bs.write_crc ();
356                 bs.write_from_byte (0, 4);
357                 bs.write_from_word (0, remaining_bits);
358
359                 ++_sync_packet;
360                 if (_sync_packet == 4) {
361                         _sync_packet = 0;
362                 }
363         }
364
365         return bs.get();
366 }
367