2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /** @file src/make_mxf_job.cc
21 * @brief A job that creates a MXF file from some data.
25 #include <boost/filesystem.hpp>
27 #include "KM_fileio.h"
28 #include "make_mxf_job.h"
30 #include "film_state.h"
32 #include "exceptions.h"
35 using namespace boost;
38 * @brief A job that creates a MXF file from some data.
41 MakeMXFJob::MakeMXFJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, Type t)
49 MakeMXFJob::name () const
54 s << "Make video MXF for " << _fs->name;
57 s << "Make audio MXF for " << _fs->name;
72 dir = _opt->frame_out_path ();
75 dir = _opt->multichannel_audio_out_path ();
80 for (filesystem::directory_iterator i = filesystem::directory_iterator (dir); i != filesystem::directory_iterator(); ++i) {
81 files.push_back (filesystem::path (*i).string());
85 throw EncodeError ("no input files found for MXF");
90 ASDCP::EssenceType_t essence_type;
91 if (ASDCP_FAILURE (ASDCP::RawEssenceType (files.front().c_str(), essence_type))) {
92 throw EncodeError ("could not work out type for MXF");
95 switch (essence_type) {
96 case ASDCP::ESS_JPEG_2000:
97 j2k (files, _fs->file ("video.mxf"));
99 case ASDCP::ESS_PCM_24b_48k:
100 case ASDCP::ESS_PCM_24b_96k:
101 wav (files, _fs->file ("audio.mxf"));
104 throw EncodeError ("unknown essence type");
111 MakeMXFJob::wav (list<string> const & files, string const & mxf)
113 ASDCP::Rational fps (rintf (_fs->frames_per_second), 1);
115 ASDCP::PCM::WAVParser pcm_parser_channel[files.size()];
116 if (pcm_parser_channel[0].OpenRead (files.front().c_str(), fps)) {
117 throw EncodeError ("could not open WAV file for reading");
120 ASDCP::PCM::AudioDescriptor audio_desc;
121 pcm_parser_channel[0].FillAudioDescriptor (audio_desc);
122 audio_desc.ChannelCount = 0;
123 audio_desc.BlockAlign = 0;
125 ASDCP::PCM::FrameBuffer frame_buffer_channel[files.size()];
126 ASDCP::PCM::AudioDescriptor audio_desc_channel[files.size()];
129 for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
131 if (ASDCP_FAILURE (pcm_parser_channel[j].OpenRead (i->c_str(), fps))) {
132 throw EncodeError ("could not open WAV file for reading");
135 pcm_parser_channel[j].FillAudioDescriptor (audio_desc_channel[j]);
137 if (audio_desc_channel[j].AudioSamplingRate != audio_desc.AudioSamplingRate) {
138 throw EncodeError ("mismatched sampling rate");
141 if (audio_desc_channel[j].QuantizationBits != audio_desc.QuantizationBits) {
142 throw EncodeError ("mismatched bit rate");
145 if (audio_desc_channel[j].ContainerDuration != audio_desc.ContainerDuration) {
146 throw EncodeError ("mismatched duration");
149 frame_buffer_channel[j].Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc_channel[j]));
154 for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
155 audio_desc.ChannelCount += audio_desc_channel[j].ChannelCount;
156 audio_desc.BlockAlign += audio_desc_channel[j].BlockAlign;
160 audio_desc.EditRate = fps;
161 audio_desc.AvgBps = audio_desc.AvgBps * files.size ();
163 ASDCP::PCM::FrameBuffer frame_buffer;
164 frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
165 frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
167 ASDCP::WriterInfo writer_info;
168 fill_writer_info (&writer_info);
170 ASDCP::PCM::MXFWriter mxf_writer;
171 if (ASDCP_FAILURE (mxf_writer.OpenWrite (mxf.c_str(), writer_info, audio_desc))) {
172 throw EncodeError ("could not open audio MXF for writing");
175 for (int i = 0; i < _fs->length; ++i) {
177 byte_t *data_s = frame_buffer.Data();
178 byte_t *data_e = data_s + frame_buffer.Capacity();
179 byte_t sample_size = ASDCP::PCM::CalcSampleSize (audio_desc_channel[0]);
182 for (list<string>::size_type j = 0; j < files.size(); ++j) {
183 memset (frame_buffer_channel[j].Data(), 0, frame_buffer_channel[j].Capacity());
184 if (ASDCP_FAILURE (pcm_parser_channel[j].ReadFrame (frame_buffer_channel[j]))) {
185 throw EncodeError ("could not read audio frame");
188 if (frame_buffer_channel[j].Size() != frame_buffer_channel[j].Capacity()) {
189 throw EncodeError ("short audio frame");
193 while (data_s < data_e) {
194 for (list<string>::size_type j = 0; j < files.size(); ++j) {
195 byte_t *frame = frame_buffer_channel[j].Data() + offset;
196 memcpy (data_s, frame, sample_size);
197 data_s += sample_size;
199 offset += sample_size;
202 if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
203 throw EncodeError ("could not write audio MXF frame");
206 set_progress (float (i) / _fs->length);
210 /* write footer information */
211 if (ASDCP_FAILURE (mxf_writer.Finalize())) {
212 throw EncodeError ("could not finalise audio MXF");
216 set_state (FINISHED_OK);
220 MakeMXFJob::j2k (list<string> const & files, string const & mxf)
222 ASDCP::JP2K::CodestreamParser j2k_parser;
223 ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte);
224 if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (files.front().c_str(), frame_buffer))) {
225 throw EncodeError ("could not open J2K file for reading");
228 ASDCP::JP2K::PictureDescriptor picture_desc;
229 j2k_parser.FillPictureDescriptor (picture_desc);
230 /* XXX: we round for DCP: not sure if this is right */
231 picture_desc.EditRate = ASDCP::Rational (rintf (_fs->frames_per_second), 1);
233 ASDCP::WriterInfo writer_info;
234 fill_writer_info (&writer_info);
236 ASDCP::JP2K::MXFWriter mxf_writer;
237 if (ASDCP_FAILURE (mxf_writer.OpenWrite (mxf.c_str(), writer_info, picture_desc))) {
238 throw EncodeError ("could not open MXF for writing");
242 for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
243 if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (i->c_str(), frame_buffer))) {
244 throw EncodeError ("could not open J2K file for reading");
247 /* XXX: passing 0 to WriteFrame ok? */
248 if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
249 throw EncodeError ("error in writing video MXF");
253 set_progress (float (j) / files.size ());
256 if (ASDCP_FAILURE (mxf_writer.Finalize())) {
257 throw EncodeError ("error in finalising video MXF");
261 set_state (FINISHED_OK);
265 MakeMXFJob::fill_writer_info (ASDCP::WriterInfo* writer_info)
267 writer_info->ProductVersion = DVDOMATIC_VERSION;
268 writer_info->CompanyName = "dvd-o-matic";
269 writer_info->ProductName = "dvd-o-matic";
271 /* set the label type */
272 writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
274 /* generate a random UUID for this essence */
275 Kumu::GenRandomUUID (writer_info->AssetUUID);