mp4creator.cpp
上传用户:sun1608
上传日期:2007-02-02
资源大小:6116k
文件大小:17k
源码类别:

流媒体/Mpeg4/MP4

开发平台:

Visual C++

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is MPEG4IP.
  13.  * 
  14.  * The Initial Developer of the Original Code is Cisco Systems Inc.
  15.  * Portions created by Cisco Systems Inc. are
  16.  * Copyright (C) Cisco Systems Inc. 2001-2002.  All Rights Reserved.
  17.  * 
  18.  * Contributor(s): 
  19.  * Dave Mackie dmackie@cisco.com
  20.  */
  21. #define MP4CREATOR_GLOBALS
  22. #include "mp4creator.h"
  23. #include "mpeg4ip_getopt.h"
  24. // forward declarations
  25. MP4TrackId* CreateMediaTracks(
  26. MP4FileHandle mp4File, 
  27. const char* inputFileName);
  28. void CreateHintTrack(
  29. MP4FileHandle mp4File, 
  30. MP4TrackId mediaTrackId,
  31. const char* payloadName, 
  32. bool interleave, 
  33. u_int16_t maxPayloadSize);
  34. void ExtractTrack(
  35. MP4FileHandle mp4File, 
  36. MP4TrackId trackId, 
  37. const char* outputFileName);
  38. // external declarations
  39. // track creators
  40. MP4TrackId* AviCreator(
  41. MP4FileHandle mp4File, const char* aviFileName);
  42. MP4TrackId AacCreator(
  43. MP4FileHandle mp4File, FILE* inFile);
  44. MP4TrackId Mp3Creator(
  45. MP4FileHandle mp4File, FILE* inFile);
  46. MP4TrackId Mp4vCreator(
  47. MP4FileHandle mp4File, FILE* inFile);
  48. // main routine
  49. int main(int argc, char** argv)
  50. {
  51. char* usageString = 
  52. "usage: %s <options> <mp4-file>n"
  53. "  Options:n"
  54. "  -create=<input-file>    Create track from <input-file>n"
  55. "    input files can be of type: .aac .mp3 .divx .mp4v .m4v .cmp .xvidn"
  56. "  -extract=<track-id>     Extract a trackn"
  57. "  -delete=<track-id>      Delete a trackn"
  58. "  -hint[=<track-id>]      Create hint track, also -Hn"
  59. "  -interleave             Use interleaved audio payload format, also -In"
  60. "  -list                   List tracks in mp4 filen"
  61. "  -mtu=<size>             MTU for hint trackn"
  62. "  -optimize               Optimize mp4 file layoutn"
  63.         "  -payload=<payload>      Rtp payload type n"
  64.                 "                          (use 3119 or mpa-robust for mp3 rfc 3119 support)n"
  65. "  -rate=<fps>             Video frame rate, e.g. 30 or 29.97n"
  66. "  -timescale=<ticks>      Time scale (ticks per second)n"
  67. "  -verbose[=[1-5]]        Enable debug messagesn"
  68.         "  -version                Display version informationn"
  69. ;
  70. bool doCreate = false;
  71. bool doExtract = false;
  72. bool doDelete = false;
  73. bool doHint = false;
  74. bool doList = false;
  75. bool doOptimize = false;
  76. bool doInterleave = false;
  77. char* mp4FileName = NULL;
  78. char* inputFileName = NULL;
  79. char* outputFileName = NULL;
  80. char* payloadName = NULL;
  81. MP4TrackId hintTrackId = MP4_INVALID_TRACK_ID;
  82. MP4TrackId extractTrackId = MP4_INVALID_TRACK_ID;
  83. MP4TrackId deleteTrackId = MP4_INVALID_TRACK_ID;
  84. u_int16_t maxPayloadSize = 1460;
  85. Verbosity = MP4_DETAILS_ERROR;
  86. VideoFrameRate = 0; // determine from input file
  87. Mp4TimeScale = 90000;
  88. // begin processing command line
  89. ProgName = argv[0];
  90. while (true) {
  91. int c = -1;
  92. int option_index = 0;
  93. static struct option long_options[] = {
  94. { "create", 1, 0, 'c' },
  95. { "delete", 1, 0, 'd' },
  96. { "extract", 1, 0, 'e' },
  97. { "help", 0, 0, '?' },
  98. { "hint", 2, 0, 'H' },
  99. { "interleave", 0, 0, 'I' },
  100. { "list", 0, 0, 'l' },
  101. { "mtu", 1, 0, 'm' },
  102. { "optimize", 0, 0, 'O' },
  103. { "payload", 1, 0, 'p' },
  104. { "rate", 1, 0, 'r' },
  105. { "timescale", 1, 0, 't' },
  106. { "verbose", 2, 0, 'v' },
  107. { "version", 0, 0, 'V' },
  108. { NULL, 0, 0, 0 }
  109. };
  110. c = getopt_long_only(argc, argv, "c:d:e:H::Ilm:Op:r:t:v::V",
  111. long_options, &option_index);
  112. if (c == -1)
  113. break;
  114. switch (c) {
  115. case 'c':
  116. doCreate = true;
  117. inputFileName = optarg;
  118. break;
  119. case 'd':
  120. if (sscanf(optarg, "%u", &deleteTrackId) != 1) {
  121. fprintf(stderr, 
  122. "%s: bad track-id specified: %sn",
  123.  ProgName, optarg);
  124. exit(EXIT_COMMAND_LINE);
  125. }
  126. doDelete = true;
  127. break;
  128. case 'e':
  129. if (sscanf(optarg, "%u", &extractTrackId) != 1) {
  130. fprintf(stderr, 
  131. "%s: bad track-id specified: %sn",
  132.  ProgName, optarg);
  133. exit(EXIT_COMMAND_LINE);
  134. }
  135. doExtract = true;
  136. break;
  137. case 'H':
  138. doHint = true;
  139. if (optarg) {
  140. if (sscanf(optarg, "%u", &hintTrackId) != 1) {
  141. fprintf(stderr, 
  142. "%s: bad track-id specified: %sn",
  143.  ProgName, optarg);
  144. exit(EXIT_COMMAND_LINE);
  145. }
  146. }
  147. break;
  148. case 'I':
  149. doInterleave = true;
  150. break;
  151. case 'l':
  152. doList = true;
  153. break;
  154. case 'm':
  155. u_int32_t mtu;
  156. if (sscanf(optarg, "%u", &mtu) != 1 || mtu < 64) {
  157. fprintf(stderr, 
  158. "%s: bad mtu specified: %sn",
  159.  ProgName, optarg);
  160. exit(EXIT_COMMAND_LINE);
  161. }
  162. maxPayloadSize = mtu - 40; // subtract IP, UDP, and RTP hdrs
  163. break;
  164. case 'O':
  165. doOptimize = true;
  166. break;
  167. case 'p':
  168. payloadName = optarg;
  169. break;
  170. case 'r':
  171. if (sscanf(optarg, "%f", &VideoFrameRate) != 1) {
  172. fprintf(stderr, 
  173. "%s: bad rate specified: %sn",
  174.  ProgName, optarg);
  175. exit(EXIT_COMMAND_LINE);
  176. }
  177. break;
  178. case 't':
  179. if (sscanf(optarg, "%u", &Mp4TimeScale) != 1) {
  180. fprintf(stderr, 
  181. "%s: bad timescale specified: %sn",
  182.  ProgName, optarg);
  183. exit(EXIT_COMMAND_LINE);
  184. }
  185. break;
  186. case 'v':
  187. Verbosity |= (MP4_DETAILS_READ | MP4_DETAILS_WRITE);
  188. if (optarg) {
  189. u_int32_t level;
  190. if (sscanf(optarg, "%u", &level) == 1) {
  191. if (level >= 2) {
  192. Verbosity |= MP4_DETAILS_TABLE;
  193. if (level >= 3) {
  194. Verbosity |= MP4_DETAILS_SAMPLE;
  195. if (level >= 4) {
  196. Verbosity |= MP4_DETAILS_HINT;
  197. if (level >= 5) {
  198. Verbosity = MP4_DETAILS_ALL;
  199. }
  200. }
  201. break;
  202. case '?':
  203. fprintf(stderr, usageString, ProgName);
  204. exit(EXIT_SUCCESS);
  205. case 'V':
  206.   fprintf(stderr, "%s - %s version %sn", 
  207.   ProgName, PACKAGE, VERSION);
  208.   exit(EXIT_SUCCESS);
  209. default:
  210. fprintf(stderr, "%s: unknown option specified, ignoring: %cn", 
  211. ProgName, c);
  212. }
  213. }
  214. // check that we have at least one non-option argument
  215. if ((argc - optind) < 1) {
  216. fprintf(stderr, usageString, ProgName);
  217. exit(EXIT_COMMAND_LINE);
  218. }
  219. if ((argc - optind) == 1) {
  220. mp4FileName = argv[optind++];
  221. } else {
  222. // it appears we have two file names
  223. if (doExtract) {
  224. mp4FileName = argv[optind++];
  225. outputFileName = argv[optind++];
  226. } else {
  227. if (inputFileName == NULL) {
  228. // then assume -c for the first file name
  229. doCreate = true;
  230. inputFileName = argv[optind++];
  231. }
  232. mp4FileName = argv[optind++];
  233. }
  234. }
  235. // warn about extraneous non-option arguments
  236. if (optind < argc) {
  237. fprintf(stderr, "%s: unknown options specified, ignoring: ", ProgName);
  238. while (optind < argc) {
  239. fprintf(stderr, "%s ", argv[optind++]);
  240. }
  241. fprintf(stderr, "n");
  242. }
  243. // operations consistency checks
  244. if (!doList && !doCreate && !doHint 
  245.   && !doOptimize && !doExtract && !doDelete) {
  246. fprintf(stderr, 
  247. "%s: no operation specifiedn",
  248.  ProgName);
  249. exit(EXIT_COMMAND_LINE);
  250. }
  251. if ((doCreate || doHint) && doExtract) {
  252. fprintf(stderr, 
  253. "%s: extract operation must be done separatelyn",
  254.  ProgName);
  255. exit(EXIT_COMMAND_LINE);
  256. }
  257. if ((doCreate || doHint) && doDelete) {
  258. fprintf(stderr, 
  259. "%s: delete operation must be done separatelyn",
  260.  ProgName);
  261. exit(EXIT_COMMAND_LINE);
  262. }
  263. if (doExtract && doDelete) {
  264. fprintf(stderr, 
  265. "%s: extract and delete operations must be done separatelyn",
  266.  ProgName);
  267. exit(EXIT_COMMAND_LINE);
  268. }
  269. // end processing of command line
  270. if (doList) {
  271. // just want the track listing
  272. char* info = MP4FileInfo(mp4FileName);
  273. if (!info) {
  274. fprintf(stderr, 
  275. "%s: can't open %sn", 
  276. ProgName, mp4FileName);
  277. exit(EXIT_INFO);
  278. }
  279. fputs(info, stdout);
  280. free(info);
  281. exit(EXIT_SUCCESS);
  282. }
  283. // test if mp4 file exists
  284. bool mp4FileExists = (access(mp4FileName, F_OK) == 0);
  285. MP4FileHandle mp4File;
  286. if (doCreate || doHint) {
  287. if (!mp4FileExists) {
  288. if (doCreate) {
  289. mp4File = MP4Create(mp4FileName, Verbosity);
  290. if (mp4File) {
  291. MP4SetTimeScale(mp4File, Mp4TimeScale);
  292. }
  293. } else {
  294. fprintf(stderr,
  295. "%s: can't hint track in file that doesn't existn", 
  296. ProgName);
  297. exit(EXIT_CREATE_FILE);
  298. }
  299. } else {
  300. mp4File = MP4Modify(mp4FileName, Verbosity);
  301. }
  302. if (!mp4File) {
  303. // mp4 library should have printed a message
  304. exit(EXIT_CREATE_FILE);
  305. }
  306. bool allMpeg4Streams = true;
  307. if (doCreate) {
  308. MP4TrackId* pCreatedTrackIds = 
  309. CreateMediaTracks(mp4File, inputFileName);
  310. if (pCreatedTrackIds) {
  311. // decide if we can raise the ISMA compliance tag in SDP
  312. // we do this if audio and/or video are MPEG-4
  313. MP4TrackId* pTrackId = pCreatedTrackIds;
  314. while (*pTrackId != MP4_INVALID_TRACK_ID) {
  315. const char *type =
  316. MP4GetTrackType(mp4File, *pTrackId);
  317. if (!strcmp(type, MP4_AUDIO_TRACK_TYPE)) { 
  318. allMpeg4Streams &=
  319. (MP4GetTrackAudioType(mp4File, *pTrackId) 
  320. == MP4_MPEG4_AUDIO_TYPE);
  321. } else if (!strcmp(type, MP4_VIDEO_TRACK_TYPE)) { 
  322. allMpeg4Streams &=
  323. (MP4GetTrackVideoType(mp4File, *pTrackId)
  324. == MP4_MPEG4_VIDEO_TYPE);
  325. }
  326. pTrackId++;
  327. }
  328. }
  329. if (pCreatedTrackIds && doHint) {
  330. MP4TrackId* pTrackId = pCreatedTrackIds;
  331. while (*pTrackId != MP4_INVALID_TRACK_ID) {
  332. CreateHintTrack(mp4File, *pTrackId, 
  333. payloadName, doInterleave, maxPayloadSize);
  334. pTrackId++;
  335. }
  336. }
  337. } else {
  338. CreateHintTrack(mp4File, hintTrackId, 
  339. payloadName, doInterleave, maxPayloadSize);
  340. MP4Close(mp4File);
  341. if (doCreate) {
  342. // this creates simple MPEG-4 OD and BIFS streams
  343. MP4MakeIsmaCompliant(mp4FileName, Verbosity, allMpeg4Streams);
  344. }
  345. } else if (doExtract) {
  346. if (!mp4FileExists) {
  347. fprintf(stderr,
  348. "%s: can't extract track in file that doesn't existn", 
  349. ProgName);
  350. exit(EXIT_CREATE_FILE);
  351. }
  352. mp4File = MP4Read(mp4FileName, Verbosity);
  353. if (!mp4File) {
  354. // mp4 library should have printed a message
  355. exit(EXIT_CREATE_FILE);
  356. }
  357. char tempName[PATH_MAX];
  358. if (outputFileName == NULL) {
  359. snprintf(tempName, sizeof(tempName), 
  360. "%s.t%u", mp4FileName, extractTrackId);
  361. outputFileName = tempName;
  362. }
  363. ExtractTrack(mp4File, extractTrackId, outputFileName);
  364. MP4Close(mp4File);
  365. } else if (doDelete) {
  366. if (!mp4FileExists) {
  367. fprintf(stderr,
  368. "%s: can't delete track in file that doesn't existn", 
  369. ProgName);
  370. exit(EXIT_CREATE_FILE);
  371. }
  372. mp4File = MP4Modify(mp4FileName, Verbosity);
  373. if (!mp4File) {
  374. // mp4 library should have printed a message
  375. exit(EXIT_CREATE_FILE);
  376. }
  377. MP4DeleteTrack(mp4File, deleteTrackId);
  378. MP4Close(mp4File);
  379. doOptimize = true; // to purge unreferenced track data
  380. }
  381. if (doOptimize) {
  382. if (!MP4Optimize(mp4FileName, NULL, Verbosity)) {
  383. // mp4 library should have printed a message
  384. exit(EXIT_OPTIMIZE_FILE);
  385. }
  386. }
  387. return(EXIT_SUCCESS);
  388. }
  389. MP4TrackId* CreateMediaTracks(MP4FileHandle mp4File, const char* inputFileName)
  390. {
  391. FILE* inFile = fopen(inputFileName, "rb");
  392. if (inFile == NULL) {
  393. fprintf(stderr, 
  394. "%s: can't open file %s: %sn",
  395. ProgName, inputFileName, strerror(errno));
  396. exit(EXIT_CREATE_MEDIA);
  397. }
  398. struct stat s;
  399. if (fstat(fileno(inFile), &s) < 0) {
  400. fprintf(stderr, 
  401. "%s: can't stat file %s: %sn",
  402. ProgName, inputFileName, strerror(errno));
  403. exit(EXIT_CREATE_MEDIA);
  404. }
  405. if (s.st_size == 0) {
  406. fprintf(stderr, 
  407. "%s: file %s is emptyn",
  408. ProgName, inputFileName);
  409. exit(EXIT_CREATE_MEDIA);
  410. }
  411. const char* extension = strrchr(inputFileName, '.');
  412. if (extension == NULL) {
  413. fprintf(stderr, 
  414. "%s: no file type extensionn", ProgName);
  415. exit(EXIT_CREATE_MEDIA);
  416. }
  417. static MP4TrackId trackIds[2] = {
  418. MP4_INVALID_TRACK_ID, MP4_INVALID_TRACK_ID
  419. };
  420. MP4TrackId* pTrackIds = trackIds;
  421. if (!strcasecmp(extension, ".avi")) {
  422. fclose(inFile);
  423. inFile = NULL;
  424. pTrackIds = AviCreator(mp4File, inputFileName);
  425. } else if (!strcasecmp(extension, ".aac")) {
  426. trackIds[0] = AacCreator(mp4File, inFile);
  427. } else if (!strcasecmp(extension, ".mp3")
  428.   || !strcasecmp(extension, ".mp1")
  429.   || !strcasecmp(extension, ".mp2")) {
  430. trackIds[0] = Mp3Creator(mp4File, inFile);
  431. } else if (!strcasecmp(extension, ".divx")
  432.   || !strcasecmp(extension, ".mp4v")
  433.   || !strcasecmp(extension, ".m4v")
  434.   || !strcasecmp(extension, ".xvid")
  435.   || !strcasecmp(extension, ".cmp")) {
  436. trackIds[0] = Mp4vCreator(mp4File, inFile);
  437. } else {
  438. fprintf(stderr, 
  439. "%s: unknown file typen", ProgName);
  440. exit(EXIT_CREATE_MEDIA);
  441. }
  442. if (inFile) {
  443. fclose(inFile);
  444. }
  445. return pTrackIds;
  446. }
  447. void CreateHintTrack(MP4FileHandle mp4File, MP4TrackId mediaTrackId,
  448. const char* payloadName, bool interleave, u_int16_t maxPayloadSize)
  449. {
  450. bool rc;
  451. if (MP4GetTrackNumberOfSamples(mp4File, mediaTrackId) == 0) {
  452. fprintf(stderr, 
  453. "%s: couldn't create hint track, no media samplesn", ProgName);
  454. exit(EXIT_CREATE_HINT);
  455. }
  456. // vector out to specific hinters
  457. const char* trackType = MP4GetTrackType(mp4File, mediaTrackId);
  458. if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
  459. u_int8_t audioType = MP4GetTrackAudioType(mp4File, mediaTrackId);
  460. switch (audioType) {
  461. case MP4_MPEG4_AUDIO_TYPE:
  462. case MP4_MPEG2_AAC_MAIN_AUDIO_TYPE:
  463. case MP4_MPEG2_AAC_LC_AUDIO_TYPE:
  464. case MP4_MPEG2_AAC_SSR_AUDIO_TYPE:
  465. rc = MP4AV_RfcIsmaHinter(mp4File, mediaTrackId, 
  466. interleave, maxPayloadSize);
  467. break;
  468. case MP4_MPEG1_AUDIO_TYPE:
  469. case MP4_MPEG2_AUDIO_TYPE:
  470. if (payloadName && 
  471.   (!strcasecmp(payloadName, "3119") 
  472.   || !strcasecmp(payloadName, "mpa-robust"))) {
  473. rc = MP4AV_Rfc3119Hinter(mp4File, mediaTrackId, 
  474. interleave, maxPayloadSize);
  475. } else {
  476. rc = MP4AV_Rfc2250Hinter(mp4File, mediaTrackId, 
  477. false, maxPayloadSize);
  478. }
  479. break;
  480. default:
  481. fprintf(stderr, 
  482. "%s: can't hint non-MPEG4/non-MP3 audio typen", ProgName);
  483. exit(EXIT_CREATE_HINT);
  484. }
  485. } else if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE)) {
  486. u_int8_t videoType = MP4GetTrackVideoType(mp4File, mediaTrackId);
  487. if (videoType == MP4_MPEG4_VIDEO_TYPE) {
  488. rc = MP4AV_Rfc3016Hinter(mp4File, mediaTrackId, maxPayloadSize);
  489. } else {
  490. fprintf(stderr, 
  491. "%s: can't hint non-MPEG4 video typen", ProgName);
  492. exit(EXIT_CREATE_HINT);
  493. }
  494. } else {
  495. fprintf(stderr, 
  496. "%s: can't hint track type %sn", ProgName, trackType);
  497. exit(EXIT_CREATE_HINT);
  498. }
  499. if (!rc) {
  500. fprintf(stderr, 
  501. "%s: error hinting track %un", ProgName, mediaTrackId);
  502. exit(EXIT_CREATE_HINT);
  503. }
  504. }
  505. void ExtractTrack(
  506. MP4FileHandle mp4File, 
  507. MP4TrackId trackId, 
  508. const char* outputFileName)
  509. {
  510. int outFd = open(outputFileName, 
  511. O_WRONLY | O_CREAT | O_TRUNC, 0644);
  512. if (outFd == -1) {
  513. fprintf(stderr, "%s: can't open %s: %sn",
  514. ProgName, outputFileName, strerror(errno));
  515. exit(EXIT_EXTRACT_TRACK);
  516. }
  517. // some track types have special needs
  518. // to properly recreate their raw ES file
  519. bool prependES = false;
  520. bool prependADTS = false;
  521. const char* trackType =
  522. MP4GetTrackType(mp4File, trackId);
  523. if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE)) {
  524. if (MP4_IS_MPEG4_VIDEO_TYPE(MP4GetTrackVideoType(mp4File, trackId))) {
  525. prependES = true;
  526. }
  527. } else if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
  528. if (MP4_IS_AAC_AUDIO_TYPE(MP4GetTrackAudioType(mp4File, trackId))) {
  529. prependADTS = true;
  530. }
  531. }
  532. MP4SampleId numSamples = 
  533. MP4GetTrackNumberOfSamples(mp4File, trackId);
  534. u_int8_t* pSample;
  535. u_int32_t sampleSize;
  536. // extraction loop
  537. for (MP4SampleId sampleId = 1 ; sampleId <= numSamples; sampleId++) {
  538. int rc;
  539. // signal to ReadSample() 
  540. // that it should malloc a buffer for us
  541. pSample = NULL;
  542. sampleSize = 0;
  543. if (prependADTS) {
  544. // need some very specialized work for these
  545. MP4AV_AdtsMakeFrameFromMp4Sample(
  546. mp4File,
  547. trackId,
  548. sampleId,
  549. &pSample,
  550. &sampleSize);
  551. } else {
  552. // read the sample
  553. rc = MP4ReadSample(
  554. mp4File, 
  555. trackId, 
  556. sampleId, 
  557. &pSample, 
  558. &sampleSize);
  559. if (rc == 0) {
  560. fprintf(stderr, "%s: read sample %u for %s failedn",
  561. ProgName, sampleId, outputFileName);
  562. exit(EXIT_EXTRACT_TRACK);
  563. }
  564. if (prependES && sampleId == 1) {
  565. u_int8_t* pConfig = NULL;
  566. u_int32_t configSize = 0;
  567. MP4GetTrackESConfiguration(mp4File, trackId, 
  568. &pConfig, &configSize);
  569. write(outFd, pConfig, configSize);
  570. free(pConfig);
  571. }
  572. }
  573. rc = write(outFd, pSample, sampleSize);
  574. if (rc == -1 || (u_int32_t)rc != sampleSize) {
  575. fprintf(stderr, "%s: write to %s failed: %sn",
  576. ProgName, outputFileName, strerror(errno));
  577. exit(EXIT_EXTRACT_TRACK);
  578. }
  579. free(pSample);
  580. }
  581. // close ES file
  582. close(outFd);
  583. }