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