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 dcp_channels.erase(std::unique(dcp_channels.begin(), dcp_channels.end()), dcp_channels.end());
214 /* Remove channels that aren't actually in this MXF at all */
216 std::remove_if(dcp_channels.begin(), dcp_channels.end(), [this](dcp::Channel channel) {
217 return static_cast<int>(channel) >= _asset->channels();
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;
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);
240 _asset->set_file (_file);
246 SoundAssetWriter::write(float const * const * data, int data_channels, int frames)
248 do_write(data, data_channels, frames);
253 SoundAssetWriter::write(int32_t const * const * data, int data_channels, int frames)
255 do_write(data, data_channels, frames);
260 SoundAssetWriter::write_current_frame ()
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))));
270 /* We need a new set of sync packets for this frame */
271 _fsk.set_data (create_sync_packets());
276 SoundAssetWriter::finalize ()
278 if (_frame_buffer_offset > 0) {
279 write_current_frame ();
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))));
289 _asset->_intrinsic_duration = _frames_written;
290 return AssetWriter::finalize ();
294 /** Calculate and return the sync packets required for this edit unit (aka "frame") */
296 SoundAssetWriter::create_sync_packets ()
298 /* Parts of this code assumes 48kHz */
299 DCP_ASSERT (_asset->sampling_rate() == 48000);
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") */
307 auto const edit_rate = _asset->edit_rate ();
308 if (edit_rate == Fraction(24, 1)) {
312 } else if (edit_rate == Fraction(25, 1)) {
316 } else if (edit_rate == Fraction(30, 1)) {
320 } else if (edit_rate == Fraction(48, 1)) {
324 } else if (edit_rate == Fraction(50, 1)) {
328 } else if (edit_rate == Fraction(60, 1)) {
332 } else if (edit_rate == Fraction(96, 1)) {
336 } else if (edit_rate == Fraction(100, 1)) {
340 } else if (edit_rate == Fraction(120, 1)) {
349 DCP_ASSERT (id.DecodeHex(_asset->id().c_str()));
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);
364 bs.write_from_byte (0, 4);
365 bs.write_from_word (0, remaining_bits);
368 if (_sync_packet == 4) {
378 SoundAssetWriter::frame_buffer_data() const
380 return _state->frame_buffer.Data();
385 SoundAssetWriter::frame_buffer_capacity() const
387 return _state->frame_buffer.Capacity();