/src/flvmeta.c

https://code.google.com/ · C · 404 lines · 321 code · 22 blank · 61 comment · 56 complexity · 5d97fada5d7f1ff7a6abbbc2c7ffa120 MD5 · raw file

  1. /*
  2. $Id: flvmeta.c 231 2011-06-27 13:46:19Z marc.noirot $
  3. FLV Metadata updater
  4. Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
  5. This file is part of FLVMeta.
  6. FLVMeta 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.
  10. FLVMeta is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with FLVMeta; if not, write to the Free Software
  16. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. */
  18. #include <getopt.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "flvmeta.h"
  23. #include "check.h"
  24. #include "dump.h"
  25. #include "update.h"
  26. /*
  27. Command-line options
  28. */
  29. static struct option long_options[] = {
  30. { "dump", no_argument, NULL, 'D'},
  31. { "full-dump", no_argument, NULL, 'F'},
  32. { "check", no_argument, NULL, 'C'},
  33. { "update", no_argument, NULL, 'U'},
  34. { "dump-format", required_argument, NULL, 'd'},
  35. { "json", no_argument, NULL, 'j'},
  36. { "raw", no_argument, NULL, 'r'},
  37. { "xml", no_argument, NULL, 'x'},
  38. { "yaml", no_argument, NULL, 'y'},
  39. { "event", required_argument, NULL, 'e'},
  40. { "level", required_argument, NULL, 'l'},
  41. { "quiet", no_argument, NULL, 'q'},
  42. { "print-metadata", no_argument, NULL, 'm'},
  43. { "add", required_argument, NULL, 'a'},
  44. { "no-lastsecond", no_argument, NULL, 's'},
  45. { "preserve", no_argument, NULL, 'p'},
  46. { "fix", no_argument, NULL, 'f'},
  47. { "ignore", no_argument, NULL, 'i'},
  48. { "reset-timestamps", no_argument, NULL, 't'},
  49. { "all-keyframes", no_argument, NULL, 'k'},
  50. { "verbose", no_argument, NULL, 'v'},
  51. { "version", no_argument, NULL, 'V'},
  52. { "help", no_argument, NULL, 'h'},
  53. { 0, 0, 0, 0 }
  54. };
  55. /* short options */
  56. #define DUMP_COMMAND "D"
  57. #define FULL_DUMP_COMMAND "F"
  58. #define CHECK_COMMAND "C"
  59. #define UPDATE_COMMAND "U"
  60. #define DUMP_FORMAT_OPTION "d:"
  61. #define JSON_OPTION "j"
  62. #define RAW_OPTION "r"
  63. #define XML_OPTION "x"
  64. #define YAML_OPTION "y"
  65. #define EVENT_OPTION "e:"
  66. #define LEVEL_OPTION "l:"
  67. #define QUIET_OPTION "q"
  68. #define PRINT_METADATA_OPTION "m"
  69. #define ADD_OPTION "a:"
  70. #define NO_LASTSECOND_OPTION "s"
  71. #define PRESERVE_OPTION "p"
  72. #define FIX_OPTION "f"
  73. #define IGNORE_OPTION "i"
  74. #define RESET_TIMESTAMPS_OPTION "t"
  75. #define ALL_KEYFRAMES_OPTION "k"
  76. #define VERBOSE_OPTION "v"
  77. #define VERSION_OPTION "V"
  78. #define HELP_OPTION "h"
  79. static void version(void) {
  80. printf("%s\n\n", PACKAGE_STRING);
  81. printf("%s\n", COPYRIGHT_STR);
  82. printf("This is free software; see the source for copying conditions. There is NO\n"
  83. "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
  84. }
  85. static void usage(const char * name) {
  86. fprintf(stderr, "Usage: %s [COMMAND] [OPTIONS] INPUT_FILE [OUTPUT_FILE]\n", name);
  87. fprintf(stderr, "Try `%s --help' for more information.\n", name);
  88. }
  89. static void help(const char * name) {
  90. printf("Usage: %s [COMMAND] [OPTIONS] INPUT_FILE [OUTPUT_FILE]\n", name);
  91. printf("\nIf OUTPUT_FILE is omitted for commands expecting it, INPUT_FILE will be overwritten instead.\n"
  92. "\nCommands:\n"
  93. " -D, --dump dump onMetaData tag (default without output file)\n"
  94. " -F, --full-dump dump all tags\n"
  95. " -C, --check check the validity of INPUT_FILE, returning 0 if\n"
  96. " the file is valid, or 10 if it contains errors\n"
  97. " -U, --update update computed onMetaData tag from INPUT_FILE\n"
  98. " into OUTPUT_FILE (default with output file)\n"
  99. /* " -A, --extract-audio extract raw audio data into OUTPUT_FILE\n"*/
  100. /* " -E, --extract-video extract raw video data into OUTPUT_FILE\n"*/
  101. "\nDump options:\n"
  102. " -d, --dump-format=TYPE dump format is of type TYPE\n"
  103. " TYPE is 'xml' (default), 'json', 'raw', or 'yaml'\n"
  104. " -j, --json equivalent to --dump-format=json\n"
  105. " -r, --raw equivalent to --dump-format=raw\n"
  106. " -x, --xml equivalent to --dump-format=xml\n"
  107. " -y, --yaml equivalent to --dump-format=yaml\n"
  108. " -e, --event=EVENT specify the event to be dumped instead of 'onMetadata'\n"
  109. "\nCheck options:\n"
  110. " -l, --level=LEVEL print only messages where level is at least LEVEL\n"
  111. " LEVEL is 'info', 'warning' (default), 'error', or 'fatal'\n"
  112. " -q, --quiet do not print messages, only return the status code\n"
  113. " -x, --xml generate an XML report\n"
  114. "\nUpdate options:\n"
  115. " -m, --print-metadata print metadata to stdout after update using\n"
  116. " the specified format\n"
  117. " -a, --add=NAME=VALUE add a metadata string value to the output file\n"
  118. " -s, --no-lastsecond do not create the onLastSecond tag\n"
  119. " -p, --preserve preserve input file existing onMetadata tags\n"
  120. " -f, --fix fix invalid tags from the input file\n"
  121. " -i, --ignore ignore invalid tags from the input file\n"
  122. " (the default is to stop with an error)\n"
  123. " -t, --reset-timestamps reset timestamps so OUTPUT_FILE starts at zero\n"
  124. " -k, --all-keyframes index all keyframe tags, including duplicate timestamps\n"
  125. "\nCommon options:\n"
  126. " -v, --verbose display informative messages\n"
  127. "\nMiscellaneous:\n"
  128. " -V, --version print version information and exit\n"
  129. " -h, --help display this information and exit\n");
  130. printf("\nPlease report bugs to <%s>\n", PACKAGE_BUGREPORT);
  131. }
  132. static int parse_command_line(int argc, char ** argv, flvmeta_opts * options) {
  133. int option, option_index;
  134. option_index = 0;
  135. do {
  136. option = getopt_long(argc, argv,
  137. DUMP_COMMAND
  138. FULL_DUMP_COMMAND
  139. CHECK_COMMAND
  140. UPDATE_COMMAND
  141. DUMP_FORMAT_OPTION
  142. JSON_OPTION
  143. RAW_OPTION
  144. XML_OPTION
  145. YAML_OPTION
  146. EVENT_OPTION
  147. LEVEL_OPTION
  148. QUIET_OPTION
  149. PRINT_METADATA_OPTION
  150. ADD_OPTION
  151. NO_LASTSECOND_OPTION
  152. PRESERVE_OPTION
  153. FIX_OPTION
  154. IGNORE_OPTION
  155. RESET_TIMESTAMPS_OPTION
  156. ALL_KEYFRAMES_OPTION
  157. VERBOSE_OPTION
  158. VERSION_OPTION
  159. HELP_OPTION,
  160. long_options, &option_index);
  161. switch (option) {
  162. /*
  163. commands
  164. */
  165. case 'D':
  166. if (options->command != FLVMETA_DEFAULT_COMMAND) {
  167. fprintf(stderr, "%s: only one command can be specified -- %s\n", argv[0], argv[optind]);
  168. return EXIT_FAILURE;
  169. }
  170. options->command = FLVMETA_DUMP_COMMAND;
  171. break;
  172. case 'F':
  173. if (options->command != FLVMETA_DEFAULT_COMMAND) {
  174. fprintf(stderr, "%s: only one command can be specified -- %s\n", argv[0], argv[optind]);
  175. return EXIT_FAILURE;
  176. }
  177. options->command = FLVMETA_FULL_DUMP_COMMAND;
  178. break;
  179. case 'C':
  180. if (options->command != FLVMETA_DEFAULT_COMMAND) {
  181. fprintf(stderr, "%s: only one command can be specified -- %s\n", argv[0], argv[optind]);
  182. return EXIT_FAILURE;
  183. }
  184. options->command = FLVMETA_CHECK_COMMAND;
  185. break;
  186. case 'U':
  187. if (options->command != FLVMETA_DEFAULT_COMMAND) {
  188. fprintf(stderr, "%s: only one command can be specified -- %s\n", argv[0], argv[optind]);
  189. return EXIT_FAILURE;
  190. }
  191. options->command = FLVMETA_UPDATE_COMMAND;
  192. break;
  193. /*
  194. options
  195. */
  196. /* check options */
  197. case 'l':
  198. if (!strcmp(optarg, "info")) {
  199. options->check_level = FLVMETA_CHECK_LEVEL_INFO;
  200. }
  201. else if (!strcmp(optarg, "warning")) {
  202. options->check_level = FLVMETA_CHECK_LEVEL_WARNING;
  203. }
  204. else if (!strcmp(optarg, "error")) {
  205. options->check_level = FLVMETA_CHECK_LEVEL_ERROR;
  206. }
  207. else if (!strcmp(optarg, "fatal")) {
  208. options->check_level = FLVMETA_CHECK_LEVEL_FATAL;
  209. }
  210. else {
  211. fprintf(stderr, "%s: invalid message level -- %s\n", argv[0], optarg);
  212. usage(argv[0]);
  213. return EXIT_FAILURE;
  214. }
  215. break;
  216. case 'q': options->quiet = 1; break;
  217. /* dump options */
  218. case 'd':
  219. if (!strcmp(optarg, "xml")) {
  220. options->dump_format = FLVMETA_FORMAT_XML;
  221. }
  222. if (!strcmp(optarg, "raw")) {
  223. options->dump_format = FLVMETA_FORMAT_RAW;
  224. }
  225. else if (!strcmp(optarg, "json")) {
  226. options->dump_format = FLVMETA_FORMAT_JSON;
  227. }
  228. else if (!strcmp(optarg, "yaml")) {
  229. options->dump_format = FLVMETA_FORMAT_YAML;
  230. }
  231. else {
  232. fprintf(stderr, "%s: invalid output format -- %s\n", argv[0], optarg);
  233. usage(argv[0]);
  234. return EXIT_FAILURE;
  235. }
  236. break;
  237. case 'j': options->dump_format = FLVMETA_FORMAT_JSON; break;
  238. case 'r': options->dump_format = FLVMETA_FORMAT_RAW; break;
  239. case 'x':
  240. /* xml dump format, or generation of xml report */
  241. options->dump_format = FLVMETA_FORMAT_XML;
  242. options->check_xml_report = 1;
  243. break;
  244. case 'y': options->dump_format = FLVMETA_FORMAT_YAML; break;
  245. case 'e': options->metadata_event = optarg; break;
  246. /* update options */
  247. case 'm': options->dump_metadata = 1; break;
  248. case 'a':
  249. {
  250. char * eq_pos;
  251. eq_pos = strchr(optarg, '=');
  252. if (eq_pos != NULL) {
  253. *eq_pos = 0;
  254. if (options->metadata == NULL) {
  255. options->metadata = amf_associative_array_new();
  256. }
  257. amf_associative_array_add(options->metadata, optarg, amf_str(eq_pos + 1));
  258. }
  259. else {
  260. fprintf(stderr, "%s: invalid metadata format -- %s\n", argv[0], optarg);
  261. usage(argv[0]);
  262. return EXIT_FAILURE;
  263. }
  264. } break;
  265. case 's': options->insert_onlastsecond = 0; break;
  266. case 'p': options->preserve_metadata = 1; break;
  267. case 'f': options->error_handling = FLVMETA_FIX_ERRORS; break;
  268. case 'i': options->error_handling = FLVMETA_IGNORE_ERRORS; break;
  269. case 't': options->reset_timestamps = 1; break;
  270. case 'k': options->all_keyframes = 1; break;
  271. /*
  272. common options
  273. */
  274. case 'v': options->verbose = 1; break;
  275. /*
  276. Miscellaneous
  277. */
  278. case 'V':
  279. options->command = FLVMETA_VERSION_COMMAND;
  280. return OK;
  281. break;
  282. case 'h':
  283. options->command = FLVMETA_HELP_COMMAND;
  284. return OK;
  285. break;
  286. /* last option */
  287. case EOF: break;
  288. /*
  289. error cases
  290. */
  291. /* unknown option */
  292. case '?':
  293. /* missing argument */
  294. case ':':
  295. /* we should not be here */
  296. default:
  297. usage(argv[0]);
  298. return EXIT_FAILURE;
  299. }
  300. } while (option != EOF);
  301. /* input filename */
  302. if (optind > 0 && optind < argc) {
  303. options->input_file = argv[optind];
  304. }
  305. else {
  306. fprintf(stderr, "%s: no input file\n", argv[0]);
  307. usage(argv[0]);
  308. return EXIT_FAILURE;
  309. }
  310. /* output filename */
  311. if (++optind < argc) {
  312. options->output_file = argv[optind];
  313. }
  314. /* determine command if default */
  315. if (options->command == FLVMETA_DEFAULT_COMMAND && options->output_file != NULL) {
  316. options->command = FLVMETA_UPDATE_COMMAND;
  317. }
  318. else if (options->command == FLVMETA_DEFAULT_COMMAND && options->output_file == NULL) {
  319. options->command = FLVMETA_DUMP_COMMAND;
  320. }
  321. /* output file is input file if not specified */
  322. if (options->output_file == NULL) {
  323. options->output_file = options->input_file;
  324. }
  325. return OK;
  326. }
  327. int main(int argc, char ** argv) {
  328. int errcode;
  329. /* flvmeta default options */
  330. static flvmeta_opts options;
  331. options.command = FLVMETA_DEFAULT_COMMAND;
  332. options.input_file = NULL;
  333. options.output_file = NULL;
  334. options.metadata = NULL;
  335. options.check_level = FLVMETA_CHECK_LEVEL_WARNING;
  336. options.quiet = 0;
  337. options.check_xml_report = 0;
  338. options.dump_metadata = 0;
  339. options.insert_onlastsecond = 1;
  340. options.reset_timestamps = 0;
  341. options.all_keyframes = 0;
  342. options.preserve_metadata = 0;
  343. options.error_handling = FLVMETA_EXIT_ON_ERROR;
  344. options.dump_format = FLVMETA_FORMAT_XML;
  345. options.verbose = 0;
  346. options.metadata_event = NULL;
  347. /* Command-line parsing */
  348. errcode = parse_command_line(argc, argv, &options);
  349. /* free metadata if necessary */
  350. if ((errcode != OK || options.command != FLVMETA_UPDATE_COMMAND) && options.metadata != NULL) {
  351. amf_data_free(options.metadata);
  352. }
  353. if (errcode == OK) {
  354. /* execute command */
  355. switch (options.command) {
  356. case FLVMETA_DUMP_COMMAND: errcode = dump_metadata(&options); break;
  357. case FLVMETA_FULL_DUMP_COMMAND: errcode = dump_flv_file(&options); break;
  358. case FLVMETA_CHECK_COMMAND: errcode = check_flv_file(&options); break;
  359. case FLVMETA_UPDATE_COMMAND: errcode = update_metadata(&options); break;
  360. case FLVMETA_VERSION_COMMAND: version(); break;
  361. case FLVMETA_HELP_COMMAND: help(argv[0]); break;
  362. }
  363. /* error report */
  364. switch (errcode) {
  365. case ERROR_OPEN_READ: fprintf(stderr, "%s: cannot open %s for reading\n", argv[0], options.input_file); break;
  366. case ERROR_NO_FLV: fprintf(stderr, "%s: %s is not a valid FLV file\n", argv[0], options.input_file); break;
  367. case ERROR_EOF: fprintf(stderr, "%s: unexpected end of file\n", argv[0]); break;
  368. case ERROR_MEMORY: fprintf(stderr, "%s: memory allocation error\n", argv[0]); break;
  369. case ERROR_EMPTY_TAG: fprintf(stderr, "%s: empty FLV tag\n", argv[0]); break;
  370. case ERROR_OPEN_WRITE: fprintf(stderr, "%s: cannot open %s for writing\n", argv[0], options.output_file); break;
  371. case ERROR_INVALID_TAG: fprintf(stderr, "%s: invalid FLV tag\n", argv[0]); break;
  372. case ERROR_WRITE: fprintf(stderr, "%s: unable to write to %s\n", argv[0], options.output_file); break;
  373. }
  374. }
  375. return errcode;
  376. }