Remove old code.
[dcpomatic.git] / src / lib / make_mxf_job.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 /** @file  src/make_mxf_job.cc
21  *  @brief A job that creates a MXF file from some data.
22  */
23
24 #include <iostream>
25 #include <boost/filesystem.hpp>
26 #include "AS_DCP.h"
27 #include "KM_fileio.h"
28 #include "make_mxf_job.h"
29 #include "film.h"
30 #include "film_state.h"
31 #include "options.h"
32 #include "exceptions.h"
33
34 using namespace std;
35 using namespace boost;
36
37 /** @class MakeMXFJob
38  *  @brief A job that creates a MXF file from some data.
39  */
40
41 MakeMXFJob::MakeMXFJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o, Log* l, Type t)
42         : Job (s, o, l)
43         , _type (t)
44 {
45
46 }
47
48 string
49 MakeMXFJob::name () const
50 {
51         stringstream s;
52         switch (_type) {
53         case VIDEO:
54                 s << "Make video MXF for " << _fs->name;
55                 break;
56         case AUDIO:
57                 s << "Make audio MXF for " << _fs->name;
58                 break;
59         }
60         
61         return s.str ();
62 }
63
64 void
65 MakeMXFJob::run ()
66 {
67         set_progress (0);
68
69         string dir;
70         switch (_type) {
71         case VIDEO:
72                 dir = _opt->frame_out_path ();
73                 break;
74         case AUDIO:
75                 dir = _opt->multichannel_audio_out_path ();
76                 break;
77         }
78
79         list<string> files;
80         for (filesystem::directory_iterator i = filesystem::directory_iterator (dir); i != filesystem::directory_iterator(); ++i) {
81                 files.push_back (filesystem::path (*i).string());
82         }
83
84         if (files.empty ()) {
85                 throw EncodeError ("no input files found for MXF");
86         }
87
88         files.sort ();
89
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");
93         }
94
95         switch (essence_type) {
96         case ASDCP::ESS_JPEG_2000:
97                 j2k (files, _fs->file ("video.mxf"));
98                 break;
99         case ASDCP::ESS_PCM_24b_48k:
100         case ASDCP::ESS_PCM_24b_96k:
101                 wav (files, _fs->file ("audio.mxf"));
102                 break;
103         default:
104                 throw EncodeError ("unknown essence type");
105         }
106         
107         set_progress (1);
108 }
109
110 void
111 MakeMXFJob::wav (list<string> const & files, string const & mxf)
112 {
113         ASDCP::Rational fps (rintf (_fs->frames_per_second), 1);
114         
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");
118         }
119         
120         ASDCP::PCM::AudioDescriptor audio_desc;
121         pcm_parser_channel[0].FillAudioDescriptor (audio_desc);
122         audio_desc.ChannelCount = 0;
123         audio_desc.BlockAlign = 0;
124
125         ASDCP::PCM::FrameBuffer frame_buffer_channel[files.size()];
126         ASDCP::PCM::AudioDescriptor audio_desc_channel[files.size()];
127         
128         int j = 0;
129         for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
130                 
131                 if (ASDCP_FAILURE (pcm_parser_channel[j].OpenRead (i->c_str(), fps))) {
132                         throw EncodeError ("could not open WAV file for reading");
133                 }
134
135                 pcm_parser_channel[j].FillAudioDescriptor (audio_desc_channel[j]);
136                 
137                 if (audio_desc_channel[j].AudioSamplingRate != audio_desc.AudioSamplingRate) {
138                         throw EncodeError ("mismatched sampling rate");
139                 }
140                 
141                 if (audio_desc_channel[j].QuantizationBits != audio_desc.QuantizationBits) {
142                         throw EncodeError ("mismatched bit rate");
143                 }
144
145                 if (audio_desc_channel[j].ContainerDuration != audio_desc.ContainerDuration) {
146                         throw EncodeError ("mismatched duration");
147                 }
148
149                 frame_buffer_channel[j].Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc_channel[j]));
150                 ++j;
151         }
152
153         j = 0;
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;
157                 ++j;
158         }
159
160         audio_desc.EditRate = fps;
161         audio_desc.AvgBps = audio_desc.AvgBps * files.size ();
162
163         ASDCP::PCM::FrameBuffer frame_buffer;
164         frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
165         frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
166
167         ASDCP::WriterInfo writer_info;
168         fill_writer_info (&writer_info);
169
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");
173         }
174
175         for (int i = 0; i < _fs->length; ++i) {
176
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]);
180                 int offset = 0;
181
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");
186                         }
187                         
188                         if (frame_buffer_channel[j].Size() != frame_buffer_channel[j].Capacity()) {
189                                 throw EncodeError ("short audio frame");
190                         }
191                 }
192
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;
198                         }
199                         offset += sample_size;
200                 }
201
202                 if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
203                         throw EncodeError ("could not write audio MXF frame");
204                 }
205
206                 set_progress (float (i) / _fs->length);
207         }
208
209         
210         /* write footer information */
211         if (ASDCP_FAILURE (mxf_writer.Finalize())) {
212                 throw EncodeError ("could not finalise audio MXF");
213         }
214
215         set_progress (1);
216         set_state (FINISHED_OK);
217 }
218
219 void
220 MakeMXFJob::j2k (list<string> const & files, string const & mxf)
221 {
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");
226         }
227         
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);
232         
233         ASDCP::WriterInfo writer_info;
234         fill_writer_info (&writer_info);
235         
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");
239         }
240
241         int j = 0;
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");
245                 }
246
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");
250                 }
251                 
252                 ++j;
253                 set_progress (float (j) / files.size ());
254         }
255         
256         if (ASDCP_FAILURE (mxf_writer.Finalize())) {
257                 throw EncodeError ("error in finalising video MXF");
258         }
259         
260         set_progress (1);
261         set_state (FINISHED_OK);
262 }
263
264 void
265 MakeMXFJob::fill_writer_info (ASDCP::WriterInfo* writer_info)
266 {
267         writer_info->ProductVersion = DVDOMATIC_VERSION;
268         writer_info->CompanyName = "dvd-o-matic";
269         writer_info->ProductName = "dvd-o-matic";
270
271         /* set the label type */
272         writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
273
274         /* generate a random UUID for this essence */
275         Kumu::GenRandomUUID (writer_info->AssetUUID);
276 }