Advertisement
Guest User

Untitled

a guest
Jul 6th, 2017
496
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.97 KB | None | 0 0
  1. /* Quicktime muxer plugin for GStreamer
  2. * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
  3. *
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library 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 GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  17. * Boston, MA 02110-1301, USA.
  18. */
  19. /*
  20. * Unless otherwise indicated, Source Code is licensed under MIT license.
  21. * See further explanation attached in License Statement (distributed in the file
  22. * LICENSE).
  23. *
  24. * Permission is hereby granted, free of charge, to any person obtaining a copy of
  25. * this software and associated documentation files (the "Software"), to deal in
  26. * the Software without restriction, including without limitation the rights to
  27. * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  28. * of the Software, and to permit persons to whom the Software is furnished to do
  29. * so, subject to the following conditions:
  30. *
  31. * The above copyright notice and this permission notice shall be included in all
  32. * copies or substantial portions of the Software.
  33. *
  34. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  35. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  36. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  37. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  38. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  39. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  40. * SOFTWARE.
  41. */
  42.  
  43. /*
  44. * This module contains functions for serializing partial information from
  45. * a mux in progress (by qtmux elements). This enables reconstruction of the
  46. * moov box if a crash happens and thus recovering the movie file.
  47. *
  48. * Usage:
  49. * 1) pipeline: ...yourelements ! qtmux moov-recovery-file=path.mrf ! \
  50. * filesink location=moovie.mov
  51. *
  52. * 2) CRASH!
  53. *
  54. * 3) gst-launch-1.0 qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \
  55. fixed-output=recovered.mov
  56. *
  57. * 4) (Hopefully) enjoy recovered.mov.
  58. *
  59. * --- Recovery file layout ---
  60. * 1) Version (a guint16)
  61. * 2) Prefix atom (if present)
  62. * 3) ftyp atom
  63. * 4) MVHD atom (without timescale/duration set)
  64. * 5) moovie timescale
  65. * 6) number of traks
  66. * 7) list of trak atoms (stbl data is ignored, except for the stsd atom)
  67. * 8) Buffers metadata (metadata that is relevant to the container)
  68. * Buffers metadata are stored in the order they are added to the mdat,
  69. * each entre has a fixed size and is stored in BE. booleans are stored
  70. * as a single byte where 0 means false, otherwise is true.
  71. * Metadata:
  72. * - guint32 track_id;
  73. * - guint32 nsamples;
  74. * - guint32 delta;
  75. * - guint32 size;
  76. * - guint64 chunk_offset;
  77. * - gboolean sync;
  78. * - gboolean do_pts;
  79. * - guint64 pts_offset; (always present, ignored if do_pts is false)
  80. *
  81. * The mdat file might contain ftyp and then mdat, in case this is the faststart
  82. * temporary file there is no ftyp and no mdat header, only the buffers data.
  83. *
  84. * Notes about recovery file layout: We still don't store tags nor EDTS data.
  85. *
  86. * IMPORTANT: this is still at a experimental state.
  87. */
  88.  
  89. #include "atomsrecovery.h"
  90.  
  91. #define ATOMS_RECOV_OUTPUT_WRITE_ERROR(err) \
  92. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, \
  93. "Failed to write to output file: %s", g_strerror (errno))
  94.  
  95. static gboolean
  96. atoms_recov_write_version (FILE * f)
  97. {
  98. guint8 data[2];
  99. GST_WRITE_UINT16_BE (data, ATOMS_RECOV_FILE_VERSION);
  100. return fwrite (data, 2, 1, f) == 1;
  101. }
  102.  
  103. static gboolean
  104. atoms_recov_write_ftyp_info (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix)
  105. {
  106. guint8 *data = NULL;
  107. guint64 offset = 0;
  108. guint64 size = 0;
  109.  
  110. if (prefix) {
  111. GstMapInfo map;
  112.  
  113. gst_buffer_map (prefix, &map, GST_MAP_READ);
  114. if (fwrite (map.data, 1, map.size, f) != map.size) {
  115. gst_buffer_unmap (prefix, &map);
  116. return FALSE;
  117. }
  118. gst_buffer_unmap (prefix, &map);
  119. }
  120. if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) {
  121. return FALSE;
  122. }
  123. if (fwrite (data, 1, offset, f) != offset) {
  124. g_free (data);
  125. return FALSE;
  126. }
  127. g_free (data);
  128. return TRUE;
  129. }
  130.  
  131. /*
  132. * Writes important info on the 'moov' atom (non-trak related)
  133. * to be able to recover the moov structure after a crash.
  134. *
  135. * Currently, it writes the MVHD atom.
  136. */
  137. static gboolean
  138. atoms_recov_write_moov_info (FILE * f, AtomMOOV * moov)
  139. {
  140. guint8 *data;
  141. guint64 size;
  142. guint64 offset = 0;
  143. guint64 atom_size = 0;
  144. gint writen = 0;
  145.  
  146. /* likely enough */
  147. size = 256;
  148. data = g_malloc (size);
  149. atom_size = atom_mvhd_copy_data (&moov->mvhd, &data, &size, &offset);
  150. if (atom_size > 0)
  151. writen = fwrite (data, 1, atom_size, f);
  152. g_free (data);
  153. return atom_size > 0 && writen == atom_size;
  154. }
  155.  
  156. /*
  157. * Writes the number of traks to the file.
  158. * This simply writes a guint32 in BE.
  159. */
  160. static gboolean
  161. atoms_recov_write_traks_number (FILE * f, guint32 traks)
  162. {
  163. guint8 data[4];
  164. GST_WRITE_UINT32_BE (data, traks);
  165. return fwrite (data, 4, 1, f) == 1;
  166. }
  167.  
  168. /*
  169. * Writes the moov's timescale to the file
  170. * This simply writes a guint32 in BE.
  171. */
  172. static gboolean
  173. atoms_recov_write_moov_timescale (FILE * f, guint32 timescale)
  174. {
  175. guint8 data[4];
  176. GST_WRITE_UINT32_BE (data, timescale);
  177. return fwrite (data, 4, 1, f) == 1;
  178. }
  179.  
  180. /*
  181. * Writes the trak atom to the file.
  182. */
  183. gboolean
  184. atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak)
  185. {
  186. guint8 *data;
  187. guint64 size;
  188. guint64 offset = 0;
  189. guint64 atom_size = 0;
  190. gint writen = 0;
  191.  
  192. /* buffer is realloced to a larger size if needed */
  193. size = 4 * 1024;
  194. data = g_malloc (size);
  195. atom_size = atom_trak_copy_data (trak, &data, &size, &offset);
  196. if (atom_size > 0)
  197. writen = fwrite (data, atom_size, 1, f);
  198. g_free (data);
  199. return atom_size > 0 && writen == atom_size;
  200. }
  201.  
  202. gboolean
  203. atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, guint32 nsamples,
  204. guint32 delta, guint32 size, guint64 chunk_offset, gboolean sync,
  205. gboolean do_pts, gint64 pts_offset)
  206. {
  207. guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
  208. /*
  209. * We have to write a TrakBufferEntryInfo
  210. */
  211. GST_WRITE_UINT32_BE (data + 0, trak->tkhd.track_ID);
  212. GST_WRITE_UINT32_BE (data + 4, nsamples);
  213. GST_WRITE_UINT32_BE (data + 8, delta);
  214. GST_WRITE_UINT32_BE (data + 12, size);
  215. GST_WRITE_UINT64_BE (data + 16, chunk_offset);
  216. if (sync)
  217. GST_WRITE_UINT8 (data + 24, 1);
  218. else
  219. GST_WRITE_UINT8 (data + 24, 0);
  220. if (do_pts) {
  221. GST_WRITE_UINT8 (data + 25, 1);
  222. GST_WRITE_UINT64_BE (data + 26, pts_offset);
  223. } else {
  224. GST_WRITE_UINT8 (data + 25, 0);
  225. GST_WRITE_UINT64_BE (data + 26, 0);
  226. }
  227.  
  228. return fwrite (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, f) ==
  229. TRAK_BUFFER_ENTRY_INFO_SIZE;
  230. }
  231.  
  232. gboolean
  233. atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix,
  234. AtomMOOV * moov, guint32 timescale, guint32 traks_number)
  235. {
  236. if (!atoms_recov_write_version (f)) {
  237. return FALSE;
  238. }
  239.  
  240. if (!atoms_recov_write_ftyp_info (f, ftyp, prefix)) {
  241. return FALSE;
  242. }
  243.  
  244. if (!atoms_recov_write_moov_info (f, moov)) {
  245. return FALSE;
  246. }
  247.  
  248. if (!atoms_recov_write_moov_timescale (f, timescale)) {
  249. return FALSE;
  250. }
  251.  
  252. if (!atoms_recov_write_traks_number (f, traks_number)) {
  253. return FALSE;
  254. }
  255.  
  256. return TRUE;
  257. }
  258.  
  259. static gboolean
  260. read_atom_header (FILE * f, guint32 * fourcc, guint32 * size)
  261. {
  262. guint8 aux[8];
  263.  
  264. if (fread (aux, 1, 8, f) != 8)
  265. return FALSE;
  266. *size = GST_READ_UINT32_BE (aux);
  267. *fourcc = GST_READ_UINT32_LE (aux + 4);
  268. return TRUE;
  269. }
  270.  
  271. static gboolean
  272. moov_recov_file_parse_prefix (MoovRecovFile * moovrf)
  273. {
  274. guint32 fourcc;
  275. guint32 size;
  276. guint32 total_size = 0;
  277. if (fseek (moovrf->file, 2, SEEK_SET) != 0)
  278. return FALSE;
  279. if (!read_atom_header (moovrf->file, &fourcc, &size)) {
  280. return FALSE;
  281. }
  282.  
  283. if (fourcc != FOURCC_ftyp) {
  284. /* we might have a prefix here */
  285. if (fseek (moovrf->file, size - 8, SEEK_CUR) != 0)
  286. return FALSE;
  287.  
  288. total_size += size;
  289.  
  290. /* now read the ftyp */
  291. if (!read_atom_header (moovrf->file, &fourcc, &size))
  292. return FALSE;
  293. }
  294.  
  295. /* this has to be the ftyp */
  296. if (fourcc != FOURCC_ftyp)
  297. return FALSE;
  298. total_size += size;
  299. moovrf->prefix_size = total_size;
  300. return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
  301. }
  302.  
  303. static gboolean
  304. moov_recov_file_parse_mvhd (MoovRecovFile * moovrf)
  305. {
  306. guint32 fourcc;
  307. guint32 size;
  308. if (!read_atom_header (moovrf->file, &fourcc, &size)) {
  309. return FALSE;
  310. }
  311. /* check for sanity */
  312. if (fourcc != FOURCC_mvhd)
  313. return FALSE;
  314.  
  315. moovrf->mvhd_size = size;
  316. moovrf->mvhd_pos = ftell (moovrf->file) - 8;
  317.  
  318. /* skip the remaining of the mvhd in the file */
  319. return fseek (moovrf->file, size - 8, SEEK_CUR) == 0;
  320. }
  321.  
  322. static gboolean
  323. mdat_recov_file_parse_mdat_start (MdatRecovFile * mdatrf)
  324. {
  325. guint32 fourcc, size;
  326.  
  327. if (!read_atom_header (mdatrf->file, &fourcc, &size)) {
  328. return FALSE;
  329. }
  330. if (size == 1) {
  331. mdatrf->mdat_header_size = 16;
  332. mdatrf->mdat_size = 16;
  333. } else {
  334. mdatrf->mdat_header_size = 8;
  335. mdatrf->mdat_size = 8;
  336. }
  337. mdatrf->mdat_start = ftell (mdatrf->file) - 8;
  338.  
  339. return fourcc == FOURCC_mdat;
  340. }
  341.  
  342. static gboolean
  343. mdat_recov_file_find_mdat (FILE * file, GError ** err)
  344. {
  345. guint32 fourcc = 0, size = 0;
  346. gboolean failure = FALSE;
  347. while (fourcc != FOURCC_mdat && !failure) {
  348. if (!read_atom_header (file, &fourcc, &size)) {
  349. goto parse_error;
  350. }
  351. switch (fourcc) {
  352. /* skip these atoms */
  353. case FOURCC_ftyp:
  354. case FOURCC_free:
  355. case FOURCC_udta:
  356. if (fseek (file, size - 8, SEEK_CUR) != 0) {
  357. goto file_seek_error;
  358. }
  359. break;
  360. case FOURCC_mdat:
  361. break;
  362. default:
  363. GST_ERROR ("Unexpected atom in headers %" GST_FOURCC_FORMAT,
  364. GST_FOURCC_ARGS (fourcc));
  365. failure = TRUE;
  366. break;
  367. }
  368. }
  369.  
  370. if (!failure) {
  371. /* Reverse to mdat start */
  372. if (fseek (file, -8, SEEK_CUR) != 0)
  373. goto file_seek_error;
  374. }
  375.  
  376. return !failure;
  377.  
  378. parse_error:
  379. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  380. "Failed to parse atom");
  381. return FALSE;
  382.  
  383. file_seek_error:
  384. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  385. "Failed to seek to start of the file");
  386. return FALSE;
  387.  
  388. }
  389.  
  390. MdatRecovFile *
  391. mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err)
  392. {
  393. MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1);
  394.  
  395. g_return_val_if_fail (file != NULL, NULL);
  396.  
  397. mrf->file = file;
  398. mrf->rawfile = datafile;
  399.  
  400. /* get the file/data length */
  401. if (fseek (file, 0, SEEK_END) != 0)
  402. goto file_length_error;
  403. /* still needs to deduce the mdat header and ftyp size */
  404. mrf->data_size = ftell (file);
  405. if (mrf->data_size == -1L)
  406. goto file_length_error;
  407.  
  408. if (fseek (file, 0, SEEK_SET) != 0)
  409. goto file_seek_error;
  410.  
  411. if (datafile) {
  412. /* this file contains no atoms, only raw data to be placed on the mdat
  413. * this happens when faststart mode is used */
  414. mrf->mdat_start = 0;
  415. mrf->mdat_header_size = 16;
  416. mrf->mdat_size = 16;
  417. return mrf;
  418. }
  419.  
  420. if (!mdat_recov_file_find_mdat (file, err)) {
  421. goto fail;
  422. }
  423.  
  424. /* we don't parse this if we have a tmpdatafile */
  425. if (!mdat_recov_file_parse_mdat_start (mrf)) {
  426. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  427. "Error while parsing mdat atom");
  428. goto fail;
  429. }
  430.  
  431. return mrf;
  432.  
  433. file_seek_error:
  434. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  435. "Failed to seek to start of the file");
  436. goto fail;
  437.  
  438. file_length_error:
  439. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  440. "Failed to determine file size");
  441. goto fail;
  442.  
  443. fail:
  444. mdat_recov_file_free (mrf);
  445. return NULL;
  446. }
  447.  
  448. void
  449. mdat_recov_file_free (MdatRecovFile * mrf)
  450. {
  451. fclose (mrf->file);
  452. g_free (mrf);
  453. }
  454.  
  455. static gboolean
  456. moov_recov_parse_num_traks (MoovRecovFile * moovrf)
  457. {
  458. guint8 traks[4];
  459. if (fread (traks, 1, 4, moovrf->file) != 4)
  460. return FALSE;
  461. moovrf->num_traks = GST_READ_UINT32_BE (traks);
  462. return TRUE;
  463. }
  464.  
  465. static gboolean
  466. moov_recov_parse_moov_timescale (MoovRecovFile * moovrf)
  467. {
  468. guint8 ts[4];
  469. if (fread (ts, 1, 4, moovrf->file) != 4)
  470. return FALSE;
  471. moovrf->timescale = GST_READ_UINT32_BE (ts);
  472. return TRUE;
  473. }
  474.  
  475. static gboolean
  476. skip_atom (MoovRecovFile * moovrf, guint32 expected_fourcc)
  477. {
  478. guint32 size;
  479. guint32 fourcc;
  480.  
  481. if (!read_atom_header (moovrf->file, &fourcc, &size))
  482. return FALSE;
  483. if (fourcc != expected_fourcc)
  484. return FALSE;
  485.  
  486. return (fseek (moovrf->file, size - 8, SEEK_CUR) == 0);
  487. }
  488.  
  489. static gboolean
  490. moov_recov_parse_tkhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  491. {
  492. guint32 size;
  493. guint32 fourcc;
  494. guint8 data[4];
  495.  
  496. /* make sure we are on a tkhd atom */
  497. if (!read_atom_header (moovrf->file, &fourcc, &size))
  498. return FALSE;
  499. if (fourcc != FOURCC_tkhd)
  500. return FALSE;
  501.  
  502. trakrd->tkhd_file_offset = ftell (moovrf->file) - 8;
  503.  
  504. /* move 8 bytes forward to the trak_id pos */
  505. if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
  506. return FALSE;
  507. if (fread (data, 1, 4, moovrf->file) != 4)
  508. return FALSE;
  509.  
  510. /* advance the rest of tkhd */
  511. if (fseek (moovrf->file, 68, SEEK_CUR) != 0)
  512. return FALSE;
  513.  
  514. trakrd->trak_id = GST_READ_UINT32_BE (data);
  515. return TRUE;
  516. }
  517.  
  518. static gboolean
  519. moov_recov_parse_stbl (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  520. {
  521. guint32 size;
  522. guint32 fourcc;
  523. guint32 auxsize;
  524.  
  525. if (!read_atom_header (moovrf->file, &fourcc, &size))
  526. return FALSE;
  527. if (fourcc != FOURCC_stbl)
  528. return FALSE;
  529.  
  530. trakrd->stbl_file_offset = ftell (moovrf->file) - 8;
  531. trakrd->stbl_size = size;
  532.  
  533. /* skip the stsd */
  534. if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
  535. return FALSE;
  536. if (fourcc != FOURCC_stsd)
  537. return FALSE;
  538. if (fseek (moovrf->file, auxsize - 8, SEEK_CUR) != 0)
  539. return FALSE;
  540.  
  541. trakrd->stsd_size = auxsize;
  542. trakrd->post_stsd_offset = ftell (moovrf->file);
  543.  
  544. /* as this is the last atom we parse, we don't skip forward */
  545.  
  546. return TRUE;
  547. }
  548.  
  549. static gboolean
  550. moov_recov_parse_minf (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  551. {
  552. guint32 size;
  553. guint32 fourcc;
  554. guint32 auxsize;
  555.  
  556. if (!read_atom_header (moovrf->file, &fourcc, &size))
  557. return FALSE;
  558. if (fourcc != FOURCC_minf)
  559. return FALSE;
  560.  
  561. trakrd->minf_file_offset = ftell (moovrf->file) - 8;
  562. trakrd->minf_size = size;
  563.  
  564. /* skip either of vmhd, smhd, hmhd that might follow */
  565. if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
  566. return FALSE;
  567. if (fourcc != FOURCC_vmhd && fourcc != FOURCC_smhd && fourcc != FOURCC_hmhd &&
  568. fourcc != FOURCC_gmhd)
  569. return FALSE;
  570. if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
  571. return FALSE;
  572.  
  573. /* skip a possible hdlr and the following dinf */
  574. if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
  575. return FALSE;
  576. if (fourcc == FOURCC_hdlr) {
  577. if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
  578. return FALSE;
  579. if (!read_atom_header (moovrf->file, &fourcc, &auxsize))
  580. return FALSE;
  581. }
  582. if (fourcc != FOURCC_dinf)
  583. return FALSE;
  584. if (fseek (moovrf->file, auxsize - 8, SEEK_CUR))
  585. return FALSE;
  586.  
  587. /* now we are ready to read the stbl */
  588. if (!moov_recov_parse_stbl (moovrf, trakrd))
  589. return FALSE;
  590.  
  591. return TRUE;
  592. }
  593.  
  594. static gboolean
  595. moov_recov_parse_mdhd (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  596. {
  597. guint32 size;
  598. guint32 fourcc;
  599. guint8 data[4];
  600.  
  601. /* make sure we are on a tkhd atom */
  602. if (!read_atom_header (moovrf->file, &fourcc, &size))
  603. return FALSE;
  604. if (fourcc != FOURCC_mdhd)
  605. return FALSE;
  606.  
  607. trakrd->mdhd_file_offset = ftell (moovrf->file) - 8;
  608.  
  609. /* get the timescale */
  610. if (fseek (moovrf->file, 12, SEEK_CUR) != 0)
  611. return FALSE;
  612. if (fread (data, 1, 4, moovrf->file) != 4)
  613. return FALSE;
  614. trakrd->timescale = GST_READ_UINT32_BE (data);
  615. if (fseek (moovrf->file, 8, SEEK_CUR) != 0)
  616. return FALSE;
  617. return TRUE;
  618. }
  619.  
  620. static gboolean
  621. moov_recov_parse_mdia (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  622. {
  623. guint32 size;
  624. guint32 fourcc;
  625.  
  626. /* make sure we are on a tkhd atom */
  627. if (!read_atom_header (moovrf->file, &fourcc, &size))
  628. return FALSE;
  629. if (fourcc != FOURCC_mdia)
  630. return FALSE;
  631.  
  632. trakrd->mdia_file_offset = ftell (moovrf->file) - 8;
  633. trakrd->mdia_size = size;
  634.  
  635. if (!moov_recov_parse_mdhd (moovrf, trakrd))
  636. return FALSE;
  637.  
  638. if (!skip_atom (moovrf, FOURCC_hdlr))
  639. return FALSE;
  640. if (!moov_recov_parse_minf (moovrf, trakrd))
  641. return FALSE;
  642. return TRUE;
  643. }
  644.  
  645. static gboolean
  646. moov_recov_parse_trak (MoovRecovFile * moovrf, TrakRecovData * trakrd)
  647. {
  648. guint64 offset;
  649. guint32 size;
  650. guint32 fourcc;
  651.  
  652. offset = ftell (moovrf->file);
  653. if (offset == -1) {
  654. return FALSE;
  655. }
  656.  
  657. /* make sure we are on a trak atom */
  658. if (!read_atom_header (moovrf->file, &fourcc, &size)) {
  659. return FALSE;
  660. }
  661. if (fourcc != FOURCC_trak) {
  662. return FALSE;
  663. }
  664. trakrd->trak_size = size;
  665.  
  666. /* now we should have a trak header 'tkhd' */
  667. if (!moov_recov_parse_tkhd (moovrf, trakrd))
  668. return FALSE;
  669.  
  670. /* FIXME add edts handling here and in qtmux, as this is only detected
  671. * after buffers start flowing */
  672.  
  673. if (!moov_recov_parse_mdia (moovrf, trakrd))
  674. return FALSE;
  675.  
  676. if (fseek (moovrf->file,
  677. (long int) trakrd->mdia_file_offset + trakrd->mdia_size,
  678. SEEK_SET) != 0)
  679. return FALSE;
  680.  
  681. trakrd->extra_atoms_offset = ftell (moovrf->file);
  682. trakrd->extra_atoms_size = size - (trakrd->extra_atoms_offset - offset);
  683.  
  684. trakrd->file_offset = offset;
  685. /* position after the trak */
  686. return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0;
  687. }
  688.  
  689. MoovRecovFile *
  690. moov_recov_file_create (FILE * file, GError ** err)
  691. {
  692. gint i;
  693. MoovRecovFile *moovrf = g_new0 (MoovRecovFile, 1);
  694.  
  695. g_return_val_if_fail (file != NULL, NULL);
  696.  
  697. moovrf->file = file;
  698.  
  699. /* look for ftyp and prefix at the start */
  700. if (!moov_recov_file_parse_prefix (moovrf)) {
  701. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  702. "Error while parsing prefix atoms");
  703. goto fail;
  704. }
  705.  
  706. /* parse the mvhd */
  707. if (!moov_recov_file_parse_mvhd (moovrf)) {
  708. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  709. "Error while parsing mvhd atom");
  710. goto fail;
  711. }
  712.  
  713. if (!moov_recov_parse_moov_timescale (moovrf)) {
  714. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  715. "Error while parsing timescale");
  716. goto fail;
  717. }
  718. if (!moov_recov_parse_num_traks (moovrf)) {
  719. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  720. "Error while parsing parsing number of traks");
  721. goto fail;
  722. }
  723.  
  724. /* sanity check */
  725. if (moovrf->num_traks > 1024) {
  726. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  727. "Unsupported number of traks");
  728. goto fail;
  729. }
  730.  
  731. /* init the traks */
  732. moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks);
  733. for (i = 0; i < moovrf->num_traks; i++) {
  734. atom_stbl_init (&(moovrf->traks_rd[i].stbl));
  735. }
  736. for (i = 0; i < moovrf->num_traks; i++) {
  737. if (!moov_recov_parse_trak (moovrf, &(moovrf->traks_rd[i]))) {
  738. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  739. "Error while parsing trak atom");
  740. goto fail;
  741. }
  742. }
  743.  
  744. return moovrf;
  745.  
  746. fail:
  747. moov_recov_file_free (moovrf);
  748. return NULL;
  749. }
  750.  
  751. void
  752. moov_recov_file_free (MoovRecovFile * moovrf)
  753. {
  754. gint i;
  755. fclose (moovrf->file);
  756. if (moovrf->traks_rd) {
  757. for (i = 0; i < moovrf->num_traks; i++) {
  758. atom_stbl_clear (&(moovrf->traks_rd[i].stbl));
  759. }
  760. g_free (moovrf->traks_rd);
  761. }
  762. g_free (moovrf);
  763. }
  764.  
  765. static gboolean
  766. moov_recov_parse_buffer_entry (MoovRecovFile * moovrf, TrakBufferEntryInfo * b)
  767. {
  768. guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE];
  769. gint read;
  770.  
  771. read = fread (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, moovrf->file);
  772. if (read != TRAK_BUFFER_ENTRY_INFO_SIZE)
  773. return FALSE;
  774.  
  775. b->track_id = GST_READ_UINT32_BE (data);
  776. b->nsamples = GST_READ_UINT32_BE (data + 4);
  777. b->delta = GST_READ_UINT32_BE (data + 8);
  778. b->size = GST_READ_UINT32_BE (data + 12);
  779. b->chunk_offset = GST_READ_UINT64_BE (data + 16);
  780. b->sync = data[24] != 0;
  781. b->do_pts = data[25] != 0;
  782. b->pts_offset = GST_READ_UINT64_BE (data + 26);
  783. return TRUE;
  784. }
  785.  
  786. static gboolean
  787. mdat_recov_add_sample (MdatRecovFile * mdatrf, guint32 size)
  788. {
  789. /* test if this data exists */
  790. if (mdatrf->mdat_size - mdatrf->mdat_header_size + size > mdatrf->data_size)
  791. return FALSE;
  792.  
  793. mdatrf->mdat_size += size;
  794. return TRUE;
  795. }
  796.  
  797. static TrakRecovData *
  798. moov_recov_get_trak (MoovRecovFile * moovrf, guint32 id)
  799. {
  800. gint i;
  801. for (i = 0; i < moovrf->num_traks; i++) {
  802. if (moovrf->traks_rd[i].trak_id == id)
  803. return &(moovrf->traks_rd[i]);
  804. }
  805. return NULL;
  806. }
  807.  
  808. static void
  809. trak_recov_data_add_sample (TrakRecovData * trak, TrakBufferEntryInfo * b)
  810. {
  811. trak->duration += b->nsamples * b->delta;
  812. atom_stbl_add_samples (&trak->stbl, b->nsamples, b->delta, b->size,
  813. b->chunk_offset, b->sync, b->pts_offset);
  814. }
  815.  
  816. /*
  817. * Parses the buffer entries in the MoovRecovFile and matches the inputs
  818. * with the data in the MdatRecovFile. Whenever a buffer entry of that
  819. * represents 'x' bytes of data, the same amount of data is 'validated' in
  820. * the MdatRecovFile and will be inluded in the generated moovie file.
  821. */
  822. gboolean
  823. moov_recov_parse_buffers (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
  824. GError ** err)
  825. {
  826. TrakBufferEntryInfo entry;
  827. TrakRecovData *trak;
  828.  
  829. /* we assume both moovrf and mdatrf are at the starting points of their
  830. * data reading */
  831. while (moov_recov_parse_buffer_entry (moovrf, &entry)) {
  832. /* be sure we still have this data in mdat */
  833. trak = moov_recov_get_trak (moovrf, entry.track_id);
  834. if (trak == NULL) {
  835. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING,
  836. "Invalid trak id found in buffer entry");
  837. return FALSE;
  838. }
  839. if (!mdat_recov_add_sample (mdatrf, entry.size))
  840. break;
  841. trak_recov_data_add_sample (trak, &entry);
  842. }
  843. return TRUE;
  844. }
  845.  
  846. static guint32
  847. trak_recov_data_get_trak_atom_size (TrakRecovData * trak)
  848. {
  849. AtomSTBL *stbl = &trak->stbl;
  850. guint64 offset;
  851.  
  852. /* write out our stbl child atoms */
  853. offset = 0;
  854.  
  855. if (!atom_stts_copy_data (&stbl->stts, NULL, NULL, &offset)) {
  856. goto fail;
  857. }
  858. if (atom_array_get_len (&stbl->stss.entries) > 0) {
  859. if (!atom_stss_copy_data (&stbl->stss, NULL, NULL, &offset)) {
  860. goto fail;
  861. }
  862. }
  863. if (!atom_stsc_copy_data (&stbl->stsc, NULL, NULL, &offset)) {
  864. goto fail;
  865. }
  866. if (!atom_stsz_copy_data (&stbl->stsz, NULL, NULL, &offset)) {
  867. goto fail;
  868. }
  869. if (stbl->ctts) {
  870. if (!atom_ctts_copy_data (stbl->ctts, NULL, NULL, &offset)) {
  871. goto fail;
  872. }
  873. }
  874. if (!atom_stco64_copy_data (&stbl->stco64, NULL, NULL, &offset)) {
  875. goto fail;
  876. }
  877.  
  878. return trak->trak_size + ((trak->stsd_size + offset + 8) - trak->stbl_size);
  879.  
  880. fail:
  881. return 0;
  882. }
  883.  
  884. static guint8 *
  885. moov_recov_get_stbl_children_data (MoovRecovFile * moovrf, TrakRecovData * trak,
  886. guint64 * p_size)
  887. {
  888. AtomSTBL *stbl = &trak->stbl;
  889. guint8 *buffer;
  890. guint64 size;
  891. guint64 offset;
  892.  
  893. /* write out our stbl child atoms
  894. *
  895. * Use 1MB as a starting size, *_copy_data functions
  896. * will grow the buffer if needed.
  897. */
  898. size = 1024 * 1024;
  899. buffer = g_malloc0 (size);
  900. offset = 0;
  901.  
  902. if (!atom_stts_copy_data (&stbl->stts, &buffer, &size, &offset)) {
  903. goto fail;
  904. }
  905. if (atom_array_get_len (&stbl->stss.entries) > 0) {
  906. if (!atom_stss_copy_data (&stbl->stss, &buffer, &size, &offset)) {
  907. goto fail;
  908. }
  909. }
  910. if (!atom_stsc_copy_data (&stbl->stsc, &buffer, &size, &offset)) {
  911. goto fail;
  912. }
  913. if (!atom_stsz_copy_data (&stbl->stsz, &buffer, &size, &offset)) {
  914. goto fail;
  915. }
  916. if (stbl->ctts) {
  917. if (!atom_ctts_copy_data (stbl->ctts, &buffer, &size, &offset)) {
  918. goto fail;
  919. }
  920. }
  921. if (!atom_stco64_copy_data (&stbl->stco64, &buffer, &size, &offset)) {
  922. goto fail;
  923. }
  924. *p_size = offset;
  925. return buffer;
  926.  
  927. fail:
  928. g_free (buffer);
  929. return NULL;
  930. }
  931.  
  932. static gboolean
  933. copy_data_from_file_to_file (FILE * from, guint position, guint size, FILE * to,
  934. GError ** err)
  935. {
  936. guint8 *data = NULL;
  937.  
  938. if (fseek (from, position, SEEK_SET) != 0)
  939. goto fail;
  940. data = g_malloc (size);
  941. if (fread (data, 1, size, from) != size) {
  942. goto fail;
  943. }
  944. if (fwrite (data, 1, size, to) != size) {
  945. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  946. goto fail;
  947. }
  948.  
  949. g_free (data);
  950. return TRUE;
  951.  
  952. fail:
  953. g_free (data);
  954. return FALSE;
  955. }
  956.  
  957. gboolean
  958. moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf,
  959. FILE * outf, GError ** err)
  960. {
  961. guint8 auxdata[16];
  962. guint8 *data = NULL;
  963. guint8 *prefix_data = NULL;
  964. guint8 *mvhd_data = NULL;
  965. guint8 *trak_data = NULL;
  966. guint32 moov_size = 0;
  967. gint i;
  968. guint64 stbl_children_size = 0;
  969. guint8 *stbl_children = NULL;
  970. guint32 longest_duration = 0;
  971. guint16 version;
  972.  
  973. /* check the version */
  974. if (fseek (moovrf->file, 0, SEEK_SET) != 0) {
  975. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  976. "Failed to seek to the start of the moov recovery file");
  977. goto fail;
  978. }
  979. if (fread (auxdata, 1, 2, moovrf->file) != 2) {
  980. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  981. "Failed to read version from file");
  982. }
  983.  
  984. version = GST_READ_UINT16_BE (auxdata);
  985. if (version != ATOMS_RECOV_FILE_VERSION) {
  986. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_VERSION,
  987. "Input file version (%u) is not supported in this version (%u)",
  988. version, ATOMS_RECOV_FILE_VERSION);
  989. return FALSE;
  990. }
  991.  
  992. /* write the ftyp */
  993. prefix_data = g_malloc (moovrf->prefix_size);
  994. if (fread (prefix_data, 1, moovrf->prefix_size,
  995. moovrf->file) != moovrf->prefix_size) {
  996. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  997. "Failed to read the ftyp atom from file");
  998. goto fail;
  999. }
  1000. if (fwrite (prefix_data, 1, moovrf->prefix_size, outf) != moovrf->prefix_size) {
  1001. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1002. goto fail;
  1003. }
  1004. g_free (prefix_data);
  1005. prefix_data = NULL;
  1006.  
  1007. /* need to calculate the moov size beforehand to add the offset to
  1008. * chunk offset entries */
  1009. moov_size += moovrf->mvhd_size + 8; /* mvhd + moov size + fourcc */
  1010. for (i = 0; i < moovrf->num_traks; i++) {
  1011. TrakRecovData *trak = &(moovrf->traks_rd[i]);
  1012. guint32 duration; /* in moov's timescale */
  1013. guint32 trak_size;
  1014.  
  1015. /* convert trak duration to moov's duration */
  1016. duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
  1017. trak->timescale);
  1018.  
  1019. if (duration > longest_duration)
  1020. longest_duration = duration;
  1021. trak_size = trak_recov_data_get_trak_atom_size (trak);
  1022. if (trak_size == 0) {
  1023. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_GENERIC,
  1024. "Failed to estimate trak atom size");
  1025. goto fail;
  1026. }
  1027. moov_size += trak_size;
  1028. }
  1029.  
  1030. /* add chunks offsets */
  1031. for (i = 0; i < moovrf->num_traks; i++) {
  1032. TrakRecovData *trak = &(moovrf->traks_rd[i]);
  1033. /* 8 or 16 for the mdat header */
  1034. gint64 offset = moov_size + ftell (outf) + mdatrf->mdat_header_size;
  1035. atom_stco64_chunks_set_offset (&trak->stbl.stco64, offset);
  1036. }
  1037.  
  1038. /* write the moov */
  1039. GST_WRITE_UINT32_BE (auxdata, moov_size);
  1040. GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_moov);
  1041. if (fwrite (auxdata, 1, 8, outf) != 8) {
  1042. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1043. goto fail;
  1044. }
  1045.  
  1046. /* write the mvhd */
  1047. mvhd_data = g_malloc (moovrf->mvhd_size);
  1048. if (fseek (moovrf->file, moovrf->mvhd_pos, SEEK_SET) != 0)
  1049. goto fail;
  1050. if (fread (mvhd_data, 1, moovrf->mvhd_size,
  1051. moovrf->file) != moovrf->mvhd_size)
  1052. goto fail;
  1053. GST_WRITE_UINT32_BE (mvhd_data + 20, moovrf->timescale);
  1054. GST_WRITE_UINT32_BE (mvhd_data + 24, longest_duration);
  1055. if (fwrite (mvhd_data, 1, moovrf->mvhd_size, outf) != moovrf->mvhd_size) {
  1056. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1057. goto fail;
  1058. }
  1059. g_free (mvhd_data);
  1060. mvhd_data = NULL;
  1061.  
  1062. /* write the traks, this is the tough part because we need to update:
  1063. * - stbl atom
  1064. * - sizes of atoms from stbl to trak
  1065. * - trak duration
  1066. */
  1067. for (i = 0; i < moovrf->num_traks; i++) {
  1068. TrakRecovData *trak = &(moovrf->traks_rd[i]);
  1069. guint trak_data_size;
  1070. guint32 stbl_new_size;
  1071. guint32 minf_new_size;
  1072. guint32 mdia_new_size;
  1073. guint32 trak_new_size;
  1074. guint32 size_diff;
  1075. guint32 duration; /* in moov's timescale */
  1076.  
  1077. /* convert trak duration to moov's duration */
  1078. duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale,
  1079. trak->timescale);
  1080.  
  1081. stbl_children = moov_recov_get_stbl_children_data (moovrf, trak,
  1082. &stbl_children_size);
  1083. if (stbl_children == NULL)
  1084. goto fail;
  1085.  
  1086. /* calc the new size of the atoms from stbl to trak in the atoms tree */
  1087. stbl_new_size = trak->stsd_size + stbl_children_size + 8;
  1088. size_diff = stbl_new_size - trak->stbl_size;
  1089. minf_new_size = trak->minf_size + size_diff;
  1090. mdia_new_size = trak->mdia_size + size_diff;
  1091. trak_new_size = trak->trak_size + size_diff;
  1092.  
  1093. if (fseek (moovrf->file, trak->file_offset, SEEK_SET) != 0)
  1094. goto fail;
  1095. trak_data_size = trak->post_stsd_offset - trak->file_offset;
  1096. trak_data = g_malloc (trak_data_size);
  1097. if (fread (trak_data, 1, trak_data_size, moovrf->file) != trak_data_size) {
  1098. goto fail;
  1099. }
  1100. /* update the size values in those read atoms before writing */
  1101. GST_WRITE_UINT32_BE (trak_data, trak_new_size);
  1102. GST_WRITE_UINT32_BE (trak_data + (trak->mdia_file_offset -
  1103. trak->file_offset), mdia_new_size);
  1104. GST_WRITE_UINT32_BE (trak_data + (trak->minf_file_offset -
  1105. trak->file_offset), minf_new_size);
  1106. GST_WRITE_UINT32_BE (trak_data + (trak->stbl_file_offset -
  1107. trak->file_offset), stbl_new_size);
  1108.  
  1109. /* update duration values in tkhd and mdhd */
  1110. GST_WRITE_UINT32_BE (trak_data + (trak->tkhd_file_offset -
  1111. trak->file_offset) + 28, duration);
  1112. GST_WRITE_UINT32_BE (trak_data + (trak->mdhd_file_offset -
  1113. trak->file_offset) + 24, trak->duration);
  1114.  
  1115. if (fwrite (trak_data, 1, trak_data_size, outf) != trak_data_size) {
  1116. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1117. goto fail;
  1118. }
  1119. if (fwrite (stbl_children, 1, stbl_children_size, outf) !=
  1120. stbl_children_size) {
  1121. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1122. goto fail;
  1123. }
  1124.  
  1125. g_free (trak_data);
  1126. trak_data = NULL;
  1127. g_free (stbl_children);
  1128. stbl_children = NULL;
  1129.  
  1130. /* Copy the extra atoms after 'minf' */
  1131. if (!copy_data_from_file_to_file (moovrf->file, trak->extra_atoms_offset,
  1132. trak->extra_atoms_size, outf, err))
  1133. goto fail;
  1134. }
  1135.  
  1136. /* write the mdat */
  1137. /* write the header first */
  1138. if (mdatrf->mdat_header_size == 16) {
  1139. GST_WRITE_UINT32_BE (auxdata, 1);
  1140. GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
  1141. GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size);
  1142. } else if (mdatrf->mdat_header_size == 8) {
  1143. GST_WRITE_UINT32_BE (auxdata, mdatrf->mdat_size);
  1144. GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat);
  1145. } else {
  1146. GST_ERROR ("Unexpected atom size: %u", mdatrf->mdat_header_size);
  1147. g_assert_not_reached ();
  1148. goto fail;
  1149. }
  1150.  
  1151. if (fwrite (auxdata, 1, mdatrf->mdat_header_size,
  1152. outf) != mdatrf->mdat_header_size) {
  1153. ATOMS_RECOV_OUTPUT_WRITE_ERROR (err);
  1154. goto fail;
  1155. }
  1156.  
  1157. /* now read the mdat data and output to the file */
  1158. if (fseek (mdatrf->file, mdatrf->mdat_start +
  1159. (mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0)
  1160. goto fail;
  1161.  
  1162. data = g_malloc (4096);
  1163. while (!feof (mdatrf->file)) {
  1164. gint read, write;
  1165.  
  1166. read = fread (data, 1, 4096, mdatrf->file);
  1167. write = fwrite (data, 1, read, outf);
  1168.  
  1169. if (write != read) {
  1170. g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE,
  1171. "Failed to copy data to output file: %s", g_strerror (errno));
  1172. goto fail;
  1173. }
  1174. }
  1175. g_free (data);
  1176.  
  1177. return TRUE;
  1178.  
  1179. fail:
  1180. g_free (stbl_children);
  1181. g_free (mvhd_data);
  1182. g_free (prefix_data);
  1183. g_free (trak_data);
  1184. g_free (data);
  1185. return FALSE;
  1186. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement