2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
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.
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.
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/>.
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
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.
35 /** @file src/sound_asset_writer.cc
36 * @brief SoundAssetWriter class
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"
49 LIBDCP_DISABLE_WARNINGS
50 #include <asdcp/AS_DCP.h>
51 #include <asdcp/Metadata.h>
52 LIBDCP_ENABLE_WARNINGS
64 struct SoundAssetWriter::ASDCPState
66 ASDCP::PCM::MXFWriter mxf_writer;
67 ASDCP::PCM::FrameBuffer frame_buffer;
68 ASDCP::WriterInfo writer_info;
69 ASDCP::PCM::AudioDescriptor desc;
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)
77 , _extra_active_channels(extra_active_channels)
79 , _include_mca_subdescriptors(include_mca_subdescriptors)
81 DCP_ASSERT (!_sync || _asset->channels() >= 14);
82 DCP_ASSERT (!_sync || _asset->standard() == Standard::SMPTE);
84 /* None of these are allowed in extra_active_channels; some are implicit, and (it seems) should never have a descriptor
87 vector<Channel> disallowed_extra = {
96 Channel::SIGN_LANGUAGE,
97 Channel::CHANNEL_COUNT
99 for (auto disallowed: disallowed_extra) {
100 DCP_ASSERT(std::find(extra_active_channels.begin(), extra_active_channels.end(), disallowed) == extra_active_channels.end());
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;
115 /* As required by Bv2.1 */
116 _state->desc.ChannelFormat = ASDCP::PCM::CF_CFG_4;
119 /* I'm fairly sure this is not necessary, as ContainerDuration is written
120 in ASDCP's WriteMXFFooter, but it stops a valgrind warning.
122 _state->desc.ContainerDuration = 0;
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());
128 _asset->fill_writer_info (&_state->writer_info, _asset->id());
131 _fsk.set_data (create_sync_packets());
136 SoundAssetWriter::~SoundAssetWriter()
139 /* Last-resort finalization to close the file, at least */
141 _state->mxf_writer.Finalize();
148 SoundAssetWriter::start ()
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));
155 if (_asset->standard() == Standard::SMPTE && _include_mca_subdescriptors) {
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)
161 DCP_ASSERT (essence_descriptor);
162 essence_descriptor->ChannelAssignment = asdcp_smpte_dict->ul(ASDCP::MDD_DCAudioChannelCfg_4_WTF);
164 auto soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(asdcp_smpte_dict);
165 GenRandomValue (soundfield->MCALinkID);
166 if (auto lang = _asset->language()) {
167 soundfield->RFC5646SpokenLanguage = *lang;
170 MCASoundField const field =
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;
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
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
190 _state->mxf_writer.OP1aHeader().AddChildObject(soundfield);
191 essence_descriptor->SubDescriptors.push_back(soundfield->InstanceUID);
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.
196 std::vector<dcp::Channel> dcp_channels = {
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).
208 std::copy(_extra_active_channels.begin(), _extra_active_channels.end(), back_inserter(dcp_channels));
210 /* Remove duplicates */
211 std::sort(dcp_channels.begin(), dcp_channels.end());
212 auto last = std::unique(dcp_channels.begin(), dcp_channels.end());
213 dcp_channels.erase(last, dcp_channels.end());
215 for (auto dcp_channel: dcp_channels) {
216 auto channel = new ASDCP::MXF::AudioChannelLabelSubDescriptor(asdcp_smpte_dict);
217 GenRandomValue (channel->MCALinkID);
218 channel->SoundfieldGroupLinkID = soundfield->MCALinkID;
219 channel->MCAChannelID = static_cast<int>(dcp_channel) + 1;
220 channel->MCATagSymbol = "ch" + channel_to_mca_id(dcp_channel, field);
221 channel->MCATagName = channel_to_mca_name(dcp_channel, field);
222 if (auto lang = _asset->language()) {
223 channel->RFC5646SpokenLanguage = *lang;
225 LIBDCP_DISABLE_WARNINGS
226 channel->MCALabelDictionaryID = channel_to_mca_universal_label(dcp_channel, field, asdcp_smpte_dict);
227 LIBDCP_ENABLE_WARNINGS
228 _state->mxf_writer.OP1aHeader().AddChildObject(channel);
229 essence_descriptor->SubDescriptors.push_back(channel->InstanceUID);
233 _asset->set_file (_file);
239 SoundAssetWriter::write(float const * const * data, int data_channels, int frames)
241 do_write(data, data_channels, frames);
246 SoundAssetWriter::write(int32_t const * const * data, int data_channels, int frames)
248 do_write(data, data_channels, frames);
253 SoundAssetWriter::write_current_frame ()
255 auto const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _crypto_context->context(), _crypto_context->hmac());
256 if (ASDCP_FAILURE(r)) {
257 boost::throw_exception (MiscError(String::compose("could not write audio MXF frame (%1)", static_cast<int>(r))));
263 /* We need a new set of sync packets for this frame */
264 _fsk.set_data (create_sync_packets());
269 SoundAssetWriter::finalize ()
271 if (_frame_buffer_offset > 0) {
272 write_current_frame ();
276 auto const r = _state->mxf_writer.Finalize();
277 if (ASDCP_FAILURE(r)) {
278 boost::throw_exception (MiscError(String::compose ("could not finalise audio MXF (%1)", static_cast<int>(r))));
282 _asset->_intrinsic_duration = _frames_written;
283 return AssetWriter::finalize ();
287 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
289 SoundAssetWriter::create_sync_packets ()
291 /* Parts of this code assumes 48kHz */
292 DCP_ASSERT (_asset->sampling_rate() == 48000);
294 /* Encoding of edit rate */
295 int edit_rate_code = 0;
296 /* How many 0 bits are used to pad the end of the packet */
297 int remaining_bits = 0;
298 /* How many packets in this edit unit (i.e. "frame") */
300 auto const edit_rate = _asset->edit_rate ();
301 if (edit_rate == Fraction(24, 1)) {
305 } else if (edit_rate == Fraction(25, 1)) {
309 } else if (edit_rate == Fraction(30, 1)) {
313 } else if (edit_rate == Fraction(48, 1)) {
317 } else if (edit_rate == Fraction(50, 1)) {
321 } else if (edit_rate == Fraction(60, 1)) {
325 } else if (edit_rate == Fraction(96, 1)) {
329 } else if (edit_rate == Fraction(100, 1)) {
333 } else if (edit_rate == Fraction(120, 1)) {
342 DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
344 for (int i = 0; i < packets; ++i) {
345 bs.write_from_byte (0x4d);
346 bs.write_from_byte (0x56);
347 bs.start_crc (0x1021);
348 bs.write_from_byte (edit_rate_code, 4);
349 bs.write_from_byte (0, 2);
350 bs.write_from_byte (_sync_packet, 2);
351 bs.write_from_byte (id.Value()[i * 4 + 0]);
352 bs.write_from_byte (id.Value()[i * 4 + 1]);
353 bs.write_from_byte (id.Value()[i * 4 + 2]);
354 bs.write_from_byte (id.Value()[i * 4 + 3]);
355 bs.write_from_word (_frames_written, 24);
357 bs.write_from_byte (0, 4);
358 bs.write_from_word (0, remaining_bits);
361 if (_sync_packet == 4) {
371 SoundAssetWriter::frame_buffer_data() const
373 return _state->frame_buffer.Data();
378 SoundAssetWriter::frame_buffer_capacity() const
380 return _state->frame_buffer.Capacity();