Seems to kind-of build video MXFs.
[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 #if 0
65 XXX
66 void
67 MakeMXFJob::run ()
68 {
69         set_progress_unknown ();
70
71         /* We round for DCP: not sure if this is right */
72         float fps = rintf (_fs->frames_per_second);
73         
74         stringstream c;
75         c << "opendcp_mxf -r " << fps << " -i ";
76         switch (_type) {
77         case VIDEO:
78                 c << "\"" << _opt->frame_out_path () << "\" -o \"" << _fs->file ("video.mxf") << "\"";
79                 break;
80         case AUDIO:
81                 c << "\"" << _opt->multichannel_audio_out_path () << "/*\" -o \"" << _fs->file ("audio.mxf") << "\"";
82                 break;
83         }
84
85         command (c.str ());
86         set_progress (1);
87 }
88 #endif
89
90 void
91 MakeMXFJob::run ()
92 {
93         set_progress (0);
94
95         string dir;
96         switch (_type) {
97         case VIDEO:
98                 dir = _opt->frame_out_path ();
99                 break;
100         case AUDIO:
101                 dir = _opt->multichannel_audio_out_path ();
102                 break;
103         }
104
105         list<string> files;
106         for (filesystem::directory_iterator i = filesystem::directory_iterator (dir); i != filesystem::directory_iterator(); ++i) {
107                 files.push_back (filesystem::path (*i).string());
108         }
109
110         if (files.empty ()) {
111                 throw EncodeError ("no input files found for MXF");
112         }
113
114         files.sort ();
115
116         ASDCP::EssenceType_t essence_type;
117         if (ASDCP_FAILURE (ASDCP::RawEssenceType (files.front().c_str(), essence_type))) {
118                 throw EncodeError ("could not work out type for MXF");
119         }
120
121         switch (essence_type) {
122         case ASDCP::ESS_JPEG_2000:
123                 j2k (files, _fs->file ("video.mxf"));
124                 break;
125         case ASDCP::ESS_PCM_24b_48k:
126         case ASDCP::ESS_PCM_24b_96k:
127                 wav (files, _fs->file ("audio.mxf"));
128                 break;
129         default:
130                 throw EncodeError ("unknown essence type");
131         }
132         
133         set_progress (1);
134 }
135
136 void
137 MakeMXFJob::wav (list<string> const & files, string const & mxf)
138 {
139
140 }
141
142 void
143 MakeMXFJob::j2k (list<string> const & files, string const & mxf)
144 {
145         /* Arbitrarily assume that the J2K MXF will take 90% of the time */
146         descend (0.9);
147
148         ASDCP::JP2K::CodestreamParser j2k_parser;
149         ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte);
150         if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (files.front().c_str(), frame_buffer))) {
151                 throw EncodeError ("could not open J2K file for reading");
152         }
153         
154         ASDCP::JP2K::PictureDescriptor picture_desc;
155         j2k_parser.FillPictureDescriptor (picture_desc);
156         /* XXX: we round for DCP: not sure if this is right */
157         picture_desc.EditRate = ASDCP::Rational (rintf (_fs->frames_per_second), 1);
158         
159         ASDCP::WriterInfo writer_info;
160         fill_writer_info (&writer_info);
161         
162         ASDCP::JP2K::MXFWriter mxf_writer;
163         if (ASDCP_FAILURE (mxf_writer.OpenWrite (mxf.c_str(), writer_info, picture_desc))) {
164                 throw EncodeError ("could not open MXF for writing");
165         }
166
167         int j = 0;
168         for (list<string>::const_iterator i = files.begin(); i != files.end(); ++i) {
169                 if (ASDCP_FAILURE (j2k_parser.OpenReadFrame (i->c_str(), frame_buffer))) {
170                         throw EncodeError ("could not open J2K file for reading");
171                 }
172
173                 /* XXX: passing 0 to WriteFrame ok? */
174                 if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, 0, 0))) {
175                         throw EncodeError ("error in writing video MXF");
176                 }
177                 
178                 ++j;
179                 set_progress (float (j) / files.size ());
180         }
181         
182         if (ASDCP_FAILURE (mxf_writer.Finalize())) {
183                 throw EncodeError ("error in finalising video MXF");
184         }
185         
186         ascend ();
187 }
188
189 void
190 MakeMXFJob::fill_writer_info (ASDCP::WriterInfo* writer_info)
191 {
192         writer_info->ProductVersion = DVDOMATIC_VERSION;
193         writer_info->CompanyName = "dvd-o-matic";
194         writer_info->ProductName = "dvd-o-matic";
195
196         /* set the label type */
197         writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
198
199         /* generate a random UUID for this essence */
200         Kumu::GenRandomUUID (writer_info->AssetUUID);
201 }