Avoid depends_on() so we can build on Ubuntu 16.04.
[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                 std::vector<dcp::Channel> dcp_channels = {
194                         Channel::LEFT,
195                         Channel::RIGHT,
196                         Channel::CENTRE,
197                         Channel::LFE,
198                         Channel::LS,
199                         Channel::RS
200                 };
201
202                 std::copy(_extra_active_channels.begin(), _extra_active_channels.end(), back_inserter(dcp_channels));
203                 std::sort(dcp_channels.begin(), dcp_channels.end());
204                 auto last = std::unique(dcp_channels.begin(), dcp_channels.end());
205                 dcp_channels.erase(last, dcp_channels.end());
206
207                 for (auto dcp_channel: dcp_channels) {
208                         auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
209                         GenRandomValue (channel->MCALinkID);
210                         channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
211                         channel->MCAChannelID = static_cast<int>(dcp_channel) + 1;
212                         channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
213                         channel->MCATagName = channel_to_mca_name(dcp_channel, field);
214                         if (auto lang = _asset->language()) {
215                                 channel->RFC5646SpokenLanguage = *lang;
216                         }
217 LIBDCP_DISABLE_WARNINGS
218                         channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
219 LIBDCP_ENABLE_WARNINGS
220                         _state->mxf_writer.OP1aHeader().AddChildObject(channel);
221                         essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
222                 }
223         }
224
225         _asset->set_file (_file);
226         _started = true;
227 }
228
229
230 void
231 SoundAssetWriter::write(float const * const * data, int data_channels, int frames)
232 {
233         do_write(data, data_channels, frames);
234 }
235
236
237 void
238 SoundAssetWriter::write(int32_t const * const * data, int data_channels, int frames)
239 {
240         do_write(data, data_channels, frames);
241 }
242
243
244 void
245 SoundAssetWriter::write_current_frame ()
246 {
247         auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
248         if (ASDCP_FAILURE(r)) {
249                 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
250         }
251
252         ++_frames_written;
253
254         if (_sync) {
255                 /* We need a new set of sync packets for this frame */
256                 _fsk.set_data (create_sync_packets());
257         }
258 }
259
260 bool
261 SoundAssetWriter::finalize ()
262 {
263         if (_frame_buffer_offset > 0) {
264                 write_current_frame ();
265         }
266
267         if (_started) {
268                 auto const r = _state->mxf_writer.Finalize();
269                 if (ASDCP_FAILURE(r)) {
270                         boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
271                 }
272         }
273
274         _asset->_intrinsic_duration = _frames_written;
275         return AssetWriter::finalize ();
276 }
277
278
279 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
280 vector<bool>
281 SoundAssetWriter::create_sync_packets ()
282 {
283         /* Parts of this code assumes 48kHz */
284         DCP_ASSERT (_asset->sampling_rate() == 48000);
285
286         /* Encoding of edit rate */
287         int edit_rate_code = 0;
288         /* How many 0 bits are used to pad the end of the packet */
289         int remaining_bits = 0;
290         /* How many packets in this edit unit (i.e. "frame") */
291         int packets = 0;
292         auto const edit_rate = _asset->edit_rate ();
293         if (edit_rate == Fraction(24, 1)) {
294                 edit_rate_code = 0;
295                 remaining_bits = 25;
296                 packets = 4;
297         } else if (edit_rate == Fraction(25, 1)) {
298                 edit_rate_code = 1;
299                 remaining_bits = 20;
300                 packets = 4;
301         } else if (edit_rate == Fraction(30, 1)) {
302                 edit_rate_code = 2;
303                 remaining_bits = 0;
304                 packets = 4;
305         } else if (edit_rate == Fraction(48, 1)) {
306                 edit_rate_code = 3;
307                 remaining_bits = 25;
308                 packets = 2;
309         } else if (edit_rate == Fraction(50, 1)) {
310                 edit_rate_code = 4;
311                 remaining_bits = 20;
312                 packets = 2;
313         } else if (edit_rate == Fraction(60, 1)) {
314                 edit_rate_code = 5;
315                 remaining_bits = 0;
316                 packets = 2;
317         } else if (edit_rate == Fraction(96, 1)) {
318                 edit_rate_code = 6;
319                 remaining_bits = 25;
320                 packets = 1;
321         } else if (edit_rate == Fraction(100, 1)) {
322                 edit_rate_code = 7;
323                 remaining_bits = 20;
324                 packets = 1;
325         } else if (edit_rate == Fraction(120, 1)) {
326                 edit_rate_code = 8;
327                 remaining_bits = 0;
328                 packets = 1;
329         }
330
331         Bitstream bs;
332
333         Kumu::UUID id;
334         DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
335
336         for (int i = 0; i < packets; ++i) {
337                 bs.write_from_byte (0x4d);
338                 bs.write_from_byte (0x56);
339                 bs.start_crc (0x1021);
340                 bs.write_from_byte (edit_rate_code, 4);
341                 bs.write_from_byte (0, 2);
342                 bs.write_from_byte (_sync_packet, 2);
343                 bs.write_from_byte (id.Value()[i * 4 + 0]);
344                 bs.write_from_byte (id.Value()[i * 4 + 1]);
345                 bs.write_from_byte (id.Value()[i * 4 + 2]);
346                 bs.write_from_byte (id.Value()[i * 4 + 3]);
347                 bs.write_from_word (_frames_written, 24);
348                 bs.write_crc ();
349                 bs.write_from_byte (0, 4);
350                 bs.write_from_word (0, remaining_bits);
351
352                 ++_sync_packet;
353                 if (_sync_packet == 4) {
354                         _sync_packet = 0;
355                 }
356         }
357
358         return bs.get();
359 }
360
361
362 byte_t*
363 SoundAssetWriter::frame_buffer_data() const
364 {
365         return _state->frame_buffer.Data();
366 }
367
368
369 int
370 SoundAssetWriter::frame_buffer_capacity() const
371 {
372         return _state->frame_buffer.Capacity();
373 }
374