Reverting const accessor for class optional_property
[asdcplib.git] / src / klvsplit.cpp
1 /*
2 Copyright (c) 2005-2013, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    klvwalk.cpp
28     \version $Id$
29     \brief   KLV+MXF test
30 */
31
32 #include "MXF.h"
33 #include <KM_log.h>
34
35
36 using namespace ASDCP;
37 using Kumu::DefaultLogSink;
38
39
40 //------------------------------------------------------------------------------------------
41 //
42 // command line option parser class
43
44 static const char* PROGRAM_NAME = "klvsplit";    // program name for messages
45 typedef std::list<std::string> FileList_t;
46
47 // Increment the iterator, test for an additional non-option command line argument.
48 // Causes the caller to return if there are no remaining arguments or if the next
49 // argument begins with '-'.
50 #define TEST_EXTRA_ARG(i,c)    if ( ++i >= argc || argv[(i)][0] == '-' ) \
51                                  { \
52                                    fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
53                                    return; \
54                                  }
55
56 //
57 void
58 banner(FILE* stream = stdout)
59 {
60   fprintf(stream, "\n\
61 %s (asdcplib %s)\n\n\
62 Copyright (c) 2005-2013 John Hurst\n\
63 %s is part of the asdcplib DCP tools package.\n\
64 asdcplib may be copied only under the terms of the license found at\n\
65 the top of every file in the asdcplib distribution kit.\n\n\
66 Specify the -h (help) option for further information about %s\n\n",
67           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME, PROGRAM_NAME);
68 }
69
70 //
71 void
72 usage(FILE* stream = stdout)
73 {
74   fprintf(stream, "\
75 USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u|-U] [-v] \n\
76            (<type-name>|<type-ul>) <mxf-filename>+\n\
77 \n\
78        %s -d\n\
79 \n\
80        %s [-h|-help] [-V]\n\
81 \n\
82   -d           - List the valid packet type names\n\
83   -h | -help   - Show help\n\
84   -l <limit>   - Stop processing after <limit> matching packets\n\
85   -p <prefix>  - Use <prefix> to start output filenames (default\n\
86                    uses the input filename minus any extension\n\
87   -s <suffix>  - Append <suffix> to output filenames\n\
88   -u           - Unwrap the packet value (i.e., do not output KL)\n\
89   -U           - Do not unwrap (default)\n\
90   -v           - Verbose. Prints informative messages to stderr\n\
91   -V           - Show version information\n\
92 \n\
93   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
94          o All option arguments must be separated from the option by whitespace.\n\
95 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
96 }
97
98 //
99 //
100  class CommandOptions
101  {
102    CommandOptions();
103
104  public:
105    bool   error_flag;               // true if the given options are in error or not complete
106    bool   version_flag;             // true if the version display option was selected
107    bool   help_flag;                // true if the help display option was selected
108    bool   verbose_flag;             // true if the informative messages option was selected
109    bool   unwrap_mode;              // true if we are to strip the K and L before writing
110    bool   list_mode;
111    ASDCP::UL target_ul;             // a UL value identifying the packets to be extracted
112    ui64_t  extract_limit;           // limit extraction to the given number of packets
113    std::string prefix;              // output filename prefix
114    std::string suffix;              // output filename suffix
115    FileList_t inFileList;           // File to operate on
116
117    CommandOptions(int argc, const char** argv, const ASDCP::Dictionary& dict) :
118      error_flag(true), version_flag(false), help_flag(false),
119      verbose_flag(false), unwrap_mode(false), list_mode(false), extract_limit(ui64_C(-1))
120    {
121      for ( int i = 1; i < argc; ++i )
122        {
123
124          if ( (strcmp( argv[i], "-help") == 0) )
125            {
126              help_flag = true;
127              continue;
128            }
129          
130          if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 )
131            {
132              switch ( argv[i][1] )
133                {
134                case 'd': list_mode = true; break;
135                case 'h': help_flag = true; break;
136
137                case 'l':
138                  TEST_EXTRA_ARG(i, 'l');
139                  extract_limit = Kumu::xabs(strtoll(argv[i], 0, 10));
140                  break;
141
142                case 'p':
143                  TEST_EXTRA_ARG(i, 'p');
144                  prefix = argv[i];
145                  break;
146
147                case 's':
148                  TEST_EXTRA_ARG(i, 's');
149                  suffix = argv[i];
150                  break;
151
152                case 'u': unwrap_mode = true; break;
153                case 'U': unwrap_mode = false; break;
154                case 'V': version_flag = true; break;
155                case 'v': verbose_flag = true; break;
156
157                default:
158                  fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
159                  return;
160                }
161            }
162          else if ( argv[i][0] != '-' )
163            {
164              if ( ! target_ul.HasValue() )
165                {
166                  if ( ! target_ul.DecodeHex(argv[i]) )
167                    {
168                      const ASDCP::MDDEntry *e = dict.FindSymbol(argv[i]);
169
170                      if ( e != 0 )
171                        target_ul = e->ul;
172                    }
173
174                  if ( ! target_ul.HasValue() )
175                    {
176                      fprintf(stderr, "Value is not a UL or valid object name: %s\n", argv[i]);
177                      return;
178                    }
179                }
180              else
181                {
182                  inFileList.push_back(argv[i]);
183                }
184            }
185          else
186            {
187              fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
188              return;
189            }
190        }
191
192      if ( help_flag || version_flag )
193        return;
194      
195      if ( ! list_mode )
196        {
197          if ( inFileList.empty() )
198            {
199              fputs("Input filename(s) required.\n", stderr);
200              return;
201            }
202      
203          if ( ! target_ul.HasValue() )
204            {
205              fputs("Packet UL not set.  Use %s -u <ul> or keyword.\n", stderr);
206              return;
207            }
208        }
209
210      error_flag = false;
211    }
212  };
213
214
215 //---------------------------------------------------------------------------------------------------
216 //
217
218 int
219 main(int argc, const char** argv)
220 {
221   const Dictionary *dict = &DefaultCompositeDict();
222   CommandOptions Options(argc, argv, *dict);
223
224   if ( Options.version_flag )
225     banner();
226
227   if ( Options.help_flag )
228     usage();
229
230   if ( Options.version_flag || Options.help_flag )
231     return 0;
232
233   if ( Options.error_flag )
234     {
235       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
236       return 3;
237     }
238
239   if ( Options.list_mode )
240     {
241       DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_WARN);
242       char buf[64];
243
244       MDD_t di = (MDD_t)0;
245       while ( di < MDD_Max )
246         {
247           const MDDEntry& e = dict->Type(di);
248
249           if ( e.name != 0  && ( e.ul[4] == 1 || e.ul[4] == 2 ) )
250             {
251               if ( Options.verbose_flag )
252                 {
253                   UL tmp_ul(e.ul);
254                   printf("%s %s\n", tmp_ul.EncodeString(buf, 64), e.name);
255                 }
256               else
257                 {
258                   printf("%s\n", e.name);
259                 }
260             }
261
262           di = (MDD_t)(di + 1);
263         }
264
265       return 0;
266     }
267
268   Result_t result = RESULT_OK;
269   FileList_t::iterator fi;
270
271   for ( fi = Options.inFileList.begin(); KM_SUCCESS(result) && fi != Options.inFileList.end(); ++fi )
272     {
273       if ( Options.verbose_flag )
274         fprintf(stderr, "Opening file %s\n", (fi->c_str()));
275       
276       std::string this_prefix =  Options.prefix.empty() ? Kumu::PathSetExtension(*fi, "") + "_" : Options.prefix;
277       Kumu::FileReader reader;
278       KLVFilePacket packet;
279       char filename_buf[1024];
280       ui64_t item_counter = 0;
281
282       result = reader.OpenRead(fi->c_str());
283           
284       if ( KM_SUCCESS(result) )
285         result = packet.InitFromFile(reader);
286           
287       while ( KM_SUCCESS(result) && item_counter < Options.extract_limit )
288         {
289           if ( packet.GetUL() == Options.target_ul
290                || packet.GetUL().MatchIgnoreStream(Options.target_ul) )
291             {
292               snprintf(filename_buf, 1024, "%s%010qu%s", this_prefix.c_str(), item_counter, Options.suffix.c_str());
293
294               if ( Options.verbose_flag )
295                 fprintf(stderr, "%s (%llu bytes)\n", filename_buf, packet.ValueLength());
296
297               Kumu::FileWriter writer;
298               writer.OpenWrite(filename_buf);
299
300               if ( KM_SUCCESS(result) )
301                 {
302                   if ( Options.unwrap_mode )
303                     {
304                       result = writer.Write(packet.m_Buffer.RoData() + packet.KLLength(), packet.ValueLength());
305                     }
306                   else
307                     {
308                       result = writer.Write(packet.m_Buffer.RoData(), packet.m_Buffer.Size());
309                     }
310
311                   ++item_counter;
312                 }
313             }
314
315           if ( KM_SUCCESS(result) )
316             result = packet.InitFromFile(reader);
317         }
318           
319       if ( result == RESULT_ENDOFFILE )
320         result = RESULT_OK;
321     }
322
323   if ( KM_FAILURE(result) )
324     {
325       fputs("Program stopped on error.\n", stderr);
326       
327       if ( result != RESULT_FAIL )
328         {
329           fputs(result, stderr);
330           fputc('\n', stderr);
331         }
332       
333       return 1;
334     }
335   
336   return 0;
337 }
338
339
340 //
341 // end klvwalk.cpp
342 //